今天在作系統報表的過程當中,我想實現批量操做DataGridView中的數據,在列中加複選框,經過一個事件觸發進行全選或取消,但是在外面添加按鈕,這種模式雖然可以實現,可是從系統界面設計的角度,美觀和靈活性就差不少了,可否在DataGridView頭標題欄上呈現複選框,經過這個頭標題複選框來對這一列的複選框,這樣是否是更靈活,也美觀一點?ide
但是,找了半天,發現微軟原始的DataGridView頭標題欄沒有CheckBox的功能,鬱悶了~~。this
我在伍兄的博客找到有關於擴展DataGridVew在頭標題欄添加全選功能按鈕的功能(不是源碼開源),並且他的這個程序集也不能知足個人需求,怎麼辦?spa
只有靠我自已去探索了,我在傳統的DataGridView中實現了這個功能,可是整合進個人換膚組件中不能實現,Why? 我找了好友Strong一塊兒研究,自已摸不清方向,最後仍是他找到了問題點,但是卻沒法入手?(no any solution)debug
最後,通過自已的努力一步一步debug, 終於解決了問題,並整合進自已的換膚組件中,我的以爲有必要總結一下。設計
在總結技術點前,先展現一下個人成果,而後再作擴展說明,以下:3d
(圖一)Office2007Blue效果code
(圖二) Office2007Silver效果orm
在傳統的DataGridView上,實現基本上沒有什麼難處,只要按以下的步驟去操做就能夠了。blog
添加一個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); } /// <summary> /// 點擊列頭checkbox單擊事件 /// </summary> protected override void OnMouseClick(DataGridViewCellMouseEventArgs e) { Point p = new Point(e.X + _cellLocation.X, e.Y + _cellLocation.Y); if (p.X >= checkBoxLocation.X && p.X <= checkBoxLocation.X + checkBoxSize.Width && p.Y >= checkBoxLocation.Y && p.Y <= checkBoxLocation.Y + checkBoxSize.Height) { _checked = !_checked; //獲取列頭checkbox的選擇狀態 datagridviewCheckboxHeaderEventArgs ex = new datagridviewCheckboxHeaderEventArgs(); ex.CheckedState = _checked; object sender = new object();//此處不表明選擇的列頭checkbox,只是做爲參數傳遞。應該列頭checkbox是繪製出來的,沒法得到它的實例 if (OnCheckBoxClicked != null) { OnCheckBoxClicked(sender, ex);//觸發單擊事件 this.DataGridView.InvalidateCell(this); } } base.OnMouseClick(e); }
在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;//此處設爲true 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旁出現的文字 }
在研究一個新的東西時,我通常是先實現粗糙的功能,由淺入深漸漸細化這麼一個演變的過程,畢竟灰姑娘一下要變成白雪公主也是要有個過程的。
元芳,你怎麼看?