【问题标题】:How to detect DataGridView CheckBox event change?如何检测 DataGridView CheckBox 事件变化?
【发布时间】:2012-08-07 09:59:36
【问题描述】:

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

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

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


编辑:

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

【问题讨论】:

  • 你检查CurrentCellDirtyStateChanged事件了吗?
  • 仍然只在用户“离开”单元格时执行。
  • 这是 MSDN 上的文章:msdn.microsoft.com/en-us/library/… 与 Killercam 的回答类似但略有不同
  • 您可能需要考虑将ListViewlistView.View = System.Windows.Forms.View.Details; listView.CheckBoxes = true; 一起使用,然后它会显示为一个表格,开头带有复选框。在那里,您可以使用 listView.ItemCheck += new System.Windows.Forms.ItemCheckEventHandler(methodname); 轻松解决 Checkbox 选中事件

标签: c# winforms datagridview


【解决方案1】:

要处理DatGridViews CheckedChanged 事件,您必须首先触发CellContentClick(它没有CheckBoxes 当前状态!)然后调用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();
}

我希望这会有所帮助。

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

【讨论】:

  • 这是一个很好的解决方案,但如果用户点击几次就不起作用,在stackoverflow.com/questions/11843488/…下方发布了替代方案
  • 我也强烈建议不要将此解决方案用于双击问题。需要调用EndEdit()函数……从@56ka找到链接,点击文章链接!
  • 我并没有在这个解决方案上花费很长时间,如果@56ka 的解决方案更好,那就太好了。但是,我不确定双击DataGridViewCheckBox 有什么大惊小怪的。这不是 WPF,双击控件不会破坏任何数据绑定,它是 WinForms。双击可能不会在视觉上更新控件,但它不会破坏任何东西,在这种情况下,也许下面的解决方案是更好的解决方案。谢谢。
  • 如果您将相同的代码从 CellContentClick 添加到 CellContentDoubleClick 中,这将非常有效。 CellMouseUp 即使选中了单元格但未单击复选框,也会触发 - 这是不希望的行为。
  • @torpidprey 或者只是为CellContentDoubleClick 设置CellContentClick 事件处理程序而不是复制,让这两个处理程序触发一个处理程序。您的解决方案修复了我的 winform 应用程序中的双击问题。
【解决方案2】:

我发现@Killercam 的解决方案可以工作,但如果用户双击速度太快,就会有点狡猾。不确定其他人是否也发现了这种情况。我找到了另一个解决方案here

它使用数据网格的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();
    }
}

【讨论】:

  • 我遇到了响应者提到的双击问题,这个问题比第一个解决方案更有效。
  • 我也遇到了双击问题,这个解决方案解决了。
  • 点击“这里”按钮并查看文章。我在双击时遇到了同样的问题。
  • 如果用空格键切换开关会怎样?
  • 为了“修复”空格键问题,我在表单上将KeyPreview 设置为true,当e.KeyCode == Keys.Space 时,设置e.Handled = true。换句话说,我只是禁用了键盘编辑。
【解决方案3】:

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

【讨论】:

  • 好答案,但最好使用CellContentClick 而不是CellMouseUp,因为后者将在用户单击单元格内的任何位置时调用,而前者仅在单击复选框时调用.
【解决方案4】:

按照 Killercam 的回答,我的代码

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

【讨论】:

    【解决方案5】:

    这也处理键盘激活。

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

    【讨论】:

      【解决方案6】:

      这里有一些代码:

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

      【讨论】:

      • 这个答案包含正确的答案,它处理鼠标和键盘交互,以及重复交互而不离开单元格。但只需要最后一个处理程序——从CurrentCellDirtyStateChanged 调用CommitEdit 是整个解决方案。
      【解决方案7】:

      都是编辑单元格,问题是单元格实际上没有编辑,所以你需要保存单元格或行的变化来获取点击复选框时的事件,这样你就可以使用功能:

      datagridview.CommitEdit(DataGridViewDataErrorContexts.CurrentCellChange)
      

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

      【讨论】:

        【解决方案8】:

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

        最好的一点是不需要多个事件。

        【讨论】:

          【解决方案9】:

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

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

          【讨论】:

            【解决方案10】:

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

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

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

            【讨论】:

              【解决方案11】:

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

              【讨论】:

                【解决方案12】:

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

                【讨论】:

                  【解决方案13】:

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

                  【讨论】:

                    【解决方案14】:

                    我发现的最好方法(也不使用多个事件)是处理 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
                            }
                        }
                    

                    【讨论】:

                    • 这对我来说是醒的
                    【解决方案15】:

                    要在使用 devexpress xtragrid 时执行此操作,必须按照 here 的描述处理相应存储库项的 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 循环来遍历行。

                    【讨论】:

                      【解决方案16】:

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

                      【讨论】:

                        【解决方案17】:

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

                        以下代码适用于我:

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

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

                        【讨论】:

                          【解决方案18】:

                          我将 DataGridView 与 VirtualMode=true 一起使用,只有这个选项对我有用 (当鼠标和空格键都在工作时,包括重复的空格键):

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

                          【讨论】:

                            【解决方案19】:

                            这对我有用

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

                            【讨论】:

                            • rawaha 欢迎来到 Stack Overflow!如果您可以添加对您的代码如何工作的描述,并可能解释这与该线程上提供的其他解决方案有何不同或更好,这将很有帮助。
                            猜你喜欢
                            • 1970-01-01
                            • 2012-11-01
                            • 1970-01-01
                            • 1970-01-01
                            • 2017-08-02
                            • 2018-10-01
                            • 1970-01-01
                            相关资源
                            最近更新 更多