在连接某些事件以执行某些操作时,谨慎行事是明智的。为什么特定事件不一定适合您希望代码在该事件中执行的操作,这可能不是很明显。
换句话说,代码可以运行,并且不会崩溃,但深入了解可能会发现代码正在执行您可能不打算执行的步骤。通常这可能是无害的,只会浪费 CPU 周期。而且还可能导致代码崩溃。
一个更微妙的问题可能会出现,其中一个网格事件会浪费大量 CPU 周期,从而导致使用该网格的应用程序变得迟缓。例子;如果网格被锚定到可调整大小的控件中并且用户调整了控件的大小。用户在重新绘制网格时可能会在调整大小时看到卡顿。
在您当前的代码中,当执行下面的行时...
cell.Value = false;
单元格的值将被更改。这似乎是无害的,但是此代码在网格的CellValueChanged 事件中......并且代码是“更改单元格值”。这将导致事件再次触发。在这种情况下,我们真的不想要这个。
LarsTech 的解决方案将起作用,我们将在下面的示例中使用它,但是应该注意,该解决方案的工作原理是它会在执行过程中创建额外的不必要的步骤。
关于可能的简单解决方案……
避免这种重新触发的一种可能解决方案是在代码将单元格值设置为false. 之前“关闭”网格的CellValueChanged 事件,然后在代码更改值后将其重新“打开”。
另一个使用不同网格事件的选项……
另一种可能(更好)的解决方案是更改代码所在的事件。如果您连接网格的 CellContentClick 事件,则可以做到这一点。
使用CellContnetClick 事件的关键优势在于它会在网格的CellValueChanged 事件被触发之前触发。这将允许代码“更改”单元格值,并且它们将立即更新,因为当代码更改值时,网格的 CellValueChanged 事件将“自动”触发。
一个完整的例子......
为了帮助可视化这一点,下面是一个示例。将两个DataGridViews 放到Form. 上。唯一需要做的更改是将顶部网格名称更改为tabela_NormasDataGridView。列被添加到代码中。
在下图中的左侧可以看到两个网格。
在此示例中,两个网格连接相同的事件。连接的事件是CellValueChanged、CurrentCellDirtyStateChanged 和CellContentClick 事件。
在每个事件中添加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");
}
我希望这是有道理的。