【问题标题】:How to force a "refresh" in a DataGridView如何在 DataGridView 中强制“刷新”
【发布时间】:2020-12-21 15:34:20
【问题描述】:

我是一个新手,正在做我的第一个 C# 项目(Haskell 和 C 方面的经验也很少),正在寻找有关如何在我的程序中实现小功能的指导。

我有一个 DataGridView 表(其中包含 3 列复选框)供用户填写。连续选中第二个复选框时,必须取消选中第一个选中的复选框。我已经可以做到这一点,但问题是,只有在我在表中选择其他内容后,第一个选中的才会取消选中。

这是与 CellValueChanged 事件有关的代码(cmets 中的内容是我试图帮助我的内容)

if (e.ColumnIndex == 0 || e.ColumnIndex == 1 || tabela_NormasDataGridView.Rows.Count == 0)
{
    return;
}

var isChecked = (bool)tabela_NormasDataGridView.Rows[e.RowIndex].Cells[e.ColumnIndex].Value;

if (isChecked)
{
    for (int i = 2; i < 5; i++)
    {
        //Console.WriteLine("og " + e.ColumnIndex);
        DataGridViewCell cell = tabela_NormasDataGridView.Rows[e.RowIndex].Cells[i];
        //Console.WriteLine("segunda " + cell.ColumnIndex);
        if (cell.ColumnIndex != e.ColumnIndex)
        {
            cell.Value = false;
            //this.Refresh();
        }
    }
}

【问题讨论】:

  • 对不起,如果我误解了这个问题。祝你有美好的一天。

标签: c# winforms datagridview


【解决方案1】:

尝试提交更改以强制刷新:

void tabela_NormasDataGridView_CurrentCellDirtyStateChanged(object sender, EventArgs e) {
    tabela_NormasDataGridView.CommitEdit(DataGridViewDataErrorContexts.Commit);
}

确保将事件连接起来。

【讨论】:

  • 对不起,我真的不知道如何为那个创建事件句柄
  • @FilipeAlmeida 你是如何为 CellValueChanged 创建的?
  • 从 CellContentClick 复制,只是更改名称 this.tabela_NormasDataGridView.CellValueChanged += new System.Windows.Forms.DataGridViewCellEventHandler(this.tabela_NormasDataGridView_CellValueChanged);
  • @FilipeAlmeida 在表单的构造函数中,添加tabela_NormasDataGridView.CurrentCellDirtyStateChanged += tabela_NormasDataGridView_CurrentCellDirtyStateChanged;,然后复制我上面的代码sn-p。
  • 很抱歉打扰您这么久,但我有一个问题:您知道为什么您给我的代码总是消失吗?如果是这样,有什么办法可以防止这种情况发生吗? (不确定,但我认为每次关闭和打开 VB 时都会发生)谢谢!
【解决方案2】:

在连接某些事件以执行某些操作时,谨慎行事是明智的。为什么特定事件不一定适合您希望代码在该事件中执行的操作,这可能不是很明显。

换句话说,代码可以运行,并且不会崩溃,但深入了解可能会发现代码正在执行您可能不打算执行的步骤。通常这可能是无害的,只会浪费 CPU 周期。而且还可能导致代码崩溃。

一个更微妙的问题可能会出现,其中一个网格事件会浪费大量 CPU 周期,从而导致使用该网格的应用程序变得迟缓。例子;如果网格被锚定到可调整大小的控件中并且用户调整了控件的大小。用户在重新绘制网格时可能会在调整大小时看到卡顿。

在您当前的代码中,当执行下面的行时...

cell.Value = false;

单元格的值将被更改。这似乎是无害的,但是此代码在网格的CellValueChanged 事件中......并且代码是“更改单元格值”。这将导致事件再次触发。在这种情况下,我们真的不想要这个。

LarsTech 的解决方案将起作用,我们将在下面的示例中使用它,但是应该注意,该解决方案的工作原理是它会在执行过程中创建额外的不必要的步骤。

关于可能的简单解决方案……

避免这种重新触发的一种可能解决方案是在代码将单元格值设置为false. 之前“关闭”网格的CellValueChanged 事件,然后在代码更改值后将其重新“打开”。

另一个使用不同网格事件的选项……

另一种可能(更好)的解决方案是更改代码所在的事件。如果您连接网格的 CellContentClick 事件,则可以做到这一点。

使用CellContnetClick 事件的关键优势在于它会在网格的CellValueChanged 事件被触发之前触发。这将允许代码“更改”单元格值,并且它们将立即更新,因为当代码更改值时,网格的 CellValueChanged 事件将“自动”触发。

一个完整的例子......

为了帮助可视化这一点,下面是一个示例。将两个DataGridViews 放到Form. 上。唯一需要做的更改是将顶部网格名称更改为tabela_NormasDataGridView。列被添加到代码中。

