今天在做系统报表的过程中,我想实现批量操作DataGridView中的数据,在列中加复选框,通过一个事件触发进行全选或取消,可是在外面添加按钮,这种模式虽然能够实现,但是从系统界面设计的角度,美观和灵活性就差很多了,能否在DataGridView头标题栏上呈现复选框,通过这个头标题复选框来对这一列的复选框,这样是不是更灵活,也美观一点?
问题可是,找了半天,发现微软原始的DataGridView头标题栏没有CheckBox的功能,郁闷了~~。
我在伍兄的博客找到有关于扩展DataGridVew在头标题栏添加全选功能按钮的功能(不是源码开源),而且他的这个程序集也不能满足我的需求,怎么办?
只有靠我自已去探索了,我在传统的DataGridView中实现了这个功能,但是整合进我的换肤组件中不能实现,Why? 我找了好友Strong一起研究,自已摸不清方向,最后还是他找到了问题点,可是却无法入手?(no any solution)
最后,经过自已的努力一步一步debug, 终于解决了问题,并整合进自已的换肤组件中,个人觉得有必要总结一下。
在总结技术点前,先展示一下我的成果,然后再做扩展说明,如下:
(图一)Office2007Blue效果
(图二) Office2007Silver效果
传统的解决方案
在传统的DataGridView上,实现基本上没有什么难处,只要按如下的步骤去操作就可以了。
添加一个DataGridViewColumnHeaderCellW.cs,继承DataGridViewColumnHeaderCell,
源码如下:
public class DataGridViewColumnHeaderCellW : DataGridViewColumnHeaderCell{public object HeaderTextDataSource { get; set; }private Type _dataSourceType = null;public Type DataSourceType{get{if (HeaderTextDataSource != null && _dataSourceType == null)_dataSourceType = HeaderTextDataSource.GetType();return _dataSourceType;}set { _dataSourceType = value; }}public string FieldName { get; set; }public string Prefix { get; set; }public string Suffix { get; set; }Point checkBoxLocation;Size checkBoxSize;bool _checked = false;Point _cellLocation = new Point();System.Windows.Forms.VisualStyles.CheckBoxState _cbState =System.Windows.Forms.VisualStyles.CheckBoxState.UncheckedNormal;public event datagridviewcheckboxHeaderEventHander OnCheckBoxClicked; //绘制列头checkbox protected override void Paint(System.Drawing.Graphics graphics,System.Drawing.Rectangle clipBounds,System.Drawing.Rectangle cellBounds,int rowIndex,DataGridViewElementStates dataGridViewElementState,object value,object formattedValue,string errorText,DataGridViewCellStyle cellStyle,DataGridViewAdvancedBorderStyle advancedBorderStyle,DataGridViewPaintParts paintParts){base.Paint(graphics, clipBounds, cellBounds, rowIndex,dataGridViewElementState, value,formattedValue, errorText, cellStyle,advancedBorderStyle, paintParts);Point p = new Point();Size s = CheckBoxRenderer.GetGlyphSize(graphics,System.Windows.Forms.VisualStyles.CheckBoxState.UncheckedNormal);p.X = cellBounds.Location.X +(cellBounds.Width / 2) - (s.Width / 2) - 1; //列头checkbox的X坐标 p.Y = cellBounds.Location.Y +(cellBounds.Height / 2) - (s.Height / 2); //列头checkbox的Y坐标 _cellLocation = cellBounds.Location;checkBoxLocation = p;checkBoxSize = s;if (_checked)_cbState = System.Windows.Forms.VisualStyles.CheckBoxState.CheckedNormal;else_cbState = System.Windows.Forms.VisualStyles.CheckBoxState.UncheckedNormal;CheckBoxRenderer.DrawCheckBox(graphics, checkBoxLocation, _cbState);}///
在DataGridViewColumnHeaderCellW.cs内部,添加一个委托和继承EventArgs事件数据的基类:
public delegate void datagridviewcheckboxHeaderEventHander(object sender, datagridviewCheckboxHeaderEventArgs e);//定义包含列头checkbox选择状态的参数类 public class datagridviewCheckboxHeaderEventArgs : EventArgs{private bool checkedState = false;public bool CheckedState{get { return checkedState; }set { checkedState = value; }}}
先在界面上添加一个DataGridView,并添加一列,选类型为DataGridViewCheckBoxColumn,在Form_Load事件中添加如下代码:
private void Form1_Load(object sender, EventArgs e){DataGridViewColumnHeaderCellW ch = new DataGridViewColumnHeaderCellW();ch.OnCheckBoxClicked += new datagridviewcheckboxHeaderEventHander(OnCheckBoxClicked);//第三列为DataGridViewCheckBoxColumn DataGridViewCheckBoxColumn checkboxCol = this.dataGridView1.Columns[0] as DataGridViewCheckBoxColumn;checkboxCol.HeaderCell = ch;checkboxCol.HeaderCell.Value = string.Empty;//消除列头checkbox旁出现的文字 }
没题解决了!
展示一下,这个复选框暂放在最后一列,如下图:
这个真是灰头土脸,像是灰姑娘那么丑。
于是,我整合进我的换肤中,可是怎么也不能实现,标题栏就是不出来,结果成了如下图这样子:
最后经过一系列努力,定位问题在CellPainting事件中,重绘的过程把标题栏的checkbox效果覆盖了。
我在此事件中添加如下代码:
if (e.RowIndex == -1){if (!(_columnHeaderUpColor == Color.Transparent) && !(_columnHeaderDownColor == Color.Transparent) &&!_columnHeaderUpColor.IsEmpty && !_columnHeaderDownColor.IsEmpty){DrawLinearGradient(e.CellBounds, e.Graphics, _columnHeaderUpColor, _columnHeaderDownColor);if (ShowColumnHeaderCheckBox){e.Paint(e.ClipBounds, (DataGridViewPaintParts.All & ~DataGridViewPaintParts.Background));}else{DrawText(e); }e.Handled = true;}}
问题终于解决,但是在代码中为什么用ShowColumnHeaderCheckBox?
并不是所有的数据呈现功能都要有这个头标题栏复选框的功能,为了更好的兼容性,我在添加了这个属性,开发人员可以通过此属性灵活选择,默认是false。
public bool showColumnHeaderCheckBox;public bool ShowColumnHeaderCheckBox{get{return showColumnHeaderCheckBox;}set { showColumnHeaderCheckBox = value; }}
private void Form1_Load(object sender, EventArgs e){InitParameterList();dataGridView1.ShowColumnHeaderCheckBox = true;//此处设为trueDataGridViewColumnHeaderCellW ch = new DataGridViewColumnHeaderCellW();ch.OnCheckBoxClicked += new datagridviewcheckboxHeaderEventHander(OnCheckBoxClicked);//第三列为DataGridViewCheckBoxColumn DataGridViewCheckBoxColumn checkboxCol = this.dataGridView1.Columns[0] as DataGridViewCheckBoxColumn;checkboxCol.HeaderCell = ch;checkboxCol.HeaderCell.Value = string.Empty;//消除列头checkbox旁出现的文字 }
在研究一个新的东西时,我一般是先实现粗糙的功能,由浅入深渐渐细化这么一个演变的过程,毕竟灰姑娘一下要变成白雪公主也是要有个过程的。
元芳,你怎么看?