DataGridView 事件序列
一个有用的工具,可以帮助您在导航 DataGridView 时理解 DataGridView 事件。
引言
DataGridView
控件在用户浏览网格时会生成许多事件。有单元格进入/退出、行进入/退出、单元格和行验证事件等。要最好地利用这些事件,您需要了解它们何时发生,以及当您以不同方式响应这些事件时会发生什么。本文提供
- 一个包含网格和事件日志的应用程序。通过编辑网格,您将看到生成的事件日志。
- 有限状态机图,描述了
EditOnKeystroke
编辑模式的事件序列。
还提供了按钮,允许您影响事件处理程序的行为。例如,当单元格验证失败时,可能会生成不同的事件序列。只需单击一下即可更改这些验证响应。
可执行文件已包含在下载文件中,因此可以轻松尝试并查看 DataGridView
正在触发哪些事件。
我没有支持每个网格事件,如果日志中充斥着所有事件,日志的用处就会减少,但我包含了主要的导航和验证事件。添加新处理程序非常简单,下面将进行说明,如果您需要的话。
让我们先看看应用程序,然后我将更详细地描述事件并介绍状态机。
使用应用程序
该应用程序包含一个事件日志、一个网格和一个工具栏。一运行,网格事件就会触发,并在日志中显示这些事件。单击网格中的任何单元格,日志中将添加更多事件。工具栏按钮可用于模拟验证事件处理程序提供的响应。
工具栏按钮
- 单元格失败 - 单击时,将启用“单元格失败”状态。每当处理
OnCellValidating
事件时,处理程序将设置 cancel 标志以指示验证失败。再次单击按钮以正常处理事件。 - 行失败 - 单击时,将启用“行失败”状态。每当处理
OnRowValidating
事件时,处理程序将设置 cancel 标志以指示验证失败。再次单击按钮以正常处理事件。 - 网格失败 - 单击时,将启用“网格失败”状态。每当处理
OnValidating
事件时,处理程序将设置 cancel 标志以指示验证失败。再次单击按钮以正常处理事件。 - 拒绝编辑 - 单击时,将启用“拒绝编辑”状态。每当处理
OnCellBeginEdit
事件时,处理程序将设置 cancel 标志以指示拒绝编辑。再次单击按钮以正常处理事件。 - 记录点击 - 单击时,将启用“记录点击”状态。这会强制在每次单击鼠标时在日志中插入一个空行。这通常使日志更容易理解,因为您可以轻松地看到自上次点击以来触发了哪些事件。
- 编辑模式 - 网格支持多种编辑模式。每种模式都有不同的事件行为。单击“自动编辑”可循环切换模式,日志将显示当前激活的模式。
- 清空日志 - 只需单击此按钮即可清空日志。所有日志将丢失。如果您想提前保存日志,可以在事件日志中复制(^C)。
Using the Code
代码并不复杂,我无意将本文作为网格编程教程,因此不会详细介绍。但是,您可能想要添加/删除网格事件。
在 EventGrid.cs 中,您会看到我支持的事件的处理程序,例如 OnCellEnter()
、OnCellBeginEdit()
等。
protected override void OnRowValidating(DataGridViewCellCancelEventArgs e)
{
base.OnRowValidating(e);
if (!ctrlInitialised)
{
return;
}
eventList.AddEvent("OnRowValidating (" + e.ColumnIndex + "," + e.RowIndex +
") - " + (form1.RowFail ? "FAIL" : "OK"));
e.Cancel = form1.RowFail;
}
每个处理程序执行以下操作:
- 调用基类事件处理程序。
- 检查网格是否已初始化。
- 使用要显示在日志中的
string
调用eventList.AddEvent()
。我的处理程序会在适当时将单元格索引放入string
中。 - 可选地修改事件参数中的标志,以指示与该事件相关的特定行为。
如果您不关心我支持的事件,只需删除处理程序即可。如果您想支持其他事件,请为新事件添加处理程序并按照上述步骤操作。
理解 DataGridView 事件
我们先从高层次的事件序列入手,然后再详细介绍下载中的状态机。
在一般情况下有几种变化,但让我们先假设网格编辑模式(DataGridView.EditMode
)是 EditOnKeystroke
,并且所有单元格、所有行和整个网格都是有效的。这允许在不进入编辑模式的情况下浏览网格。当我们从一个单元格移动到另一个单元格时,我们会看到(按顺序)
1. 离开事件
尝试离开单元格时,OnCellLeave()
事件始终会发生。根据移动到的位置,可能会发生其他 Leave
事件。例如:
- 移动到同一行的单元格 -
OnCellLeave()
- 移动到不同行的单元格 -
OnCellLeave()
,OnRowLeave()
- 将焦点移出网格 -
OnCellLeave()
,OnRowLeave()
,OnLeave()
(这是网格离开事件)
Leave
事件很简单,因为未来事件不受 Leave
事件处理程序中任何参数更改的影响。
2. 验证事件
当 Leave
事件完成后,Validation
事件将开始触发。现在,请记住我们在这里假设验证成功,因此 Validating/Validated 事件成对发生,Validating 之后是 Validated。您会从上面的顺序中辨认出来。
- 移动到同一行的单元格 -
OnCellValidating()
,OnCellValidated()
- 移动到不同行的单元格 -
OnCellValidating()
,OnCellValidated()
,OnRowValidating()
,OnRowValidated()
- 将焦点移出网格 -
OnCellValidating()
,OnCellValidated()
,OnRowValidating()
,OnRowValidated()
,OnValidating()
,OnValidated
3. 进入事件
一切都已验证,因此焦点可以按预期移动,事件序列通过触发新单元格的 Enter 事件来完成。
- 移动到同一行的单元格 -
OnCellEnter()
- 移动到不同行的单元格 -
OnRowEnter()
,OnCellEnter()
- 从网格外部移入单元格 -
OnRowEnter()
,OnCellEnter()
,OnEnter()
(这是网格进入事件)
将所有这些放在一起,当从有效行 R1 中的有效单元格 C1 移动到行 R2 中的单元格 C2 时,将触发以下事件序列:
OnCellLeave(C1)
OnRowLeave(R1)
OnCellValidating(C1)
OnCellValidated(C1)
OnRowValidating(R1)
OnRowValidated(R1)
OnRowEnter(R2)
OnCellEnter(C2)
如前所述,这只是一个案例,还有许多变体,因此让我们通过有限状态机(FSM)来跟踪这个案例(即,移动到不同行的单元格)。FSM 处理 EditOnKeystroke
的所有变体,当您熟悉它之后,就很容易理解 EditOnEnter
。稍后会介绍更多内容,但现在请查看 DgvFsmMasterEoK.pdf 中的主 FSM。
从图的左上角的状态 1 开始。在此状态下,已选择其中一个单元格但未进行编辑。当选择另一行的单元格时,将过渡到状态 5。这表明事件 OnCellLeave()
、OnRowLeave()
和 OnCellValidating()
(按此顺序)会发生。状态 5 是一个子 FSM,您可以在 DgvFsmA2.pdf 中找到它。现在查看 FSM A2,从状态 1(假设 OnCellValidating()
处理程序指示单元格有效)开始,将过渡到状态 2。此过渡表明事件 OnCellValidated()
和 OnRowValidating()
会发生。接下来,假设 OnRowValidating()
指示行有效,将发生事件 OnRowValidated()
、OnRowEnter()
和 OnCellEnter()
,子 FSM 将退出并返回主 FSM 状态 1。图中事件中显示的(旧)和(新)参数表示提供的参数是指正在离开(旧)还是正在进入(新)的单元格/行。
在完成了这个示例之后,您现在应该能够尝试各种变体(例如,当 OnRowValidating()
事件处理程序指示行无效时会发生什么)。可以尝试使用该应用程序并在 FSM 上跟踪它。
进入编辑
那么,如何将编辑模式更改为 EditOnEnter
呢?主要区别在于主 FSM 的状态 1。状态 1 正在等待用户选择另一个单元格或按键来为该单元格启动编辑模式。对于 EditOnEnter
,此状态变为瞬态,即无需等待,因为一旦选择了单元格,它将自动进入编辑模式。不再需要在状态 1 中等待。更改可以在 DgvFsmMasterEoE.pdf 中看到。所有进入状态 1 的输入都会在进入此状态之前的最后一个事件中触发 OnCellBeginEdit()
事件。您的 OnCellBeginEdit()
处理程序可以拒绝编辑,将单元格保留在状态 2 中,或者接受它并像以前一样进入状态 3 的编辑模式。您可能还会注意到状态 3 不再可以通过 Escape 键退出编辑模式,该键将被忽略。没有其他区别,子 FSM 不受影响。