Version: 2018.4
事件系统
事件响应

分发事件

UIElements 监听来自操作系统或脚本的事件,并使用 EventDispatcher 将这些事件分发给视觉元素。事件分发程序为发送的每个事件确定相应分发策略。一旦确定后,分发程序就会执行该策略。

视觉元素和其他支持类实现了几个事件的默认行为。有时,涉及创建和发送额外的事件。例如,MouseMoveEvent 可能生成额外的 MouseEnterEvent 和 MouseLeaveEvent。这些额外的事件将放置在队列中,并会在当前事件完成后处理这些事件。例如,完成 MouseMoveEvent 后处理 MouseEnterEvent 和 MouseLeaveEvent 事件。

事件目标

EventDispatcher.DispatchEvent() 的首个任务是查找事件目标。

有时,这很容易,因为已经设置事件 target 属性。但是,对于源自操作系统的大多数事件,则并非如此。

事件的目标取决于事件类型。对于鼠标事件,目标通常是直接位于鼠标下面最上层的可选择元素。对于键盘事件,目标是当前聚焦的元素。

找到目标后,目标会存储在 EventBase.target 中,且在分发过程中不会改变。属性 Event.currentTarget 会更新为当前正在处理该事件的元素。

拾取模式和自定义形状

大多数鼠标事件使用拾取模式来确定其目标。VisualElement 类有一个支持以下值的 pickingMode 属性:

  • PickingMode.Position(默认值):根据位置矩形执行拾取。
  • PickingMode.Ignore:阻止因鼠标事件而拾取。

可以覆盖 VisualElement.ContainsPoint() 方法来执行自定义的交集逻辑。

捕获鼠标

有时,在鼠标按下后,元素必须捕获鼠标位置以确保所有后续的鼠标事件都只发送给自己(即使指针不再悬停在该元素上)。如果一个控件会响应鼠标按下和鼠标松开序列 - 在鼠标按下和鼠标松开事件之间可能会移动鼠标,通常便会有此要求。例如,在单击按钮、滑动条或滚动条时就属于这种情况。

要捕获鼠标,请调用 element.CaptureMouse() 或 MouseCaptureController.CaptureMouse()。

要释放鼠标,请调用 MouseCaptureController.ReleaseMouse()。如果在调用 CaptureMouse() 时已经有另一个元素正在捕获鼠标,该元素将收到 MouseCaptureOutEvent 并失去此捕获。

在应用程序中,任何时候都只能有一个元素具有捕获。当元素具有捕获时,该元素便是除鼠标滚轮事件之外的所有后续鼠标事件的目标。

注意:这只适用于尚未设置目标的鼠标事件。

焦点环和焦点顺序

每个 UIElement 面板都有一个焦点环用于定义元素的焦点顺序。默认情况下,元素的焦点顺序是通过对视觉元素树执行深度优先搜索 (DFS) 来确定的。例如,以下所示的树的焦点顺序将是 F、B、A、D、C、E、G、I、H。

焦点顺序
焦点顺序

有些事件使用焦点顺序来确定哪个元素获得焦点。例如,键盘事件的目标是当前获得焦点的元素。

使用 focus-index 属性可控制焦点顺序,如下所示:

  • 如果 focus-index 为负,则无法聚焦该元素。默认情况下,无法聚焦 VisualElements,但是默认可聚焦某些子类(例如 TextField)。
  • 如果 focus-index 为零,该元素保持默认的焦点顺序(通过焦点环算法确定的焦点顺序)。
  • 如果 focus-index 为正,该元素将置于 focus-index = 0 的所有元素以及具有更高 focus-index 的所有元素之前。

事件传播

选择事件目标后,分发程序将计算事件的传播路径。传播路径是接收事件的视觉元素的有序列表。

元素列表的获得方式是从视觉元素树的根开始,朝着目标往下,再朝着树的根往上。

传播路径
传播路径

传播路径的第一阶段从根下降到目标父级。此阶段称为涓滴 (trickle down) 阶段。

传播路径的最后一个阶段从目标父级上升到根。此阶段称为冒泡 (bubble-up) 阶段。

事件目标位于传播路径中间。

大多数事件类型将发送到传播路径上的所有元素。但是,一些事件类型会跳过冒泡阶段。此外,一些事件类型仅发送到事件目标。

如果元素被隐藏或禁用,它将不会接收事件。事件仍会传播到被隐藏或禁用的元素的祖先和后代。

在沿着传播路径发送事件时,Event.currentTarget 更新为当前正在处理事件的元素。这意味着,在事件回调函数中,Event.currentTarget 是对回调注册的元素,而 Event.target 是发生事件的元素。

事件类型的分发行为

每个事件类型都有自己的分发行为。下表以三列的形式汇总了每个事件类型的行为:

  • 涓滴:在涓滴阶段将此类型的事件发送到元素。
  • 冒泡:在冒泡阶段将此类型的事件发送到元素。
  • 可取消:此类事件可以取消、停止或阻止默认操作的执行。
涓滴 冒泡 可取消
MouseCaptureOutEvent ✔ ✔
MouseCaptureEvent ✔ ✔
ChangeEvent ✔ ✔
ValidateCommandEvent ✔ ✔ ✔
ExecuteCommandEvent ✔ ✔ ✔
DragExitedEvent ✔ ✔
DragUpdatedEvent ✔ ✔ ✔
DragPerformEvent ✔ ✔ ✔
DragEnterEvent ✔
DragLeaveEvent ✔
FocusOutEvent ✔ ✔
BlurEvent ✔
FocusInEvent ✔ ✔
FocusEvent ✔
InputEvent ✔ ✔
KeyDownEvent ✔ ✔ ✔
KeyUpEvent ✔ ✔ ✔
GeometryChangedEvent
MouseDownEvent ✔ ✔ ✔
MouseUpEvent ✔ ✔ ✔
MouseMoveEvent ✔ ✔ ✔
ContextClickEvent ✔ ✔ ✔
WheelEvent ✔ ✔ ✔
MouseEnterEvent ✔
MouseLeaveEvent ✔
MouseEnterWindowEvent ✔
MouseLeaveWindowEvent ✔
MouseOverEvent ✔ ✔ ✔
MouseOutEvent ✔ ✔ ✔
ContextualMenuPopulateEvent ✔ ✔ ✔
AttachToPanelEvent
DetachFromPanelEvent
TooltipEvent ✔ ✔
IMGUIEvent ✔ ✔ ✔

  • 2018–11–02 页面已修订并只进行了有限的编辑审查
事件系统
事件响应