Как обнаружить изменение события DataGridView CheckBox?


92

У меня есть приложение winforms, и я хочу запустить какой-то код, когда установлен или снят флажок, встроенный в элемент DataGridViewуправления. Каждое мероприятие, которое я пробовал

  1. Срабатывает, как только CheckBoxщелкают, но до того, как его отмеченное состояние изменится, или
  2. Срабатывает только после того, как CheckBoxтеряет фокус

Кажется, я не могу найти событие, которое срабатывает сразу после изменения отмеченного состояния.


Редактировать:

Я пытаюсь добиться того, чтобы при изменении отмеченного состояния CheckBoxв одном DataGridViewизменяются данные в двух других DataGridView. Тем не менее, все события, которые я использовал, данные в других сетках меняются только после того, CheckBoxкак первая DataGridViewтеряет фокус.


2
Вы проверили CurrentCellDirtyStateChangedсобытие?
Йоградж Гупта 07

По-прежнему выполняется только тогда, когда пользователь «покидает» ячейку.
PJW,

1
Вот статья MSDN по этому поводу : msdn.microsoft.com/en-us/library/… похоже, но немного отличается от ответа Killercam
Дэвид Холл,

Ответы:


99

Чтобы обработать событие DatGridViews, CheckedChangedвы должны сначала запустить CellContentClickего (которое не имеет CheckBoxтекущего состояния es!), А затем вызвать CommitEdit. Это, в свою очередь, запустит CellValueChangedсобытие, которое вы можете использовать для своей работы. Это недосмотр Microsoft . Сделайте что-нибудь вроде следующего ...

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

Надеюсь, это поможет.

PS Проверьте эту статью https://msdn.microsoft.com/en-us/library/system.windows.forms.datagridview.currentcelldirtystatechanged(v=vs.110).aspx


5
Это хорошее решение, но оно не работает, если пользователь нажимает несколько раз, альтернатива была опубликована ниже stackoverflow.com/questions/11843488/…
56ka

1
Я также настоятельно рекомендую НЕ использовать это решение для проблемы двойного щелчка. Необходимо вызвать функцию EndEdit () ... найдите ссылку на @ 56ka и щелкните ссылку на статью!
Люк

1
Я не тратил много времени на это решение, и если решение @ 56ka лучше, отлично. Однако я не уверен, в чем DataGridViewCheckBoxзаключается суета о двойном щелчке по a . Это не WPF, и двойной щелчок по элементу управления не нарушает привязку данных, это WinForms. Двойной щелчок может не обновить элемент управления визуально, но он ничего не сломает, и в этом случае, возможно, решение, приведенное ниже, является лучшим. Благодарю.
MoonKnight

Это отлично работает, если вы добавите тот же код из CellContentClickв CellContentDoubleClick. CellMouseUpis будет срабатывать, даже если ячейка выбрана, но флажок не установлен, что является нежелательным поведением.
Torpid Prey

89

Я обнаружил, что решение @Killercam работает, но было немного хитро, если пользователь дважды щелкал слишком быстро. Не уверен, что и другие нашли это так. Я нашел другое решение здесь .

Он использует сетку данных CellValueChangedи CellMouseUp. Чанхун объясняет, что

"Причина этого в том, что событие 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. Эту логику необходимо добавить в событие CellDoubleClick в дополнение к событию MouseUp:

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

3
I ran into the double-click problem noted by the responder, and this one worked much better than the first solution in handling that correctly.
Steve Ferguson

1
I also ran into the double-click problem and this solution fixed it.
Chris C

Click the 'here' button and check out the article. I had the same issue with the double click.
Luke

4
What if you toggle the toggle with the space bar?
Halfgaar

1
To 'fix' the spacebar issue, I set KeyPreview to true on the form and when e.KeyCode == Keys.Space, set e.Handled = true. In other words, I just disabled keyboard editing.
Halfgaar

9

jsturtevants's solution worked great. However, I opted to do the processing in the EndEdit event. I prefer this approach (in my application) because, unlike the CellValueChanged event, the EndEdit event does not fire while you are populating the grid.

Here is my code (part of which is stolen from jsturtevant:

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

3
Good answer, but it is preferable to use CellContentClick instead of CellMouseUp because the latter will be called when the user clicks anywhere inside the cell whereas the former is only called when the checkbox is clicked.
Jamie Kitson

6

This also handles the keyboard activation.

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

5

following Killercam'answer, My code

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

and :

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

5

Here is some code:

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

2
This answer contains the correct answer, that handles both mouse and keyboard interactions, and repeated interactions without leaving the cell. But only the last handler is needed -- calling CommitEdit from CurrentCellDirtyStateChanged is the whole solution.
Ben Voigt

2

It's all about editing the cell, the problem that is the cell didn't edited actually, so you need to save The changes of the cell or the row to get the event when you click the check box so you can use this function:

datagridview.CommitEdit(DataGridViewDataErrorContexts.CurrentCellChange)

with this you can use it even with a different event.


2

I have found a simpler answer to this problem. I simply use reverse logic. The code is in VB but it is not much different than 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

One of the best things about this is no need for multiple events.


1

What worked for me was CurrentCellDirtyStateChanged in combination with datagridView1.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();
}

1

The Code will loop in DataGridView and Will check if CheckBox Column is Checked

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

1

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

1

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.

}

0

To do this when using the devexpress xtragrid, it is necessary to handle the EditValueChanged event of a corresponding repository item as described here. It is also important to call the gridView1.PostEditor() method to ensure the changed value has been posted. Here is an implementation:

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

Note that because the xtragrid doesnt provide an enumerator it is necessary to use a for loop to iterate over rows.


0

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

}

0

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.


0

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.


0

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
   }
}  
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.