How to detect DataGridView CheckBox event change?

I have a winforms app and want to trigger some code when a checkbox embedded in a DataGridView control is checked / unchecked. Every event I have tried either

  1. Triggers as soon as the CheckBox is clicked but before its checked state changes, or
  2. Triggers only once the CheckBox looses its focus

I can't seem to find event that triggers immediately after the checked state changes.


Edit:

What I am trying to achieve is that when the checked state of a CheckBox in one DataGridView changes, the data in two other DataGridViews changes. Yet all the events I have used, the data in the other grids only changes after the CheckBox in the first DataGridView looses focus.

169782 次浏览

要处理 DatGridViewsCheckedChanged事件,必须首先激发 CellContentClick(它没有 CheckBoxes 当前状态!)然后打电话给 CommitEdit。这将反过来激发 CellValueChanged事件,您可以使用它来完成您的工作。这是微软的疏忽.做一些像下面这样的事情..。

private void dataGridViewSites_CellContentClick(object sender,
DataGridViewCellEventArgs e)
{
dataGridViewSites.CommitEdit(DataGridViewDataErrorContexts.Commit);
}


/// <summary>
/// Works with the above.
/// </summary>
private void dataGridViewSites_CellValueChanged(object sender,
DataGridViewCellEventArgs e)
{
UpdateDataGridViewSite();
}

希望这个能帮上忙。

附注: 检查这篇文章 https://msdn.microsoft.com/en-us/library/system.windows.forms.datagridview.currentcelldirtystatechanged(v=vs.110).aspx

下面是一些代码:

private void dgvStandingOrder_CellContentClick(object sender, DataGridViewCellEventArgs e)
{
if (dgvStandingOrder.Columns[e.ColumnIndex].Name == "IsSelected" && dgvStandingOrder.CurrentCell is DataGridViewCheckBoxCell)
{
bool isChecked = (bool)dgvStandingOrder[e.ColumnIndex, e.RowIndex].EditedFormattedValue;
if (isChecked == false)
{
dgvStandingOrder.Rows[e.RowIndex].Cells["Status"].Value = "";
}
dgvStandingOrder.EndEdit();
}
}


private void dgvStandingOrder_CellEndEdit(object sender, DataGridViewCellEventArgs e)
{


dgvStandingOrder.CommitEdit(DataGridViewDataErrorContexts.Commit);
}


private void dgvStandingOrder_CurrentCellDirtyStateChanged(object sender, EventArgs e)
{
if (dgvStandingOrder.CurrentCell is DataGridViewCheckBoxCell)
{
dgvStandingOrder.CommitEdit(DataGridViewDataErrorContexts.Commit);
}
}

我找到了@Killercam 的解决方案,但如果用户双击太快,就有点可疑了。不知道其他人是否也发现了这个案子。我找到了另一个解决方案 给你

它使用了 datagrid 的 CellValueChangedCellMouseUp

”原因是 OnCellvalueChanged 事件在 DataGridView 认为您已完成编辑之前不会触发。这对于 TextBox 列来说是有意义的,因为 OnCellvalueChanged 不会为每次键击而费心触发,但对于 CheckBox 来说是没有意义的。”

以下是他的实例:

private void myDataGrid_OnCellValueChanged(object sender, DataGridViewCellEventArgs e)
{
if (e.ColumnIndex == myCheckBoxColumn.Index && e.RowIndex != -1)
{
// Handle checkbox state change here
}
}

以及告诉复选框单击时已完成编辑的代码,而不是等到用户离开字段:

private void myDataGrid_OnCellMouseUp(object sender,DataGridViewCellMouseEventArgs e)
{
// End of edition on each click on column of checkbox
if (e.ColumnIndex == myCheckBoxColumn.Index && e.RowIndex != -1)
{
myDataGrid.EndEdit();
}
}

编辑: DoubleClick 事件与 MouseUp 事件分开处理。如果检测到 DoubleClick 事件,应用程序将完全忽略第一个 MouseUp 事件。除了 MouseUp 事件之外,还需要将此逻辑添加到 CellDoubleClick 事件:

