C# CellValueChanged期间winform datagridview更新值

C# CellValueChanged期间winform datagridview更新值,c#,winforms,datagridview,C#,Winforms,Datagridview,我正在构建这样一个应用程序: 在表中的每一行中: 用户可以在选项1和选项2列中选中或取消选中值。 之后,All option列中复选框的值将更新为: -“选中”:如果选中了选项1和选项2, -“未选中”:如果选项1和选项2都未选中 -“不确定”:其他情况 用户可以选中或取消选中所有选项列中的值。 之后,选项1和选项2的值将根据所有选项的当前值更新 在这种情况下,用户无法将All option列的值更改为不确定(仅在选中和未选中之间切换) 我正在Winforms中使用DataGridVi

我正在构建这样一个应用程序:

在表中的每一行中:

  • 用户可以在
    选项1
    选项2
    列中选中或取消选中值。
    之后,
    All option
    列中复选框的值将更新为:
    -“选中”:如果选中了
    选项1
    选项2

    -“未选中”:如果
    选项1
    选项2
    都未选中
    -“不确定”:其他情况

  • 用户可以选中或取消选中
    所有选项
    列中的值。
    之后,
    选项1
    选项2
    的值将根据
    所有选项的当前值更新
    在这种情况下,用户无法将
    All option
    列的值更改为不确定(仅在选中和未选中之间切换)

我正在Winforms中使用DataGridView实现上述应用程序。以下是我的代码:

public partial class Form1 : Form
    {
        DataTable dataTable;

        public Form1()
        {
            InitializeComponent();

            dataTable = new DataTable();
            dataTable.Columns.Add("Item");
            dataTable.Columns.Add("All option", typeof(CheckState));
            dataTable.Columns.Add("Option 1", typeof(bool));
            dataTable.Columns.Add("Option 2", typeof(bool));
            dataTable.Rows.Add("Item 1", CheckState.Unchecked, false, false);
            dataTable.Rows.Add("Item 2", CheckState.Unchecked, false, false);
            dataGrid.DataSource = dataTable;

        }

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

        bool isUnderUpdateAll = false;

        private void dataGrid_CellValueChanged(object sender, DataGridViewCellEventArgs e)
        {
            if ((dataGrid.CurrentCell is DataGridViewCheckBoxCell) == false) return;

            // This is column "All option"
            if (e.ColumnIndex == 1)
            {
                // Avoid stackoverflow exception
                if (isUnderUpdateAll) return;

                CheckState allOption = (CheckState)dataGrid["All option", e.RowIndex].Value;
                
                // Don't allow user select 'indeterminate' value
                if (allOption == CheckState.Indeterminate)
                {
                    allOption = CheckState.Unchecked;

                    // These code didn't work
                    dataTable.Rows[e.RowIndex]["All option"] = CheckState.Unchecked;
                    dataGrid["All option", e.RowIndex].Value = CheckState.Unchecked;
                    dataGrid.UpdateCellValue(e.ColumnIndex, e.RowIndex);
                    dataGrid.NotifyCurrentCellDirty(true);
                    dataGrid.Refresh();
                    dataGrid.Update();

                }

                dataGrid["Option 1", e.RowIndex].Value = allOption;
                dataGrid["Option 2", e.RowIndex].Value = allOption;
            }

            // This is column "Option 1" or "Option 2"
            else
            {
                bool option1 = (bool)dataGrid["Option 1", e.RowIndex].Value;
                bool option2 = (bool)dataGrid["Option 2", e.RowIndex].Value;

                isUnderUpdateAll = true;
                dataGrid["All option", e.RowIndex].Value = (option1 && option2) ? CheckState.Checked 
                    : (!option1 && !option2 ? CheckState.Unchecked : CheckState.Indeterminate );
                isUnderUpdateAll = false;
            }
        }
    }
代码似乎有效,但有一点不完整:用户仍然可以在
All选项
列中切换到不确定状态。在我的代码中,我已经添加了许多内容,如:

// Don't allow user select 'indeterminate' value
if (allOption == CheckState.Indeterminate)
{
    allOption = CheckState.Unchecked;

    // These code didn't work
    dataTable.Rows[e.RowIndex]["All option"] = CheckState.Unchecked;
    dataGrid["All option", e.RowIndex].Value = CheckState.Unchecked;
    dataGrid.UpdateCellValue(e.ColumnIndex, e.RowIndex);
    dataGrid.NotifyCurrentCellDirty(true);
    dataGrid.Refresh();
    dataGrid.Update();

}
但是这些代码不起作用。当点击
All选项
复选框时,状态仍然在Checked->undeterminate->Unchecked->Checked->……
那么,有人能建议如何从上面删除不确定状态吗?
我希望它是:Checked->Unchecked->Checked->Unchecked…

尝试将设置为false

((DataGridViewCheckBoxCell)(dataGrid.CurrentCell)).ThreeState = false;

我不确定是否同意@Kyle Wang的解决方案。当您将当前单元格的
ThreeState
设置为
false
时,基本上不允许出现“不确定”状态。这是用户单击“所有选项”复选框时所需的行为,但这不是更改“选项1”或“选项2”复选框时所需的行为

假设用户单击“所有选项”复选框并将其值更改为
true
。此时,单元格“三状态”设置为
false
,因此
不确定的
状态不是选项。然后,用户更改“选项1”或“选项2”复选框,其中一个为
true
,另一个为
false
。然后,代码必须将“All option”单元格的
ThreeState
属性更改回
true
,以便根据您的要求将其设置为
不确定的
状态。我可以看到一个噩梦,跟踪每个单元格的状态“所有选项”单元格

