我有一个自定义类
CheckboxHeaderCell
继承自DataGridViewColumnHeaderCell
,可以将复选框添加到任何数据网格视图中。复选框添加成功,但在复选框选中事件上仅自定义类事件触发,但我添加此复选框的表单(方法HeaderCell_CheckedChanged
)中的事件永远不会触发。
using System;
using System.Drawing;
using System.Windows.Forms;
namespace test.CustomControls
{
public class CheckboxHeaderCell : DataGridViewColumnHeaderCell
{
public event EventHandler CheckedChanged;
public CheckBox checkBox;
public bool IsChecked = false;
public CheckboxHeaderCell()
{
checkBox = new CheckBox();
checkBox.BackColor = SystemColors.Control;
checkBox.Margin = new Padding(0);
checkBox.Padding = new Padding(0);
checkBox.Size = new Size(14, 14);
checkBox.UseVisualStyleBackColor = true;
checkBox.CheckedChanged += CheckBox_CheckedChanged;
CheckedChanged = new EventHandler(doNothing);
}
public void CheckBox_CheckedChanged(object sender, EventArgs e)
{
IsChecked = checkBox.Checked;
CheckedChanged?.Invoke(this, EventArgs.Empty);
if (DataGridView != null)
{
DataGridView.InvalidateCell(this);
}
}
public void doNothing(object sender, EventArgs e)
{
}
//other methods, paint etc
}
}
我使用此类通过以下方法将自定义复选框添加到我的 dgv 中:
private void AddCheckBoxDGV(DataGridView dgv, string columnName)
{
if (dgv.Columns.Contains(columnName))
{
CustomControls.CheckboxHeaderCell checkboxHeaderCell = new CustomControls.CheckboxHeaderCell();
checkboxHeaderCell.CheckedChanged += HeaderCell_CheckedChanged;
dgv.Columns[columnName].HeaderCell = checkboxHeaderCell;
}
}
private void HeaderCell_CheckedChanged(object sender, EventArgs e)
{
//do smthing
}
您的自定义
HeaderCell
不会执行任何操作,只会创建一个您永远不会作为 UI 元素看到或访问的 CheckBox
。既不在 HeaderCell
上,也不在网格上的其他任何地方。所以,我不明白如何:
复选框添加成功...
您必须将 Control 添加到某个
Parent.Controls
集合,并且 Parent
必须直接(本身)或间接(其父级)添加到顶级控件。例如,Form
。 HeaderCell
不是这样使用的控件。
为了证明这个概念,将
CheckBox
添加到 yourGrid.Controls
,您将在左上角看到它,勾选它,您将引发 CheckedChanged
事件。
我知道您需要一个自定义的
DataGridViewCheckBoxColumn
,并在 HeaderCell
上有一个主复选框来选中/取消选中所有内容。这是一个例子。
▶ 标题单元格
在第一部分中,从
DataGridViewColumnHeaderCell
和 派生一个新类
CheckState
。如果检查了所有单元格,则为 Checked
;如果未检查所有单元格,则为 Unchecked
;如果未检查所有单元格,则为 Indeterminate
。OnMouseClick
方法来切换状态并设置属性,Value
属性设置为 true
或 false
。public class CheckAllColumnHeaderCell : DataGridViewColumnHeaderCell
{
enum MouseState : int
{
Normal,
Hot,
Down
}
CheckState _checkState = CheckState.Unchecked;
MouseState _mouseState = MouseState.Normal; // See the mouse methods...
Rectangle _checkBoxRect; // See the Paint method...
public CheckState CheckState
{
get => _checkState;
set
{
if (_checkState != value)
{
if (DataGridView.RowCount == 0 ||
DataGridView.Rows[0].IsNewRow) return;
_checkState = value;
DataGridView?.InvalidateCell(this);
SetCellsCheckState();
}
}
}
private void SetCellsCheckState()
{
if (CheckState != CheckState.Indeterminate)
{
bool state = CheckState == CheckState.Checked;
foreach (var row in DataGridView.Rows.Cast<DataGridViewRow>())
{
if (!row.IsNewRow) row.Cells[ColumnIndex].Value = state;
}
}
}
protected override void OnMouseClick(DataGridViewCellMouseEventArgs e)
{
base.OnMouseClick(e);
if (e.Button == MouseButtons.Left &&
DataGridView.RowCount > 0 && !DataGridView.Rows[0].IsNewRow &&
_checkBoxRect.Contains(DataGridView.PointToClient(Cursor.Position)))
{
CheckState = CheckState == CheckState.Checked ||
CheckState == CheckState.Indeterminate
? CheckState.Unchecked
: CheckState.Checked;
}
}
第二部分是当
DataGridView
的一些相关事件发生时更新主CheckBox的状态。例如,当用户勾选/取消勾选所属列的单元格时、添加或删除行时...
protected override void OnDataGridViewChanged()
{
base.OnDataGridViewChanged();
if (DataGridView != null)
{
DataGridView.CurrentCellDirtyStateChanged -= OnCurrentCellDirtyStateChanged;
DataGridView.CellValueChanged -= OnDataGridViewOnCellValueChanged;
DataGridView.RowsAdded -= OnDataGridViewRowsAdded;
DataGridView.RowsRemoved -= OnDataGridViewRowsRemoved;
DataGridView.CurrentCellDirtyStateChanged += OnCurrentCellDirtyStateChanged;
DataGridView.CellValueChanged += OnDataGridViewOnCellValueChanged;
DataGridView.RowsAdded += OnDataGridViewRowsAdded;
DataGridView.RowsRemoved += OnDataGridViewRowsRemoved;
}
}
private void OnDataGridViewOnCellValueChanged(object sender,
DataGridViewCellEventArgs e)
{
if (e.RowIndex >= 0 && e.ColumnIndex == ColumnIndex)
{
if (DataGridView.NewRowIndex == e.RowIndex)
DataGridView.NotifyCurrentCellDirty(true);
else
UpdateCheckState();
}
}
private void OnDataGridViewRowsAdded(object sender,
DataGridViewRowsAddedEventArgs e) => UpdateCheckState();
private void OnDataGridViewRowsRemoved(object sender,
DataGridViewRowsRemovedEventArgs e) => UpdateCheckState();
private void OnCurrentCellDirtyStateChanged(object sender, EventArgs e)
{
if (DataGridView.CurrentCell?.ColumnIndex == ColumnIndex)
UpdateCheckState();
}
private void UpdateCheckState()
{
DataGridView.EndEdit();
int total = DataGridView.RowCount;
if (DataGridView.AllowUserToAddRows) total--;
int chkCount = DataGridView.Rows.Cast<DataGridViewRow>()
.Aggregate(0, (count, row) =>
{
count += (bool)row.Cells[ColumnIndex].EditedFormattedValue ? 1 : 0;
return count;
});
_checkState = chkCount == 0
? CheckState.Unchecked
: chkCount == total
? CheckState.Checked
: CheckState.Indeterminate;
DataGridView.InvalidateCell(this);
}
最后一部分,绘制主复选框。重写鼠标事件以更新 mouse state,该状态在
Paint
方法中使用,以确定要绘制哪个 CheckBoxState
。在本例中,我将使用 CheckBoxRenderer
来绘制 CheckBox
。您可以尝试使用 ControlPaint.DrawCheckBox
或按您想要的方式绘制。
protected override void OnMouseMove(DataGridViewCellMouseEventArgs e)
{
base.OnMouseMove(e);
if (Control.MouseButtons != MouseButtons.None) return;
if (_checkBoxRect.Contains(DataGridView.PointToClient(Cursor.Position)))
{
if (_mouseState != MouseState.Hot)
{
_mouseState = MouseState.Hot;
DataGridView.InvalidateCell(this);
}
}
else if (_mouseState != MouseState.Normal)
{
_mouseState = MouseState.Normal;
DataGridView.InvalidateCell(this);
}
}
protected override void OnMouseDown(DataGridViewCellMouseEventArgs e)
{
base.OnMouseDown(e);
if (e.Button == MouseButtons.Left &&
_checkBoxRect.Contains(DataGridView.PointToClient(Cursor.Position)))
{
_mouseState = MouseState.Down;
DataGridView.InvalidateCell(this);
}
}
protected override void OnMouseUp(DataGridViewCellMouseEventArgs e)
{
base.OnMouseUp(e);
if (e.Button == MouseButtons.Left && _checkBoxRect
.Contains(DataGridView.PointToClient(Cursor.Position)))
{
_mouseState = MouseState.Normal;
DataGridView.InvalidateCell(this);
}
}
protected override void OnMouseLeave(int rowIndex)
{
base.OnMouseLeave(rowIndex);
if (_mouseState != MouseState.Normal)
{
_mouseState = MouseState.Normal;
DataGridView.InvalidateCell(this);
}
}
protected override void Paint(Graphics graphics,
Rectangle clipBounds,
Rectangle cellBounds,
int rowIndex,
DataGridViewElementStates dataGridViewElementState,
object value,
object formattedValue,
string errorText,
DataGridViewCellStyle cellStyle,
DataGridViewAdvancedBorderStyle advancedBorderStyle,
DataGridViewPaintParts paintParts)
{
paintParts &= ~DataGridViewPaintParts.ContentForeground;
base.Paint(graphics,
clipBounds,
cellBounds,
rowIndex,
dataGridViewElementState,
value,
formattedValue,
errorText,
cellStyle,
advancedBorderStyle,
paintParts);
bool enabled = DataGridView.Enabled;
CheckBoxState state = CheckBoxState.UncheckedNormal;
switch (CheckState)
{
case CheckState.Checked:
state = !enabled ? CheckBoxState.CheckedDisabled
: _mouseState == MouseState.Down
? CheckBoxState.CheckedPressed
: _mouseState == MouseState.Hot
? CheckBoxState.CheckedHot
: CheckBoxState.CheckedNormal;
break;
case CheckState.Indeterminate:
state = !enabled ? CheckBoxState.MixedDisabled
: _mouseState == MouseState.Down
? CheckBoxState.MixedPressed
: _mouseState == MouseState.Hot
? CheckBoxState.MixedHot
: CheckBoxState.MixedNormal;
break;
case CheckState.Unchecked:
state = !enabled ? CheckBoxState.UncheckedDisabled
: _mouseState == MouseState.Down
? CheckBoxState.UncheckedPressed
: _mouseState == MouseState.Hot
? CheckBoxState.UncheckedHot
: CheckBoxState.UncheckedNormal;
break;
}
Size sz = CheckBoxRenderer.GetGlyphSize(graphics, state);
Point pt = new Point(
cellBounds.X + (cellBounds.Width - sz.Width) / 2,
cellBounds.Y + (cellBounds.Height - sz.Height) / 2);
_checkBoxRect = new Rectangle(pt, sz);
CheckBoxRenderer.DrawCheckBox(graphics, pt, state);
}
}
一些想法,请参阅:
▶ 色谱柱和单元格
从
DataGridViewCheckBoxCell
派生一个新类。除非你想重写 Paint
方法来自己绘制 CheckBox。您不需要在这里做任何事情。主要用于设置列类的CellTemplate
。
public class CheckAlCheckBoxlCell : DataGridViewCheckBoxCell
{
public CheckAlCheckBoxlCell() { }
}
...并从
DataGridViewCheckBoxColumn
衍生出另一个。
public class CheckAllCheckBoxColumn : DataGridViewCheckBoxColumn
{
private readonly CheckAllColumnHeaderCell _headerCell;
public CheckAllCheckBoxColumn()
{
CellTemplate = new CheckAlCheckBoxlCell();
_headerCell = new CheckAllColumnHeaderCell();
HeaderCell = _headerCell;
}
public override DataGridViewCell CellTemplate
{
get => base.CellTemplate;
set
{
if (value != null &&
!value.GetType().IsAssignableFrom(typeof(CheckAlCheckBoxlCell)))
throw new InvalidCastException("Must be a CheckAlCheckBoxlCell.");
base.CellTemplate = value;
}
}
[Browsable(false)]
public CheckState CheckState
{
get => _headerCell.CheckState;
set
{
switch (value)
{
case CheckState.Checked:
case CheckState.Indeterminate:
_headerCell.CheckState = CheckState.Checked;
break;
default:
_headerCell.CheckState = CheckState.Unchecked;
break;
}
}
}
}