在下图中的左侧可以看到两个网格。
在此示例中,两个网格连接相同的事件。连接的事件是CellValueChangedCurrentCellDirtyStateChangedCellContentClick 事件。

在每个事件中添加Debug 语句以输出“何时”事件“进入”和“何时”事件“退出”(离开)。

Top 网格事件中有什么代码

  • CellValueChanged - 此事件包含我们的代码。
  • CurrentCellDirtyStateChanged - 此事件包含“提交”代码 编辑。
  • CellContentClick - 此事件不包含代码。

底部网格事件中有什么代码

  • CellValueChanged - 此事件不包含代码。
  • CurrentCellDirtyStateChanged - 此事件不包含代码。
  • CellContentClick - 此事件包含我们的代码。

复制,运行下面的代码,单击“Check2”列中的顶部网格复选框单元格。然后在底部网格中执行相同的操作。这应该会产生上面的图片。

跟踪顶部网格中的代码(黄色)...

  • 我们可以看到网格CurrentCellDirtyStateChanged 事件是 首先触发并且该事件中的代码将编辑提交到 网格。输入了……然而……

    ...我们可以看到它不会立即“离开”事件。至少 还没有。事实上,该事件被重新输入到列表的下方 调试语句。我们还可以看到这个事件被触发了两次。

  • 接下来,我们对网格CellValueChanged 事件进行了六 (6) 次调用。 最初,事件在完成之前重新进入(进入-进入)。

    调用网格CellValueChanged 事件六 (6) 次时我们只 想改变两个细胞,是“创造”不必要的工作。在 此外,这会打开代码无限循环的可能性。

  • 在对事件(我们的代码所在的位置)的六次调用之后,我们可以看到 重新输入CurrentCellDirtyStateChanged事件,然后代码 离开活动两次。

  • 最后,网格CellContentClick 被触发并立即离开 因为事件中没有代码。

这段代码似乎“创建”了许多对CellValueChanged 事件的不必要调用......这就是我们的代码所在的地方......嗯。

追踪底部网格中的代码(红色)……

  • 我们可以看到网格CurrentCellDirtyStateChanged事件是 首先触发并立即退出,因为该事件中没有代码。

  • CellContentClick 事件被触发并进入。这是我们的 代码是。

  • 在我们离开CellContnetClick 事件之前,网格的 CellValueChanged 事件被触发(进入/离开)两次,因为没有代码 那里。 当代码将两个单元格值设置为false.时,对网格的CellValueChanged 事件的两次调用来自我们的代码。

  • 最后,代码离开网格CellContentClick事件。

这看起来更干净,显然没有创建额外的步骤。

上图中使用的代码……

添加列和连接事件的初步准备工作。

private void Form1_Load(object sender, EventArgs e) {
  AddCheckBoxColumnsToGrid(tabela_NormasDataGridView);
  tabela_NormasDataGridView.CellValueChanged += new DataGridViewCellEventHandler(tabela_NormasDataGridView_CellValueChanged);
  tabela_NormasDataGridView.CurrentCellDirtyStateChanged += new EventHandler(tabela_NormasDataGridView_CurrentCellDirtyStateChanged);
  tabela_NormasDataGridView.CellContentClick += new DataGridViewCellEventHandler(tabela_NormasDataGridView_CellContentClick);

  AddCheckBoxColumnsToGrid(dataGridView2);
  dataGridView2.CellValueChanged += new DataGridViewCellEventHandler(dataGridView2_CellValueChanged);
  dataGridView2.CurrentCellDirtyStateChanged += new EventHandler(dataGridView2_CurrentCellDirtyStateChanged);
  dataGridView2.CellContentClick += new DataGridViewCellEventHandler(dataGridView2_CellContentClick);
}

private void AddCheckBoxColumnsToGrid(DataGridView dgv) {
  DataGridViewTextBoxColumn txtCol = new DataGridViewTextBoxColumn();
  txtCol.Name = "col0";
  dgv.Columns.Add(txtCol);
  txtCol = new DataGridViewTextBoxColumn();
  txtCol.Name = "col0";
  dgv.Columns.Add(txtCol);
  DataGridViewCheckBoxColumn col = new DataGridViewCheckBoxColumn();
  col.Name = "Check1";
  dgv.Columns.Add(col);
  col = new DataGridViewCheckBoxColumn();
  col.Name = "Check2";
  dgv.Columns.Add(col);
  col = new DataGridViewCheckBoxColumn();
  col.Name = "Check3";
  dgv.Columns.Add(col);
  dgv.Rows.Add(4);
}

顶部网格的事件…