private void myDataGrid_OnCellDoubleClick(object sender,DataGridViewCellEventArgs e)
{
// End of edition on each click on column of checkbox
if (e.ColumnIndex == myCheckBoxColumn.Index && e.RowIndex != -1)
{
myDataGrid.EndEdit();
}
}

对我起作用的是 CurrentCellDirtyStateChangeddatagridView1.EndEdit()的结合

private void dataGridView1_CurrentCellDirtyStateChanged( object sender, EventArgs e ) {
if ( dataGridView1.CurrentCell is DataGridViewCheckBoxCell ) {
DataGridViewCheckBoxCell cb = (DataGridViewCheckBoxCell)dataGridView1.CurrentCell;
if ( (byte)cb.Value == 1 ) {
dataGridView1.CurrentRow.Cells["time_loadedCol"].Value = DateTime.Now.ToString();
}
}
dataGridView1.EndEdit();
}

根据杀手摄像头的回答,我的代码

private void dgvProducts_CellContentClick(object sender, DataGridViewCellEventArgs e)
{
dgvProducts.CommitEdit(DataGridViewDataErrorContexts.Commit);
}

以及:

private void dgvProducts_CellValueChanged(object sender, DataGridViewCellEventArgs e)
{
if (dgvProducts.DataSource != null)
{
if (dgvProducts.Rows[e.RowIndex].Cells[e.ColumnIndex].Value.ToString() == "True")
{
//do something
}
else
{
//do something
}
}
}

这也处理键盘激活。

    private void dgvApps_CellContentClick(object sender, DataGridViewCellEventArgs e)
{
if(dgvApps.CurrentCell.GetType() == typeof(DataGridViewCheckBoxCell))
{
if (dgvApps.CurrentCell.IsInEditMode)
{
if (dgvApps.IsCurrentCellDirty)
{
dgvApps.EndEdit();
}
}
}
}




private void dgvApps_CellValueChanged(object sender, DataGridViewCellEventArgs e)
{
// handle value changed.....
}

Jstrtevants 的解决方案很有效。但是,我选择在 EndEdit 事件中进行处理。我更喜欢这种方法(在我的应用程序中) ,因为与 CellValueChanged 事件不同,在填充网格时 EndEdit 事件不会触发。

这是我的密码(其中一部分是从 jstrtevant 偷来的:

private void gridCategories_CellEndEdit(object sender, DataGridViewCellEventArgs e)
{
if (e.ColumnIndex == gridCategories.Columns["AddCategory"].Index)
{
//do some stuff
}
}






private void gridCategories_CellMouseUp(object sender, DataGridViewCellMouseEventArgs e)
{
if (e.ColumnIndex == gridCategories.Columns["AddCategory"].Index)
{
gridCategories.EndEdit();
}
}

这都是关于编辑单元格的,问题是单元格实际上没有被编辑,所以你需要保存单元格或行的更改,当你点击复选框时,你就可以得到事件,这样你就可以使用这个函数:

datagridview.CommitEdit(DataGridViewDataErrorContexts.CurrentCellChange)

有了这个,你甚至可以在不同的事件中使用它。

该代码将在 DataGridView 中循环并检查是否选中了 CheckBox 列

private void dgv1_CellMouseUp(object sender, DataGridViewCellMouseEventArgs e)
{
if (e.ColumnIndex == 0 && e.RowIndex > -1)
{
dgv1.CommitEdit(DataGridViewDataErrorContexts.Commit);
var i = 0;
foreach (DataGridViewRow row in dgv1.Rows)
{
if (Convert.ToBoolean(row.Cells[0].Value))
{
i++;
}
}


//Enable Button1 if Checkbox is Checked
if (i > 0)
{
Button1.Enabled = true;
}
else
{
Button1.Enabled = false;
}
}
}

为了在使用 devExpress xtracrid 时执行此操作,需要处理相应存储库项的 价值改变事件,如 给你所述。调用 gridView1.PostEditor ()方法以确保已发布更改的值也很重要。下面是一个实现方案:

        private void RepositoryItemCheckEdit1_EditValueChanged(object sender, System.EventArgs e)
{
gridView3.PostEditor();


var isNoneOfTheAboveChecked = false;


for (int i = 0; i < gridView3.DataRowCount; i++)
{
if ((bool) (gridView3.GetRowCellValue(i, "NoneOfTheAbove")) && (bool) (gridView3.GetRowCellValue(i, "Answer")))
{
isNoneOfTheAboveChecked = true;
break;
}
}


if (isNoneOfTheAboveChecked)
{
for (int i = 0; i < gridView3.DataRowCount; i++)
{
if (!((bool)(gridView3.GetRowCellValue(i, "NoneOfTheAbove"))))
{
gridView3.SetRowCellValue(i, "Answer", false);
}
}
}
}

请注意,由于 xtracrid 不提供枚举器,因此有必要使用 for 循环来迭代行。

对于这个问题,我已经找到了一个更简单的答案。我只是用了反向逻辑。这段代码是用 VB 写的,但是和 C # 没什么不同。

 Private Sub DataGridView1_CellContentClick(sender As Object, e As
DataGridViewCellEventArgs) Handles DataGridView1.CellContentClick


Dim _ColumnIndex As Integer = e.ColumnIndex
Dim _RowIndex As Integer = e.RowIndex


'Uses reverse logic for current cell because checkbox checked occures
'after click
'If you know current state is False then logic dictates that a click
'event will set it true
'With these 2 check boxes only one can be true while both can be off


If DataGridView1.Rows(_RowIndex).Cells("Column2").Value = False And
DataGridView1.Rows(_RowIndex).Cells("Column3").Value = True Then
DataGridView1.Rows(_RowIndex).Cells("Column3").Value = False
End If


If DataGridView1.Rows(_RowIndex).Cells("Column3").Value = False And
DataGridView1.Rows(_RowIndex).Cells("Column2").Value = True Then
DataGridView1.Rows(_RowIndex).Cells("Column2").Value = False
End If




End Sub

关于这一点,最好的事情之一是不需要多个事件。

在单元格值更改后移除焦点,可以在 DataGridView 中更新这些值。通过将 CurrentCell 设置为 null 来移除焦点。

private void DataGridView1OnCellValueChanged(object sender, DataGridViewCellEventArgs dataGridViewCellEventArgs)
{
// Remove focus
dataGridView1.CurrentCell = null;
// Put in updates
Update();
}


private void DataGridView1OnCurrentCellDirtyStateChanged(object sender, EventArgs eventArgs)
{
if (dataGridView1.IsCurrentCellDirty)
{
dataGridView1.CommitEdit(DataGridViewDataErrorContexts.Commit);
}


}

您可以在单击复选框并捕获 CellValueChanged事件后立即强制单元格提交值。单击该复选框后,CurrentCellDirtyStateChanged将立即触发。

以下代码适用于我:

private void grid_CurrentCellDirtyStateChanged(object sender, EventArgs e)
{
SendKeys.Send("{tab}");
}

然后可以在 手机价值改变了事件中插入代码。

In the event CellContentClick you can use this strategy:

private void myDataGrid_CellContentClick(object sender, DataGridViewCellEventArgs e)
{
if (e.ColumnIndex == 2)//set your checkbox column index instead of 2
{   //When you check
if (Convert.ToBoolean(myDataGrid.Rows[e.RowIndex].Cells[2].EditedFormattedValue) == true)
{
//EXAMPLE OF OTHER CODE
myDataGrid.Rows[e.RowIndex].Cells[5].Value = DateTime.Now.ToShortDateString();


//SET BY CODE THE CHECK BOX
myDataGrid.Rows[e.RowIndex].Cells[2].Value = 1;
}
else //When you decheck
{
myDataGrid.Rows[e.RowIndex].Cells[5].Value = String.Empty;


//SET BY CODE THE CHECK BOX
myDataGrid.Rows[e.RowIndex].Cells[2].Value = 0;
}
}
}

I've tried some answers from here, but I've always had some kind of problem (like double clicking or using the keyboard). So, I combined some of them and got a consistent behavior (it's not perfect, but works properly).

void gridView_CellContentClick(object sender, DataGridViewCellEventArgs e) {
if(gridView.CurrentCell.GetType() != typeof(DataGridViewCheckBoxCell))
return;
if(!gridView.CurrentCell.IsInEditMode)
return;
if(!gridView.IsCurrentCellDirty)
return;
gridView.EndEdit();
}


void gridView_CellMouseUp(object sender, DataGridViewCellMouseEventArgs e) {
if(e.ColumnIndex == gridView.Columns["cFlag"].Index && e.RowIndex >= 0)
gridView.EndEdit();
}


void gridView_CellValueChanged(object sender, DataGridViewCellEventArgs e) {
if(e.ColumnIndex != gridView.Columns["cFlag"].Index || e.RowIndex < 0)
return;


// Do your stuff here.


}

Ben Voigt 在上面的回复中找到了最好的解决方案:

private void dgvStandingOrder_CurrentCellDirtyStateChanged(object sender, EventArgs e)
{
if (dgvStandingOrder.CurrentCell is DataGridViewCheckBoxCell)
dgvStandingOrder.CommitEdit(DataGridViewDataErrorContexts.Commit);
}

说真的,这就够了。

我使用带有 VirtualMode = true 的 DataGridView,只有这个选项对我有效 (当鼠标和空格键都工作时,包括重复的空格点击) :

private void doublesGridView_CurrentCellDirtyStateChanged(object sender, EventArgs e)
{
var data_grid = (DataGridView)sender;
      

if (data_grid.CurrentCell.IsInEditMode && data_grid.IsCurrentCellDirty) {
data_grid.EndEdit();
}
}


private void doublesGridView_CellContentClick(object sender, DataGridViewCellEventArgs e)
{
if (e.ColumnIndex == CHECKED_COLUMN_NUM && e.RowIndex >= 0 && e.RowIndex < view_objects.Count) { // view_objects - pseudocode
view_objects[e.RowIndex].marked = !view_objects[e.RowIndex].marked;        // Invert the state of the displayed object
}
}

this worked for me

  private void employeeDataGridView_CellEndEdit(object sender, DataGridViewCellEventArgs e)
{
if (e.ColumnIndex == employeeDataGridView.Columns["employeeStatusColumn"].Index)
{
bool isChecked = (bool)employeeDataGridView.CurrentCell.Value;
if (isChecked)
{
MessageBox.Show("Checked " + isChecked); //out true;
}
else
{
MessageBox.Show("unChecked " + isChecked);
}
}


}


private void employeeDataGridView_CellMouseUp(object sender, DataGridViewCellMouseEventArgs e)
{
if (employeeDataGridView.DataSource != null)
{
if (e.ColumnIndex == employeeDataGridView.Columns["employeeStatusColumn"].Index && e.RowIndex != -1)
{
employeeDataGridView.EndEdit();
}
}
}

我发现的最佳方法(也不使用多个事件)是处理 CurrentCellDirtyStateChanged事件。

private void dataGrid_CurrentCellDirtyStateChanged(object sender, EventArgs e)
{
if (dataGridMatten.CurrentCell.OwningColumn == dataGridMatten.Columns["checkBoxColumn"] && dataGridMatten.IsCurrentCellDirty)
{
dataGrid.CommitEdit(DataGridViewDataErrorContexts.Commit);


//your code goes here
}
}
private void dataGridViewPendingBill_CellContentClick(object sender, DataGridViewCellEventArgs e)
{
bool isChecked = (bool) dataGridViewPendingBill[e.ColumnIndex, e.RowIndex].EditedFormattedValue;


if (isChecked)
{
totalAmount += int.Parse(dataGridViewPendingBill.Rows[e.RowIndex].Cells["Amount"].Value.ToString());
textBoxAmount.Text = totalAmount.ToString();
}
else
{
totalAmount -= int.Parse(dataGridViewPendingBill.Rows[e.RowIndex].Cells["Amount"].Value.ToString());
textBoxAmount.Text = totalAmount.ToString();
}


dataGridViewPendingBill.EndEdit();
}