此外,您可能需要注意“哪个”您选择执行某些操作的事件。例如,代码似乎正在连接两个网格事件:
CurrentCellDirtyStateChanged
CellValueChanged
。这是完全有效的,可以工作,但它可能有助于查看这些事件触发的频率。我没有检查这一点,因为我确信,如果您显示了多少次当事件触发时,您会发现它们触发的次数比您预期的要多。在下面的示例中,在测试事件时,可以直观地看到事件触发的次数。我建议您在当前代码中尝试此方法

考虑到这一点,显然“最佳”事件是grids
CellValueChanged
事件。不幸的是,该事件在用户“离开”之前不会触发单元格。通过一个复选框,我们希望在用户单击复选框时立即触发事件。一个可能有帮助的事件是grids
CellContentClick
事件。此事件将在用户单击“复选框”时立即触发。请记住,如果用户单击复选框“cell”,而不是“复选框”本身

使用此事件的一个问题是它何时触发。它是在网格中的复选框“值”实际更改之前触发的。幸运的是,因为我们知道它是一个复选框单元格,所以我们可以继续“提交”网格更改。然后我们将获得单元格的实际最终值

如果您创建一个新的“winform”项目,将一个
DataGridView
和一个多行
TextBox
放到表单上(如下所示),您应该能够测试下面的代码

我将所有初始化代码移到forms
Load
事件中,但是除了添加两个不同的行之外,代码是相同的

遍历网格
CellContentClick
event:代码将文本添加到文本框中,以显示事件已触发(如前所述)。然后代码“提交”单元格中的更改。然后使用
if
语句检查更改的单元格是“全部选项”列还是“选项”列之一s列。如果更改的单元格位于“所有选项”列中,则变量
allOption
被设置为“所有选项”单元格的
CheckState
值,选中、未选中或不确定

接下来,在每个状态的
allOption
变量上执行
switch
语句,选中、取消选中或不确定。如果状态更改为
true
false
,则代码只是将两个“选项”更改为相同的状态。显然,无需更改“All option”状态

private void Form1_Load(object sender, EventArgs e) {
  dataTable = new DataTable();
  dataTable.Columns.Add("Item");
  dataTable.Columns.Add("All option", typeof(CheckState));
  dataTable.Columns.Add("Option 1", typeof(bool));
  dataTable.Columns.Add("Option 2", typeof(bool));
  dataTable.Rows.Add("Item 1", CheckState.Unchecked, false, false);
  dataTable.Rows.Add("Item 2", CheckState.Checked, true, true);
  dataTable.Rows.Add("Item 3", CheckState.Indeterminate, false, true);
  dataTable.Rows.Add("Item 4", CheckState.Indeterminate, true, false);
  dataGrid.DataSource = dataTable;
}

private void dataGrid_CellContentClick(object sender, DataGridViewCellEventArgs e) {
  textBox1.Text += "CellContentClick" + Environment.NewLine;
  dataGrid.CommitEdit(DataGridViewDataErrorContexts.Commit);
  if (dataGrid.Columns[e.ColumnIndex].Name == "All option") {
    var allOption = (CheckState)dataGrid["All option", e.RowIndex].Value;
    switch (allOption) {
      case CheckState.Unchecked:
        dataGrid["Option 1", e.RowIndex].Value = CheckState.Unchecked;
        dataGrid["Option 2", e.RowIndex].Value = CheckState.Unchecked;
        break;
      case CheckState.Checked:
        dataGrid["Option 1", e.RowIndex].Value = CheckState.Checked;
        dataGrid["Option 2", e.RowIndex].Value = CheckState.Checked;
        break;
      case CheckState.Indeterminate:
        dataGrid["Option 1", e.RowIndex].Value = CheckState.Unchecked;
        dataGrid["Option 2", e.RowIndex].Value = CheckState.Unchecked;
        dataGrid["All option", e.RowIndex].Value = CheckState.Unchecked;
        // we changed the current All option cell so we need to refresh the edit
        dataGrid.RefreshEdit();
        break;
    }
  }
  else {
    if (dataGrid.Columns[e.ColumnIndex].Name == "Option 1" ||
        dataGrid.Columns[e.ColumnIndex].Name == "Option 2") {
      var op1Checked = (bool)dataGrid["Option 1", e.RowIndex].Value;
      var op2Checked = (bool)dataGrid["Option 2", e.RowIndex].Value;
      if (op1Checked && op2Checked) {
        dataGrid["All option", e.RowIndex].Value = CheckState.Checked;
      }
      else {
        if (!op1Checked && !op2Checked) {
          dataGrid["All option", e.RowIndex].Value = CheckState.Unchecked;
        }
        else {
          dataGrid["All option", e.RowIndex].Value = CheckState.Indeterminate;
        }
      }
    }
  }
}
如果“所有选项”单元格的当前状态为不确定,则表示一个选项为真,另一个为假。此“不确定”状态是由另一个选项复选框设置的。哪个单元格是
true
false
,与此无关,代码只是将所有状态更改为unchecked。注意:由于我们正在更改代码中的“all option”单元格,并且我们知道该单元格已提交到
不确定的状态(我们不希望这样做),我们需要刷新中的编辑