private void tabela_NormasDataGridView_CellValueChanged(object sender, DataGridViewCellEventArgs e) {
  Debug.WriteLine("DGV1 - Cell Value Changed -> enter");
  if (e.ColumnIndex == 0 || e.ColumnIndex == 1 || tabela_NormasDataGridView.Rows.Count == 0) {
    return;
  }
  var isChecked = (bool)tabela_NormasDataGridView.Rows[e.RowIndex].Cells[e.ColumnIndex].Value;
  if (isChecked) {
    for (int i = 2; i < 5; i++) {
      DataGridViewCell cell = tabela_NormasDataGridView.Rows[e.RowIndex].Cells[i];
      if (cell.ColumnIndex != e.ColumnIndex) {
        cell.Value = false;
      }
    }
  }
  Debug.WriteLine("DGV1 - Cell Value Changed -> leave");
}

private void tabela_NormasDataGridView_CurrentCellDirtyStateChanged(object sender, EventArgs e) {
  Debug.WriteLine("DGV1 - Current Cell Dirty Cell State Changed -> enter");
  tabela_NormasDataGridView.CommitEdit(DataGridViewDataErrorContexts.Commit);
  Debug.WriteLine("DGV1 - Current Cell Dirty Cell State Changed -> leave");
}

private void tabela_NormasDataGridView_CellContentClick(object sender, DataGridViewCellEventArgs e) {
  Debug.WriteLine("DGV1 - CellContentClick -> enter");
  // Do nothing
  Debug.WriteLine("DGV1 - CellContentClick -> leave");
}

底部网格的事件…

private void dataGridView2_CellValueChanged(object sender, DataGridViewCellEventArgs e) {
  Debug.WriteLine("DGV2 - Cell Value Changed -> enter");
  // do nothing
  Debug.WriteLine("DGV2 - Cell Value Changed -> leave");
}

private void dataGridView2_CurrentCellDirtyStateChanged(object sender, EventArgs e) {
  Debug.WriteLine("DGV2 - Current Cell Dirty Cell State Changed -> enter");
  // do nothing
  //dataGridView1.CommitEdit(DataGridViewDataErrorContexts.Commit);
  Debug.WriteLine("DGV2 - Current Cell Dirty Cell State Changed -> leave");
}

private void dataGridView2_CellContentClick(object sender, DataGridViewCellEventArgs e) {
  Debug.WriteLine("DGV2 - Cell Content Click -> enter");
  string colName = dataGridView2.Columns[e.ColumnIndex].Name;
  if (colName == "Check1" || colName == "Check2" || colName == "Check3") {
    int rowIndex = e.RowIndex;
    switch (colName) {
      case "Check1":
        dataGridView2.Rows[rowIndex].Cells["Check2"].Value = false;
        dataGridView2.Rows[rowIndex].Cells["Check3"].Value = false;
        break;
      case "Check2":
        dataGridView2.Rows[rowIndex].Cells["Check1"].Value = false;
        dataGridView2.Rows[rowIndex].Cells["Check3"].Value = false;
        break;
      case "Check3":
        dataGridView2.Rows[rowIndex].Cells["Check1"].Value = false;
        dataGridView2.Rows[rowIndex].Cells["Check2"].Value = false;
        break;
    }
  }
  Debug.WriteLine("DGV2 - Cell Content Click -> leave");
}

我希望这是有道理的。

【讨论】:

  • 只有一个复选框是可以接受的。感谢您的意见,我马上试试 :)
  • 所以我检查了一下,代码对于添加的列非常有效!但是我如何只适应我的呢?我刚刚在我的表单加载中删除了与你的 3 列相关的所有内容,但我应该在那里留下一些东西吗?
  • 是的,Load event 形式的代码是为了完成示例,在您的代码中是不必要的。 CellContentClick 事件中的代码检查单击的单元格是否是复选框列之一... Check1、Check2 或 Check3。在这里,您需要将我的代码中的名称更改为代码中复选框列名称的名称。然后,如果单击的单元格是这些列之一,则使用 switch 语句来确定要设置为 false 的“哪个”复选框列。
  • 因此,在 switch/case 代码部分,您需要更改 case 语句的名称以匹配您的复选框列名称。基本上,将我代码中的列名 Check1、Check2 和 Check3 更改为代码中复选框列的名称。
  • 我问是因为我完全按照你说的做了(删除负责在表单加载中创建 Check1、Check2、Check3 列的代码,并将所有剩余的名称更改为我的版本),但它不起作用我应该删除我最初在这里发布的代码正确吗?
猜你喜欢
  • 2010-11-24
  • 2010-10-15
  • 1970-01-01
  • 2015-08-09
  • 2012-12-15
  • 1970-01-01
  • 1970-01-01
  • 2015-06-25
相关资源
最近更新 更多