How to detect DataGridView CheckBox event change?

Viewed 166839

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.

20 Answers

Ben Voigt found the best solution in a comment-reply above:

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

Seriously, that's ALL you need.

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.

}

The best way that I found (which also doesn't use multiple events) is by handling the CurrentCellDirtyStateChanged event.

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
        }
    }

Removing the focus after the cell value changes allow the values to update in the DataGridView. Remove the focus by setting the CurrentCell to 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);
    }

}

You can force the cell to commit the value as soon as you click the checkbox and then catch the CellValueChanged event. The CurrentCellDirtyStateChanged fires as soon as you click the checkbox.

The following code works for me:

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

You can then insert your code in the CellValueChanged event.

I use DataGridView with VirtualMode=true and only this option worked for me (when both the mouse and the space bar are working, including repeated space clicks):

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();
                }
            }
        }
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();
        }
Related