前言:其实我感觉在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);
}
}
}
实现效果: