Version: 2021.3
Dispatching events
Synthesizing events

Handle events

UI 工具包中的事件类似于 HTML 事件 。事件发生时,将沿着视觉元素树中的传播路径发送事件。事件不仅发送到目标视觉元素,而且发送到传播路径中的所有元素。

事件处理顺序如下:

  1. 在从根元素往下到事件目标父级的元素上执行事件回调。这是分发过程的涓滴阶段
  2. 在事件目标上执行事件回调。这是分发过程的目标阶段
  3. 在事件目标上调用 ExecuteDefaultActionAtTarget()
  4. 从事件目标父级往上到直到根部的元素上执行事件回调。这是分发过程的冒泡阶段
  5. 在事件目标上调用 ExecuteDefaultAction()

在沿着传播路径发送事件时,Event.currentTarget 属性更新为当前正在处理事件的元素。在事件回调函数中:

  • Event.currentTarget 是回调注册的视觉元素。
  • Event.target 是原始事件发生的视觉元素。

有关更多信息,请参阅分发事件

Register an event callback

通过注册事件回调,您可以自定义现有类的单个实例的行为,例如对鼠标单击文本标签做出反应。

传播路径上的每个元素(目标除外)对一个事件可以接收两次:

  • 在涓滴阶段一次。
  • 在冒泡阶段一次。

默认情况下,注册的回调会在目标阶段和冒泡阶段执行。此默认行为可确保父元素在其子元素之后做出反应。例如,如果您希望父元素在其子元素之前做出反应,请使用 TrickleDown.TrickleDown 选项注册回调:

// 为涓滴阶段注册回调
myElement.RegisterCallback<MouseDownEvent>(MyCallback, TrickleDown.TrickleDown);

此代码将通知分发程序在目标阶段和涓滴阶段执行回调。

要将自定义行为添加到特定的视觉元素,您必须在该元素上注册事件回调。例如,以下代码为 MouseDownEvent 注册一个回调:

// 对鼠标按下事件注册回调
myElement.RegisterCallback<MouseDownEvent>(MyCallback);

回调签名应如下所示:

void MyCallback(MouseDownEvent evt) { /* ...*/ }

您可以为一个事件注册多个回调。同一事件的同一回调函数在传播阶段只能注册一次。要从 VisualElement 中移除回调,则调用 myElement.UnregisterCallback() 方法。

Send custom data to an event callback

您可以将自定义数据随同回调一起发送到事件。要附加自定义数据,您必须通过扩展该调用的方式来注册回调。

以下代码为 MouseDownEvent 注册一个回调并将自定义数据发送到回调函数:

//将用户数据一起发送到回调
myElement.RegisterCallback<MouseDownEvent, MyType>(MyCallbackWithData, myData);

回调函数应返回此签名:

void MyCallbackWithData(MouseDownEvent evt, MyType data) { /* ...*/ }

Handle input events for a control

You can use an event handler or use a manipulator to handle input events.

Capture the pointer

When you handle pointer input, you might want the control to capture a pointer. When a visual element captures a pointer, Unity sends all the events associated with the pointer to the visual element regardless of whether the pointer hovers over the visual element. For example, if you create a control that receives drag events and captures the pointer, the control still receives drag events regardless of the pointer location.

To capture a pointer, use capture events. See Create a drag-and-drop UI inside a custom Editor window for an example.

Use a manipulator to handle events

If you want to separate your event logic from your UI code, use a manipulator to handle events. A manipulator is a dedicated class that stores, registers, and unregisters event callbacks. You can use or inherit from one of the manipulators that UI Toolkit supports to handle events.

UI Toolkit supports the following manipulators:

Manipulator Inherits from 描述
Manipulator Base class for all provided manipulators.
KeyboardNavigationManipulator Manipulator Handles translation of device-specific input events to higher-level navigation operations with a keyboard.
MouseManipulator Manipulator Handles mouse input. Has a list of activation filters.
ContextualMenuManipulator MouseManipulator Displays a contextual menu when the user clicks the right mouse button or presses the menu key on the keyboard.
PointerManipulator MouseManipulator Handles pointer input. Has a list of activation filters.
Clickable PointerManipulator Tracks mouse events on an element and callbacks when a user clicks a mouse button while the pointer hovers over an element.

Respond to events with custom controls

如果您要实现自定义控件,则可以通过两种方式响应 UI 工具包事件:

  • 注册事件回调。
  • 实现默认操作。

您选择如何响应事件取决于情况。

回调和默认操作之间的区别是:

  • 回调必须在类的实例上注册。默认操作作为类上的虚拟函数运行。
  • 回调在传播路径上的所有视觉元素都会执行。默认操作仅对事件目标执行。
  • 回调可能会执行额外的检查,以确定它们是否应该对事件做出反应。例如,处理鼠标单击的回调可能会检查元素是否是事件的目标。默认操作可以跳过此步骤。
  • 默认操作在性能上略有优势,因为它们在传播阶段不需要在回调注册表中查找。

Implement a default action

默认操作适用于该类的所有实例。实现默认操作的类也可以在该类的实例上注册回调。

当一个类实现默认操作时,它必须派生一个新的 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() 中实现默认操作。

应该将默认操作视为一个元素类型在接收事件时应该具有的行为。例如,复选框应通过切换其状态来响应单击事件。您可以通过覆盖默认操作虚拟函数(而不是在所有复选框上注册回调)来实现此行为。

自定义控件的最佳实践

The following are best practices for custom controls.

Implement behaviors

您应该通过具有默认操作的元素来实现行为。您可以在附加到实例的回调中调用 PreventDefault() 取消默认元素行为。

将行为实现为默认操作的其他好处是:

  • 默认操作不需要在回调注册表中查找。
  • 没有回调的实例不进入传播过程。

为了获得更大灵活性,可在事件分发过程中的以下两个时刻执行事件目标的默认操作:

  1. 在涓滴与冒泡传播阶段之间,在刚执行目标回调之后,覆盖 ExecuteDefaultActionsAtTarget()
  2. 在事件分发过程结束时,覆盖 ExecuteDefaultActions()

类的默认操作

如有可能,尽量在 ExecuteDefaultActions() 中实现类的默认操作。这允许更多选项来覆盖类。您可以在事件传播过程的涓滴阶段或冒泡阶段调用 PreventDefault() 来覆盖类。

如果事件不应传播到父元素,则必须在默认操作期间停止事件传播。例如,文本字段接收修改其值的 KeyDownEvent,例如使用 Delete 键删除内容。此事件不得传播到父视觉元素。使用 ExecuteDefaultActionsAtTarget() 实现默认操作并调用 StopPropagation() 以确保在冒泡阶段不处理该事件。

仅对事件目标执行默认操作。要使类对针对其子元素或父元素的事件做出反应,您必须注册回调以在涓流或冒泡传播阶段接收事件。避免在类中注册回调以提高性能。

Stop event propagation and cancel default actions

在回调或默认操作中处理事件时,您可以停止进一步的事件传播和默认操作的执行。例如,父面板可通过在涓流阶段停止传播来阻止其子面板接收事件。

您不能阻止事件类自身内部的 EventBase.PreDispatch()EventBase.PostDispatch() 方法。

以下方法影响事件传播和默认操作:

  • StopImmediatePropagation(): Stops the event propagation process immediately, so no other callbacks execute for the event. However, the ExecuteDefaultActionAtTarget() and ExecuteDefaultAction() default actions still execute.
  • StopPropagation(): Stops the event propagation process after the last callback on the current element. This ensures that all callbacks execute on the current element, but no further elements react to the event. The ExecuteDefaultActionAtTarget() and ExecuteDefaultAction() default actions still execute.
  • PreventDefaultAction(): Prevents the event propagation process from calling the ExecuteDefaultActionAtTarget() and ExecuteDefaultAction() default actions. PreventDefaultAction() doesn’t prevent the execution of other callbacks and doesn’t effective the ExecuteDefaultActionAtTarget() action during the bubble-up phase.
Dispatching events
Synthesizing events