如何检测DataGridView CheckBox事件更改?


90

我有一个winforms应用程序,并希望在DataGridView选中/取消选中控件中嵌入的复选框时触发一些代码。我尝试过的每件事

  1. CheckBox单击时立即触发,但在其检查状态更改之前触发,或
  2. 仅在CheckBox失去焦点时触发

我似乎找不到在检查状态更改后立即触发的事件。


编辑:

我想要实现的是,当一个CheckBox中的a的检查状态DataGridView发生变化时,另外两个中的数据DataGridView发生变化。但是我使用过的所有事件,其他网格中的数据仅CheckBox在第一个网格之后DataGridView失去焦点。


2
您检查CurrentCellDirtyStateChanged活动了吗?
Yograj Gupta 2012年

仍然仅在用户“离开”单元格时执行。
PJW 2012年

1
这是MSDN上的文章:msdn.microsoft.com/zh-cn/library/…与Killercam的答案类似但略有不同
David Hall

Answers:


96

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

我希望这有帮助。

PS检查本文https://msdn.microsoft.com/zh-cn/library/system.windows.forms.datagridview.currentcelldirtystatechanged(v=vs.110).aspx


5
这是一个很好的解决方案,但没有工作,如果用户点击了几次,替代已经贴下面stackoverflow.com/questions/11843488/...
56ka

1
我也强烈建议不要对双击问题使用此解决方案。需要调用EndEdit()函数...从@ 56ka找到链接,然后单击文章链接!
路加福音

1
我没有花很多时间在这个解决方案上,如果@ 56ka的解决方案是更好的解决方案,那就太好了。但是,我不确定双击aDataGridViewCheckBox有什么大惊小怪的。这不是WPF,双击控件不会破坏任何数据绑定,它是WinForms。双击可能无法直观地更新控件,但不会破坏任何内容,在这种情况下,也许以下解决方案是更好的解决方案。谢谢。
2014年

如果从添加相同的代码,这工作完全CellContentClick进入CellContentDoubleClick为好。CellMouseUp即使已选中该单元格但未单击复选框,它也会触发-这是不希望的行为。
to食

89

我发现@Killercam的解决方案可以正常工作,但是如果用户双击得太快,那会有点令人困惑。不知道其他人是否也是如此。我在这里找到了另一个解决方案。

它使用datagrid的CellValueChangedCellMouseUp。长虹解释说

“原因是OnCellvalueChanged事件在DataGridView认为您已完成编辑之前不会触发。这对于TextBox列是有意义的,因为OnCellvalueChanged不会为每次击键触发[bother],但不会[有意义]。

这是他的例子的作用:

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

3
我遇到了响应者指出的双击问题,在正确处理该问题方面,该问题比第一个解决方案要好得多。
史蒂夫·弗格森

1
我也遇到了双击问题,此解决方案已解决。
克里斯C

单击“此处”按钮,然后查看文章。双击我也遇到了同样的问题。
路加福音

4
如果使用空格键切换切换框怎么办?
Halfgaar 2014年

1
要“修复”空格键问题,我KeyPreview在表单上将其设置为true,而在e.KeyCode == Keys.Spaceset时将其设置为true e.Handled = true。换句话说,我只是禁用了键盘编辑。
Halfgaar 2014年

9

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

这是我的代码(其中一部分是从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
好的答案,但是最好使用CellContentClick代替,CellMouseUp因为当用户单击单元格内的任何位置时将调用后者,而仅当单击复选框时才调用前者。
Jamie Kitson

6

这也可以处理键盘激活。

    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

这是一些代码:

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
该答案包含正确的答案,该答案可以处理鼠标和键盘的交互以及重复的交互,而无需离开单元格。但是只需要最后一个处理程序-CommitEdit从调用CurrentCellDirtyStateChanged是整个解决方案。
Ben Voigt '18

4

按照Killercam'answer,我的代码

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

2

这与编辑单元格有关,问题在于该单元格实际上并未编辑,因此您需要保存该单元格或行的更改,以在单击复选框时获取事件,以便可以使用此功能:

datagridview.CommitEdit(DataGridViewDataErrorContexts.CurrentCellChange)

这样,即使发生其他事件,也可以使用它。


2

我找到了这个问题的简单答案。我只是使用反向逻辑。该代码在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

最好的事情之一就是不需要多个事件。


1

对我有用的是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();
}

1

该代码将在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;
        }
    }
}

1

在CellContentClick事件中,您可以使用以下策略:

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

我从这里尝试了一些答案,但是我总是遇到某种问题(例如双击或使用键盘)。因此,我将其中的一些结合在一起,并获得了一致的行为(这并不完美,但可以正常工作)。

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

为此,在使用devexpress xtragrid时,必须按此处所述处理相应存储库项目的EditValueChanged事件。调用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);
                    }
                }
            }
        }

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


0

在单元格值更改后移出焦点将允许值在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);
    }

}

0

单击复选框然后捕获CellValueChanged事件,可以强制该单元格提交值。该CurrentCellDirtyStateChanged作为点击复选框立即触发。

以下代码对我有用:

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

然后,您可以将代码插入CellValueChanged事件中。


0

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

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

说真的,这就是您所需要的。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.