UI 工具包中的事件类似于 HTML 事件 。事件发生时,将沿着视觉元素树中的传播路径发送事件。事件不仅发送到目标视觉元素,而且发送到传播路径中的所有元素。
事件处理顺序如下:
1.在从根元素往下到事件目标父级的元素上执行事件回调。这是分发过程的涓滴阶段。
2.在事件目标上执行事件回调。这是分发过程的目标阶段。
3.在事件目标上调用 ExecuteDefaultActionAtTarget()
。
4.从事件目标父级往上到直到根部的元素上执行事件回调。这是分发过程的冒泡阶段。
5.在事件目标上调用 ExecuteDefaultAction()
。
在沿着传播路径发送事件时,Event.currentTarget
属性更新为当前正在处理事件的元素。在事件回调函数中:
Event.currentTarget
是回调注册的视觉元素。Event.target
是原始事件发生的视觉元素。有关更多信息,请参阅分发事件。
通过注册事件回调,您可以自定义现有类的单个实例的行为,例如对鼠标单击文本标签做出反应。
传播路径上的每个元素(目标除外)对一个事件可以接收两次:
默认情况下,注册的回调会在目标阶段和冒泡阶段执行。此默认行为可确保父元素在其子元素之后做出反应。例如,如果您希望父元素在其子元素之前做出反应,请使用 TrickleDown.TrickleDown
选项注册回调:
// 为涓滴阶段注册回调
myElement.RegisterCallback<MouseDownEvent>(MyCallback, TrickleDown.TrickleDown);
此代码将通知分发程序在目标阶段和涓滴阶段执行回调。
要将自定义行为添加到特定的视觉元素,您必须在该元素上注册事件回调。例如,以下代码为 MouseDownEvent
注册一个回调:
// 对鼠标按下事件注册回调
myElement.RegisterCallback<MouseDownEvent>(MyCallback);
回调签名应如下所示:
void MyCallback(MouseDownEvent evt) { /* ...*/ }
您可以为一个事件注册多个回调。同一事件的同一回调函数在传播阶段只能注册一次。要从 VisualElement
中移除回调,则调用 myElement.UnregisterCallback()
方法。
您可以将自定义数据随同回调一起发送到事件。要附加自定义数据,您必须通过扩展该调用的方式来注册回调。
以下代码为 MouseDownEvent
注册一个回调并将自定义数据发送到回调函数:
//将用户数据一起发送到回调
myElement.RegisterCallback<MouseDownEvent, MyType>(MyCallbackWithData, myData);
回调函数应返回此签名:
void MyCallbackWithData(MouseDownEvent evt, MyType data) { /* ...*/ }
如果您要实现自定义控件,则可以通过两种方式响应 UI 工具包事件:
您选择如何响应事件取决于情况。
回调和默认操作之间的区别是:
默认操作适用于该类的所有实例。实现默认操作的类也可以在该类的实例上注册回调。
当一个类实现默认操作时,它必须派生一个新的 VisualElement
的子类并实现 ExecuteDefaultActionAtTarget()
和/或 ExecuteDefaultAction()
方法,或两者都实现。
默认操作是视觉元素子类的每个实例在接收事件时执行的操作。您可以通过覆盖 ExecuteDefaultActionAtTarget()
和 ExecuteDefaultAction()
来自定义默认操作,如下例所示:
override void ExecuteDefaultActionAtTarget(EventBase evt)
{
// 调用基函数。
base.ExecuteDefaultActionAtTarget(evt);
if (evt.GetEventTypeId() == MouseDownEvent.TypeId())
{
// ...
}
else if (evt.GetEventTypeId() == MouseUpEvent.TypeId())
{
// ...
}
//更多事件类型
}
通过在 ExecuteDefaultAction()
中实现您的默认操作,您可以停止或阻止一个默认操作的执行。
如果希望在父级回调之前执行目标默认操作,请在 `ExecuteDefaultActionAtTarget() 中实现默认操作。
应该将默认操作视为一个元素类型在接收事件时应该具有的行为。例如,复选框应通过切换其状态来响应单击事件。您可以通过覆盖默认操作虚拟函数(而不是在所有复选框上注册回调)来实现此行为。
您应该通过具有默认操作的元素来实现行为。您可以在附加到实例的回调中调用 PreventDefault()
取消默认元素行为。
将行为实现为默认操作的其他好处是:
为了获得更大灵活性,可在事件分发过程中的以下两个时刻执行事件目标的默认操作:
1.在涓滴与冒泡传播阶段之间,在刚执行目标回调之后,覆盖 ExecuteDefaultActionsAtTarget()
。
2.在事件分发过程结束时,覆盖 ExecuteDefaultActions()
。
如有可能,尽量在 ExecuteDefaultActions()
中实现类的默认操作。这允许更多选项来覆盖类。您可以在事件传播过程的涓滴阶段或冒泡阶段调用 PreventDefault()
来覆盖类。
如果事件不应传播到父元素,则必须在默认操作期间停止事件传播。例如,文本字段接收修改其值的 KeyDownEvent
,例如使用 Delete
键删除内容。此事件不得传播到父视觉元素。使用 ExecuteDefaultActionsAtTarget()
实现默认操作并调用 StopPropagation()
以确保在冒泡阶段不处理该事件。
仅对事件目标执行默认操作。要使类对针对其子元素或父元素的事件做出反应,您必须注册回调以在涓流或冒泡传播阶段接收事件。避免在类中注册回调以提高性能。
在回调或默认操作中处理事件时,您可以停止进一步的事件传播和默认操作的执行。例如,父面板可通过在涓流阶段停止传播来阻止其子面板接收事件。
您不能阻止事件类自身内部的 EventBase.PreDispatch()
和 EventBase.PostDispatch()
方法。
以下方法影响事件传播和默认操作:
StopImmediatePropagation()
ExecuteDefaultActionAtTarget()
和 ExecuteDefaultAction()
默认操作仍然执行。StopPropagation()
ExecuteDefaultActionAtTarget()
和 ExecuteDefaultAction()
默认操作仍然执行。PreventDefaultAction()
ExecuteDefaultActionAtTarget()
和 ExecuteDefaultAction()
默认操作。PreventDefaultAction()
不会阻止其他回调的执行,并且在冒泡阶段调用时对 ExecuteDefaultActionAtTarget()
无效。