事件调度
UIElements监听来自操作系统或者脚本的事件,并使用EventDispatcher将这些事件分发给可视元素。事件调度程序为其发送的每个事件确定适当的调度策略。一旦确定,调度员就执行该策略。
可视元素和其他支持类,实现了某些事件的默认行为。某些情况下,这将涉及到创建和发送其它附加事件。例如,MouseMoveEvent
可以生成额外的MouseEnterEvent
和MouseLeaveEvent
。这些附加事件将被放在队列中,并在当前事件完成后处理。例如,MouseMoveEvent
将在处理MouseEnterEvent
和MouseLeaveEvent
事件之前完成。
事件目标(Event target)
EventDispatcher.DispatchEvent()
的首要任务是找到事件的目标。
这有时很容易,因为事件的target
属性已设置。但是,对于源自操作系统的大多数事件而言,情况并非如此。
事件的目标依赖于事件类型。对于鼠标事件,目标通常是位于鼠标下方的最直接的可选元素。对于键盘事件,目标是处于当前焦点的元素。
找到目标后,会将其存储在EventBase.target
中,在整个调度过程执行期间不会发生变化。Event.currentTarget
将更新为当前正在处理的事件中的目标元素。
拾取模式和自定义形状
大多数鼠标事件使用拾取模式来确定其目标。在VisualElement
类中有一个pickingMode
属性,它支持下列值:
PickingMode.Position
(默认值):根据定位矩形执行拾取。PickingMode.Ignore
:避免被鼠标事件选择为目标。
您可以覆盖VisualElement.ContainsPoint()
方法来执行自定义的交互逻辑。
鼠标捕获
有时候,在鼠标按下之后,有的元素必须持续捕获鼠标位置,以确保后续所有鼠标事件都只发送给本元素,即使指针不再悬停在该元素上也是如此。例如某些控键(如:按钮,滑块或滚动条)需要依赖这样鼠标事件序列:MouseDown、MouseMove、MouseUp,此时就需要持续捕获鼠标位置。
要捕获鼠标,需要调用element.CaptureMouse()
或MouseCaptureController.CaptureMouse()
。
要释放鼠标,需要调用MouseCaptureController.ReleaseMouse()
。如果另一个元素在您调用时已经捕获了鼠标CaptureMouse()
,则该元素将接收MouseCaptureOutEvent
事件并将丢失鼠标捕获。
任意时刻,应用程序中只有一个元素可以捕获鼠标。一旦某个元素捕获鼠标,它将成为所有后续鼠标事件的目标,鼠标滚轮事件除外。注意:这仅适用于尚未设置目标的鼠标事件。
焦点环和Tab顺序
每个UIElement面板都有一个焦点环,用于定义元素的焦点顺序。默认情况下,通过在可视元素树上执行深度优先搜索(DFS depth-first search)来确定元素的焦点顺序。例如,下面描述的树的焦点顺序是F,B,A,D,C,E,G,I,H。
焦点顺序
某些事件使用焦点顺序来确定哪个元素持有焦点。例如,键盘事件的目标是当前持有焦点的元素。
使用focusable
属性可以控制元素是否可以获得焦点。默认情况下,VisualElements
不可获得焦点,但默认情况下某些子类(例如TextField
,可能是可以获得焦点的)。
使用tabIndex
属性来控制焦点顺序,规则如下所示(tabIndex
默认值为0):
- 如果
tabIndex
为负数,则元素不可通过Tab获得焦点。 - 如果
tabIndex
为零,则元素保持其默认Tab顺序,即由焦点环算法确定。 - 如果
tabIndex
为正,则元素放在那些tabIndex
为零或tabIndex
值更高的元素前面{从小到达优先排列正值,后自动排列零值}。
事件传播
选择事件目标后,调度器计算事件的传播路径。传播路径是可以接收该事件的可视元素的有序列表。
从可视元素树的根开始,下降到目标,然后再上升到根来获得元素列表。
传播路径
传播路径的第一阶段从根节点到达目标的父节点。这被称为涓滴阶段。{涓滴这个词来自于经济学术语“涓滴效应”}
传播路径的后一个阶段从目标父节点上升到根节点。这被称为冒泡阶段。
事件目标位于传播路径的中间。
大多数事件类型都会发送给传播路径上的所有元素,某些事件类型会跳过冒泡阶段。此外,某些事件类型仅仅发送到事件目标。
如果某个元素被隐藏或禁用,则它不会接收事件。事件仍然会传播到隐藏或禁用元素的前辈节点和后辈节点。
当事件沿传播路径发送时,Event.currentTarget
将更新为当前被处理事件的元素。这意味着在事件的回调函数中,Event.currentTarget
是回调函数所注册的元素,而Event.target
的是事件发生所在的元素。
事件类型的分发行为
每种事件类型都有自己的分发行为。下表每个事件类型的行为总结在三列中:
- Trickles down(涓流):这种类型的事件在涓滴阶段被发送到元素。
- Bubbles up(冒泡):这种类型的事件在冒泡阶段被发送到元素。
- Cancellable(可取消):此类事件可以取消,停止或阻止其默认操作的执行。
Trickles down | Bubbles up | Cancellable | |
---|---|---|---|
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 | ✔ | ✔ | ✔ |