c# - DataGridView CheckBox 选择错误
问题描述
我们的应用程序有一个显示在 DataGridView 中的项目列表。第一列是 DataGridViewCheckBoxColumn。我们希望我们的应用程序允许用户单击行上的任意位置,以选择第一列中的 CheckBox。
我们发现如果用户直接点击 CheckBox,选择/取消选择效果很好。如果用户单击其他列中的数据也是如此。
但是,如果用户只点击复选框的一侧,我们会得到奇怪的行为。该行中的 CheckBox 未被选中/取消选中,但通常会选中另一行。为了更清楚地了解正在发生的事情,您可以查看我的错误行为短视频。
我尝试在代码中设置一些断点,例如,在我们的 SelectionChanged 处理程序、我们的 CellClick 处理程序和我们的 CellValueChanged 处理程序上。我发现这些断点以相同的模式命中,无论我是单击复选框,还是单击复选框的一侧或其他列中的数据。
有没有人见过这样的行为?任何想法可能会发生什么?它是 .NET DataGridView 代码中的一个错误,还是我应该在我们的代码中寻找什么?
这是相关代码,根据要求(或者您可以下载带有完整解决方案的 ZIP 文件)...
来自 Form1.cs:
public Form1()
{
InitializeComponent();
dgsControl.SetUp();
}
来自 Form1.Designer.cs:
private void InitializeComponent()
{
this.components = new System.ComponentModel.Container();
this.dgsControl = new DGSelection();
this.Controls.Add(this.dgsControl);
//
// dgsControl
//
this.dgsControl.Dock = System.Windows.Forms.DockStyle.Fill;
this.dgsControl.Location = new System.Drawing.Point(3, 3);
this.dgsControl.Margin = new System.Windows.Forms.Padding(2, 2, 2, 2);
this.dgsControl.Name = "dgsControl";
this.dgsControl.Size = new System.Drawing.Size(689, 325);
this.dgsControl.TabIndex = 0;
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.ClientSize = new System.Drawing.Size(800, 450);
this.Text = "DataGridView Demo";
}
来自 DGSelection.cs:
public partial class DGSelection : UserControl
{
#region Member variables
private class ListData
{
public string Option;
public string Description;
}
private static readonly List<ListData> TestData = new List<ListData>
{
new ListData { Option = "Option1", Description = "Description1" },
new ListData { Option = "Option2", Description = "Description2" },
new ListData { Option = "Option3", Description = "Description3" },
new ListData { Option = "Option4", Description = "Description4" }
};
public event EventHandler OptionsChanged;
#endregion
#region Constructor
public DGSelection()
{
InitializeComponent();
dgvTable.BackgroundColor = Color.DarkGray;
dgvTable.DefaultCellStyle.BackColor = Color.DarkGray;
dgvTable.DefaultCellStyle.ForeColor = Color.Black;
dgvTable.ColumnHeadersDefaultCellStyle.BackColor = Color.DarkGray;
dgvTable.ColumnHeadersDefaultCellStyle.ForeColor = Color.Black;
dgvTable.GridColor = Color.DarkGray;
cbxCheckAll.BackColor = Color.DarkGray;
// Move label where it belongs (moved elsewhere in Designer for ease of editing).
lbl_empty.Top = Top + 5;
}
#endregion
#region Public Methods
public void SetUp()
{
dgvTable.Rows.Clear();
cbxCheckAll.Checked = false;
bool anyRows = TestData.Any();
lbl_empty.Visible = !anyRows;
cbxCheckAll.Visible = anyRows;
dgvTable.ColumnHeadersVisible = anyRows;
foreach (ListData ld in TestData)
{
dgvTable.Rows.Add(false, ld.Option, ld.Description);
}
}
#endregion
#region Event Handlers
private void DGSelection_SelectionChanged(object sender, EventArgs e)
{
dgvTable.ClearSelection();
}
private void cbxCheckAll_CheckedChanged(object sender, EventArgs e)
{
try
{
dgvTable.CellValueChanged -= DgvTableCellValueChanged;
bool checkAll = cbxCheckAll.Checked;
foreach (DataGridViewRow row in dgvTable.Rows)
row.Cells[0].Value = checkAll;
}
finally
{
dgvTable.CellValueChanged += DgvTableCellValueChanged;
}
OptionsChanged?.Invoke(this, EventArgs.Empty);
}
private void DGSelection_CellClick(object sender, DataGridViewCellEventArgs e)
{
if (e.RowIndex < 0)
return; // Ignore clicks in the header row
DataGridViewCell checkBoxCell = dgvTable.Rows[e.RowIndex].Cells[0];
checkBoxCell.Value = !(bool)checkBoxCell.Value;
}
private void DgvTableCellValueChanged(object sender, DataGridViewCellEventArgs e)
{
try
{
cbxCheckAll.CheckedChanged -= cbxCheckAll_CheckedChanged;
cbxCheckAll.CheckedChanged -= cbxCheckAll_CheckedChanged;
// Not sure why, but sometimes subscribed twice
bool checkAll = dgvTable.Rows.Count > 0;
foreach (DataGridViewRow row in dgvTable.Rows)
checkAll &= row.Cells[0].Value.Equals(true);
cbxCheckAll.Checked = checkAll;
}
finally
{
cbxCheckAll.CheckedChanged += cbxCheckAll_CheckedChanged;
}
OptionsChanged?.Invoke(this, EventArgs.Empty);
}
private void DGSelection_CurrentCellDirtyStateChanged(object sender, EventArgs e)
{
dgvTable.CommitEdit(DataGridViewDataErrorContexts.Commit);
}
#endregion
}
来自 DGSelection.Designer.cs:
private void InitializeComponent()
{
components = new System.ComponentModel.Container();
System.Windows.Forms.DataGridViewCellStyle dataGridViewCellStyle1 = new System.Windows.Forms.DataGridViewCellStyle();
System.Windows.Forms.DataGridViewCellStyle dataGridViewCellStyle2 = new System.Windows.Forms.DataGridViewCellStyle();
System.Windows.Forms.DataGridViewCellStyle dataGridViewCellStyle3 = new System.Windows.Forms.DataGridViewCellStyle();
this.dgvTable = new System.Windows.Forms.DataGridView();
this.colCheckboxes = new System.Windows.Forms.DataGridViewCheckBoxColumn();
this.colText1 = new System.Windows.Forms.DataGridViewTextBoxColumn();
this.colText2 = new System.Windows.Forms.DataGridViewTextBoxColumn();
this.cbxCheckAll = new System.Windows.Forms.CheckBox();
this.lbl_empty = new System.Windows.Forms.Label();
((System.ComponentModel.ISupportInitialize)(this.dgvTable)).BeginInit();
this.SuspendLayout();
//
// dgvTable
//
this.dgvTable.AllowUserToAddRows = false;
this.dgvTable.AllowUserToDeleteRows = false;
this.dgvTable.AllowUserToResizeColumns = false;
this.dgvTable.AllowUserToResizeRows = false;
this.dgvTable.AutoSizeRowsMode = System.Windows.Forms.DataGridViewAutoSizeRowsMode.AllCells;
this.dgvTable.BackgroundColor = System.Drawing.SystemColors.Window;
this.dgvTable.BorderStyle = System.Windows.Forms.BorderStyle.None;
this.dgvTable.CellBorderStyle = System.Windows.Forms.DataGridViewCellBorderStyle.None;
this.dgvTable.ColumnHeadersBorderStyle = System.Windows.Forms.DataGridViewHeaderBorderStyle.Single;
dataGridViewCellStyle1.Alignment = System.Windows.Forms.DataGridViewContentAlignment.MiddleLeft;
dataGridViewCellStyle1.BackColor = System.Drawing.SystemColors.ControlDark;
dataGridViewCellStyle1.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
dataGridViewCellStyle1.ForeColor = System.Drawing.SystemColors.WindowText;
dataGridViewCellStyle1.Padding = new System.Windows.Forms.Padding(3);
dataGridViewCellStyle1.SelectionBackColor = System.Drawing.SystemColors.Highlight;
dataGridViewCellStyle1.SelectionForeColor = System.Drawing.SystemColors.HighlightText;
dataGridViewCellStyle1.WrapMode = System.Windows.Forms.DataGridViewTriState.True;
this.dgvTable.ColumnHeadersDefaultCellStyle = dataGridViewCellStyle1;
this.dgvTable.ColumnHeadersHeightSizeMode = System.Windows.Forms.DataGridViewColumnHeadersHeightSizeMode.AutoSize;
this.dgvTable.Columns.AddRange(new System.Windows.Forms.DataGridViewColumn[] {
this.colCheckboxes,
this.colText1,
this.colText2 });
this.dgvTable.Dock = System.Windows.Forms.DockStyle.Fill;
this.dgvTable.EditMode = System.Windows.Forms.DataGridViewEditMode.EditOnEnter;
this.dgvTable.EnableHeadersVisualStyles = false;
this.dgvTable.Location = new System.Drawing.Point(0, 0);
this.dgvTable.Margin = new System.Windows.Forms.Padding(2);
this.dgvTable.MultiSelect = false;
this.dgvTable.Name = "dgvTable";
this.dgvTable.RowHeadersVisible = false;
this.dgvTable.RowTemplate.Height = 24;
this.dgvTable.ScrollBars = System.Windows.Forms.ScrollBars.Vertical;
this.dgvTable.SelectionMode = System.Windows.Forms.DataGridViewSelectionMode.CellSelect;
this.dgvTable.Size = new System.Drawing.Size(484, 318);
this.dgvTable.TabIndex = 0;
this.dgvTable.CellClick += new System.Windows.Forms.DataGridViewCellEventHandler(this.DGSelection_CellClick);
this.dgvTable.CellValueChanged += new System.Windows.Forms.DataGridViewCellEventHandler(this.DgvTableCellValueChanged);
this.dgvTable.CurrentCellDirtyStateChanged += new System.EventHandler(this.DGSelection_CurrentCellDirtyStateChanged);
this.dgvTable.SelectionChanged += new System.EventHandler(this.DGSelection_SelectionChanged);
//
// colCheckboxes
//
this.colCheckboxes.AutoSizeMode = System.Windows.Forms.DataGridViewAutoSizeColumnMode.None;
this.colCheckboxes.Frozen = true;
this.colCheckboxes.HeaderText = "";
this.colCheckboxes.Name = "colCheckboxes";
this.colCheckboxes.Resizable = System.Windows.Forms.DataGridViewTriState.False;
this.colCheckboxes.Width = 30;
//
// colText1
//
this.colText1.AutoSizeMode = System.Windows.Forms.DataGridViewAutoSizeColumnMode.AllCells;
dataGridViewCellStyle2.Padding = new System.Windows.Forms.Padding(5, 0, 0, 0);
this.colText1.DefaultCellStyle = dataGridViewCellStyle2;
this.colText1.HeaderText = "Option";
this.colText1.Name = "colText1";
this.colText1.ReadOnly = true;
this.colText1.Resizable = System.Windows.Forms.DataGridViewTriState.False;
this.colText1.SortMode = System.Windows.Forms.DataGridViewColumnSortMode.NotSortable;
this.colText1.Width = 57;
//
// colText2
//
this.colText2.AutoSizeMode = System.Windows.Forms.DataGridViewAutoSizeColumnMode.Fill;
dataGridViewCellStyle3.Padding = new System.Windows.Forms.Padding(5, 0, 0, 0);
this.colText2.DefaultCellStyle = dataGridViewCellStyle3;
this.colText2.HeaderText = "Description";
this.colText2.Name = "colText2";
this.colText2.ReadOnly = true;
this.colText2.Resizable = System.Windows.Forms.DataGridViewTriState.False;
this.colText2.SortMode = System.Windows.Forms.DataGridViewColumnSortMode.NotSortable;
//
// cbxCheckAll
//
this.cbxCheckAll.AutoSize = true;
this.cbxCheckAll.BackColor = System.Drawing.SystemColors.ControlDark;
this.cbxCheckAll.Location = new System.Drawing.Point(8, 5);
this.cbxCheckAll.Margin = new System.Windows.Forms.Padding(2);
this.cbxCheckAll.Name = "cbxCheckAll";
this.cbxCheckAll.Size = new System.Drawing.Size(15, 14);
this.cbxCheckAll.TabIndex = 1;
this.cbxCheckAll.UseVisualStyleBackColor = false;
this.cbxCheckAll.CheckedChanged += new System.EventHandler(this.cbxCheckAll_CheckedChanged);
//
// lbl_empty
//
this.lbl_empty.Anchor = ((System.Windows.Forms.AnchorStyles)
(((System.Windows.Forms.AnchorStyles.Top |
System.Windows.Forms.AnchorStyles.Left) |
System.Windows.Forms.AnchorStyles.Right)));
this.lbl_empty.BackColor = System.Drawing.Color.Transparent;
this.lbl_empty.Location = new System.Drawing.Point(3, 25);
this.lbl_empty.Name = "lbl_empty";
this.lbl_empty.Size = new System.Drawing.Size(478, 44);
this.lbl_empty.TabIndex = 2;
this.lbl_empty.Text = "No data defined for the list";
this.lbl_empty.Visible = false;
//
// DGSelection
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.Controls.Add(this.lbl_empty);
this.Controls.Add(this.cbxCheckAll);
this.Controls.Add(this.dgvTable);
this.Margin = new System.Windows.Forms.Padding(2);
this.Name = "DGSelectionControl";
this.Size = new System.Drawing.Size(484, 318);
((System.ComponentModel.ISupportInitialize)(this.dgvTable)).EndInit();
this.ResumeLayout(false);
this.PerformLayout();
}
我们的代码中有什么东西导致了这种行为吗?或者它是DataGridView 实现中的一个错误?我已经用 .NET Framework v.4.6 和 v.4.8 复制了这个。
(注:转自微软问答论坛,因为我在那里没有得到回复。)
解决方案
我建议进行这些更改(使用 .Net Framework 4.8 测试):
不要使用 CheckBox事件:它会在尝试更改Select All CheckBox 状态时
CheckedChanged
干扰事件。请改用事件。 这也将允许摆脱所有那些添加处理程序/删除处理程序的东西。CellValueChanged
Click
单击单元格后立即调用RefreshEdit()更新 CheckBox 单元格的状态:这将立即更新 CheckBox 值(这是您在单击单元格区域而不是 CheckBox 内容时看到的问题:控件没有立即更新)。
有关更多详细信息,请参阅此处的注释:
Programmatically check a DataGridView CheckBox that was just unchecked删除它
CommitEdit(DataGridViewDataErrorContexts.Commit);
:如果您需要立即更新值,请改为调用DataGridView.EndEdit()方法(也请参阅这些注释)。
这就是它现在的工作方式:
private void cbxCheckAll_Click(object sender, EventArgs e)
{
if (dgvTable.Rows.Count == 0) return;
try {
bool checkAll = cbxCheckAll.Checked;
foreach (DataGridViewRow row in dgvTable.Rows) {
row.Cells[0].Value = checkAll;
}
}
finally {
OptionsChanged?.Invoke(this, EventArgs.Empty);
}
}
private void DGSelection_CellClick(object sender, DataGridViewCellEventArgs e)
{
if (e.RowIndex < 0) return;
bool currentValue = (bool)dgvTable[0, e.RowIndex].Value;
dgvTable[0, e.RowIndex].Value = !currentValue;
}
private void DgvTableCellValueChanged(object sender, DataGridViewCellEventArgs e)
{
if (!dgvTable.IsHandleCreated) return;
cbxCheckAll.Checked = dgvTable.Rows.OfType<DataGridViewRow>().All(r => (bool)r.Cells[0].Value == true);
dgvTable.BeginInvoke(new Action(() => dgvTable.RefreshEdit()));
OptionsChanged?.Invoke(this, EventArgs.Empty);
}
推荐阅读
- angular - 如何将数据从数据库传递到 ng multiselect-dropdown
- python - 带有 Plotly Express 的宽格式 CSV
- python - PyTorch - 将不同函数应用于张量的不同“行/列”的有效方法
- html - 如何将类名添加到所有“%th”
- javascript - 即使单击外部的按钮,如何保持输入的焦点
- javascript - 创建 d3 条形图
- python - 使用(多个)嵌套 For 循环消除冗余
- html - 如何在 tailwindcss 中覆盖 @apply 指令
- javascript - 如何使用 localStorage 项目更新 vue 数据属性
- ios - 难以将来自 TextField 的用户输入作为 Double / Date 传递