前言:其实我感觉在Winform中的话,DataGridView提供的属性已经足够我们使用了,包括简单调一些背景、字体以及其他样式也能做到简单大方。但是在选择框这里确实是个短板,首先是调整行的大小或者字体颜色的时候,CheckBox并不会随着改变,单独设置也没啥效果;简而言之,无论列高设置多少,这个控件依然是那么一点。如果使用到触摸屏上的话,操作就会很难受,美观度的话更是差强人意。.
所以,这里单独对CheckBox进行一个重绘,其实主要还是用来改变大小以及实现全选功能。
-
新建一个自定义控件继承自DataGridView,并在其中定义以下属性
public partial class GridViewEx : DataGridView{[Description("选择框选中样式")]public Font CheckFont { get; set; } = new Font("黑体", 18);[Description("选择框选中颜色")]public Color CheckColor { get; set; } = Color.FromArgb(0, 0, 139);[Description("选择框未选中颜色")]public Color NormalCheckColor { get; set; } = Color.Black;private string _CheckAllColumn;[Description("使用全选按钮的列")]public string UseCheckAllColumn{get { return _CheckAllColumn; }set { _CheckAllColumn = value; }}
-
绘制每一列的选择框样式,其实选择框可以考虑使用图片以提升美观度,但是基于效率考虑的话,我是选择了绘制矩形,然后在矩形内部绘制一个对勾进行选中
private void DrawCheckBox(Graphics graphics, Rectangle rect, bool isCheck){graphics.DrawRectangle(new Pen(NormalCheckColor), rect);if (isCheck){SizeF textSize = graphics.MeasureString("√", CheckFont);graphics.DrawString("√", CheckFont, new SolidBrush(CheckColor), rect.X + (rect.Height - textSize.Width) / 2, rect.Y + (rect.Height - textSize.Height) / 2);}}
-
绘制头部的选择框样式,用来实现全选功能
private void DrawHeaderCheckBox(Graphics graphics, Rectangle rect, bool isCheck){graphics.DrawRectangle(new Pen(ColumnHeadersDefaultCellStyle.ForeColor), rect);if (isCheck){SizeF textSize = graphics.MeasureString("√", CheckFont);graphics.DrawString("√", CheckFont, new SolidBrush(ColumnHeadersDefaultCellStyle.ForeColor), rect.X + (rect.Height - textSize.Width) / 2, rect.Y + (rect.Height - textSize.Height) / 2);}}
-
在
OnCellPainting事件中进行绘制调用,这里的大小是取了行高的一半。所以选择框的大小是根据行高自动调整的。值得注意的是,这里加入了一个UseCheckAllColumn字段,只有当设置了这个字段需要有全选时才会绘制全选选择框,可以适应有多列选择框的情况。protected override void OnCellPainting(DataGridViewCellPaintingEventArgs e){base.OnCellPainting(e);int size = e.CellBounds.Height / 2;int x = e.CellBounds.X + (e.CellBounds.Width - size) / 2;int y = e.CellBounds.Y + (e.CellBounds.Height - size) / 2;Rectangle rect = new Rectangle(x, y, size, size);if (e.RowIndex == -1 && e.ColumnIndex > -1 && UseCheckAllColumn != null && Columns[e.ColumnIndex] is DataGridViewCheckBoxColumn){var column = Columns[e.ColumnIndex];if (column.Tag == null){column.Tag = false;}column.HeaderText = "";e.PaintBackground(rect, true);DrawHeaderCheckBox(e.Graphics, rect, (bool)column.Tag);e.Handled = true;}else if (e.RowIndex > -1 && e.ColumnIndex > -1 && Columns[e.ColumnIndex] is DataGridViewCheckBoxColumn){e.PaintBackground(rect, true);DrawCheckBox(e.Graphics, rect, (bool)e.Value);e.Handled = true;}}
-
在选中或者取消选中的时候需要重绘对应的状态,所以在
OnCellClick事件中重新绘制一遍,同时如果点击的是全选时,则需要对所有行进行一个状态重绘。protected override void OnCellClick(DataGridViewCellEventArgs e){base.OnCellClick(e);if (e.RowIndex == -1 && e.ColumnIndex > -1 && Columns[e.ColumnIndex].Name == UseCheckAllColumn){DataGridViewColumn column = Columns[e.ColumnIndex];if (column is DataGridViewCheckBoxColumn){column.Tag = !(bool)column.Tag;foreach (DataGridViewRow row in Rows){row.Cells[e.ColumnIndex].Value = column.Tag;}InvalidateCell(e.ColumnIndex, e.RowIndex);}}else if (e.RowIndex > -1 && e.ColumnIndex > -1){DataGridViewColumn column = Columns[e.ColumnIndex];if (column is DataGridViewCheckBoxColumn){bool value = (bool)Rows[e.RowIndex].Cells[e.ColumnIndex].Value;Rows[e.RowIndex].Cells[e.ColumnIndex].Value = !value;InvalidateCell(e.ColumnIndex, e.RowIndex);}}}
-
最后,我们需要在数据源发生改变的时候,对选中状态进行一个初始化,也就是回到未选中状态
protected override void OnDataBindingComplete(DataGridViewBindingCompleteEventArgs e){base.OnDataBindingComplete(e);if (UseCheckAllColumn != null){var column = Columns[UseCheckAllColumn];if (column.Tag != null&&(bool)column.Tag == true){column.Tag = false;InvalidateCell(Columns[UseCheckAllColumn].Index, -1);}}}
实现效果:
