Terminal.Gui 事件术语体系详解:从 Event、Bubble 到 CommandRouting 的统一词汇表
2026/9/24 11:06:39 网站建设 项目流程
  • UI组件
  • 跨平台
  • 桌面应用

【免费下载链接】Terminal.Gui

Cross Platform Terminal UI toolkit for .NET

项目地址:https://gitcode.com/gh_mirrors/te/Terminal.Gui
点击查看免费下载

Terminal.Gui 的事件系统横跨Cancellable Work Pattern(可取消工作模式,CWP)Command 命令系统与视图绘制、键盘、鼠标等众多子系统,因此官方维护了一份精确定义的"事件词表"(Events Lexicon),统一了EventRaiseBubbleDispatchBridge等 18 个核心术语的含义。本文以该词表为骨架,逐条解释每个术语在框架中的确切含义与源码对应实现,帮助读者在阅读 事件深度指南、命令深度指南 和 CWP 概念文档 时使用同一种语言沟通。

这份词表由仓库中的 docfx/includes/events-lexicon.md 定义,并被 docfx/docs/events.md、docfx/docs/cancellable-work-pattern.md 和总词表 docfx/docs/lexicon.md 共同引用。读完本文,你将能准确区分"事件向上 Bubble 还是向下 Dispatch""Bridge 与 SuperView 关系有何不同""HandledCancel各自适用什么场景"等关键概念。

这套词表服务于哪三个体系

在深入词条之前,先明确这套术语的三块应用土壤,它们共同构成了 Terminal.Gui 的事件世界观:

  • Cancellable Work Pattern(CWP):一种"默认执行 + 外部定制 + 可取消"的工作流模式,以事件为主、虚方法为辅。词表中的CancelCancellationContextDefault BehaviorNotificationsWorkflow均直接源于该模式,概念性定义见 cancellable-work-pattern.md。
  • Command 命令系统:用Command枚举作为用户操作的标准化词汇(ActivateAcceptHotKeyCutPasteSave等 50 余个),并借助CommandRouting在视图层级中传播。词表中的CommandDispatchBubbleBridgeRouting全部属于这一体系,详见 command.md。
  • 视图核心流程:绘制(View.Draw)、键盘(View.Keyboard)、命令(View.Command)等内置工作流是 CWP 在框架中的具体应用,词表中的EventListenInvokeRaiseHandle贯穿其间。

换句话说:CWP 定义了"何时通知、如何取消",Command 体系定义了"通知什么、往哪个方向走",而词表统一了两者的话语体系。

通知的基础四词:Event / Listen / Invoke / Raise

事件机制的本质是"观察者模式":一个对象在感兴趣的事情发生时通知其他对象。词表用四个词精确区分了这一流程的不同环节:

术语含义
Event一种通知机制,允许对象在感兴趣的事情发生时相互通信。Terminal.Gui 在 UI 交互中大量使用事件。
Listen订阅或注册某个事件以接收通知的行为。
Invoke调用或触发一个事件、Action 或方法的行为。
Raise触发事件、通知所有已注册的事件处理器"事件已发生"的行为。

在代码层面,这四个词的对应关系非常清晰:

  • Event即字段形式的event EventHandler<TEventArgs>?声明,例如View上的Accepting/ActivatedSliderOrientationChanging
  • Listen+=订阅,例如button.Accepted += (_, _) => DoTheThing ();
  • Invoke指框架内部主动调用处理逻辑,例如View.InvokeCommand (Command.Activate)
  • RaiseRaiseActivating/RaiseActivated这类专门负责触发事件的方法(词表在NotificationsDefault Behavior条目中将其与虚方法并列,见下文)。

一个值得注意的实现约定(见 events.md 的 Recipe 3):虚方法默认必须是无操作(no-op),事件触发必须发生在独立的Raise*方法中,而不是虚方法内部。正确的顺序永远是:先调用虚方法(子类优先)→ 再触发事件(外部订阅者)→ 最后执行默认行为。

反馈与干预:Handle、Cancel 与 Cancellation

事件不仅用于"通知",还用于"干预"。词表用HandleCancel两个家族区分两种不同的干预语义:

术语含义
Handle/Handling/Handled适用于"事件可以由监听者(或重写)处理,也可以不处理"的场景。典型例子是源于用户操作的事件,如鼠标移动和按键。
Cancel/Cancelling/Cancelled适用于"某些事情可以被取消"的场景,例如改变SliderOrientation
Cancellation用于中止某个阶段或工作流的机制,例如在事件参数中设置Cancel/Handled属性,或从虚方法返回bool

两者的边界在 cancellable-work-pattern.md 中有更细的刻画:输入类事件(InputEventArgs)惯用Handled,而独立的可取消工作流(如属性变更)惯用Cancel。不过在 Terminal.Gui 的 CWP 实现中,框架统一以Handled作为取消标志——events.md 的"Common Pitfalls"专门强调:ValueChangingEventArgs没有Cancel属性,args.Cancel = true是错误用法,必须写args.Handled = true

从源码可以印证这一设计。CWP 的取消判定在 CWPPropertyHelper.cs 中表现为两条链:

onChanging 虚方法返回 true 或 args.Handled == true → 取消 changingEvent?.Invoke 后 args.Handled == true → 取消

而 CWPWorkflowHelper.cs 的Execute<T>同样以onMethod(args) || args.Handled作为"已处理"的判定起点。也就是说,框架层把"处理"与"取消"统一收敛到Handled一个布尔开关上,词表将它们分开列出是为了帮助读者理解语义来源,而落地编码时只需记住Handled

干预的载体:Context 与 Default Behavior

Handle/Cancel回答"能否干预",ContextDefault Behavior则回答"干预时看到什么、不干预时发生什么":

术语含义
Context传递给观察者用于决策的数据,例如绘制用的DrawContext、键盘用的Key、命令用的ICommandContext,以及方向变更用的CancelEventArgs<Orientation>
Default Behavior每个阶段的标准实现,例如绘制的DrawText、键盘与应用级的InvokeCommands、命令的RaiseActivating,以及属性更新(OrientationHelper)。

Context:命令上下文ICommandContext

词表明确点名了ICommandContext,这是 Command 体系中最重要的上下文对象,定义于 ICommandContext.cs:

public interface ICommandContext { Command Command { get; } // 正在调用的命令 WeakReference<View>? Source { get; } // 指向命令发起视图的弱引用 ICommandBinding? Binding { get; } // 触发命令的绑定(键/鼠标/编程式) CommandRouting Routing { get; } // Direct / BubblingUp / DispatchingDown / Bridged IReadOnlyList<object?> Values { get; } // 命令传播过程中累积的值链 object? Value { get; } // 最近追加的值(Values[^1]) }

两个细节值得注意:

  1. SourceWeakReference<View>目的是防止命令传播期间因视图被释放而产生内存泄漏。安全访问方式是args.Context?.Source?.TryGetTarget (out View? view)
  2. Values是一条只追加的值链:每个实现了IValue的视图在命令传播时把自己的值追加进来,顺序从最内层(发起者)到最外层。Value只是Values[^1]的便捷访问器。用 LINQ 按类型搜索(ctx.Values?.FirstOrDefault (v => v is Schemes))是在深层级联中定位具体值的惯用做法。

Default Behavior:CWP 的三元结构

Default Behavior是 CWP 得以"开箱即用"的保证。CWP 的核心结构是"默认执行 + 定制 + 取消"三元组:即使没有任何外部代码介入,每个阶段也有一条标准实现路径保证系统照常运转。在 CWPPropertyHelper.ChangeProperty 中可以看到完整的默认行为链:

  1. 值相等 → 直接返回false(无变更);
  2. onChanging虚方法 +changingEvent事件,任一取消则返回false
  3. 非空校验(NewValue对非可空引用类型不能为 null,否则抛InvalidOperationException);
  4. doWork写入后备字段并更新相关状态(先做工作、再发 Changed 事件);
  5. onChanged虚方法 +changedEvent事件通知完成。

命令传播的四方向:Command / Dispatch / Bubble / Bridge / Routing

如果说前两节是 CWP 的"时间轴"(阶段先后),那么本节是 Command 体系的"空间轴"(传播方向)。词表用一组动词精确区分了命令在视图层级中的四种运动方式:

术语含义
Command一种将请求封装为对象的模式,允许对请求进行参数化和排队。详见 command.md。
Dispatch/Dispatching从 SuperView向下把命令发送到特定 SubView。向下的方向永远是 "dispatch",绝不叫 "bubble"。DispatchDown发送时抑制冒泡;TryDispatchToTarget借助GetDispatchTargetConsumeDispatch为组合视图自动化分发。
Bubble/Bubbling命令从 SubView向上传播到其 SuperView。通过CommandsToBubbleUp选择启用。向上的方向永远是 "bubble"——绝不叫 "dispatch"。
Bridge/Bridging非包含边界路由命令(例如MenuBarItemPopoverMenu)。CommandBridge订阅远程视图的完成事件,并以CommandRouting.Bridged在所有者上重新触发。
RoutingCommandRouting枚举描述命令的路由方式:Direct(本地调用)、BubblingUp(向上到 SuperView)、DispatchingDown(向下到 SubView)、Bridged(跨非包含边界)。由ICommandContext.Routing携带。

Routing:用一个判别式枚举取代两个布尔标志

词表所指的CommandRouting定义于 CommandRouting.cs。从源码注释看,它取代了旧的临时布尔标志IsBubblingUpIsBubblingDown,把方向信息收敛为一个判别式(discriminated)枚举。四种取值与源码注释的对应关系是:

  • Direct—— 编程式调用,或来自视图自身绑定的调用;
  • BubblingUp—— 命令正沿 SuperView 链向上传播;
  • DispatchingDown—— SuperView 正向下分发到特定 SubView;
  • Bridged—— 命令正通过CommandBridge跨越非包含边界。

Bubble:通知而非消费

Bubble的关键语义是它是"通知"而非"消费":SuperView 的返回值会被传播,但中继视图无论结果如何都会继续自己的处理。启用方式是在祖先视图上设置CommandsToBubbleUp

myWindow.CommandsToBubbleUp = [Command.Activate, Command.Accept];

此后窗口内任意 SubView 触发Activate/Accept时,myWindow.Activated/Accepted都会收到冒泡事件。框架内常用取值见 command.md:Shortcut冒泡[Activate, Accept]Dialog冒泡[Accept]SelectorBase冒泡[Activate, Accept]

Dispatch:向下的自动化分发

Dispatch服务于组合视图(Composite View)模式。框架通过三个虚成员实现自动化:

  • GetDispatchTarget(ICommandContext?)—— 返回应接收分发的 SubView,返回null表示跳过分发;
  • ConsumeDispatch—— 控制分发是否"消费"命令:false为中继(如Shortcut分发到CommandView后发起者继续自己的激活),true为消费(如OptionSelector/MenuBar分发后由组合视图自己触发RaiseActivated/RaiseAccepted);
  • DispatchDown(target, ctx)—— 以CommandRouting.DispatchingDown构造上下文并在目标上调用,TryBubbleUp检测到该路由会跳过冒泡,从而防止无限递归

Bridge:跨非包含边界的单向通道

Bridge解决的是 SuperView/SubView 树之外的关系。典型场景:MenuItem拥有的SubMenuPopoverMenu)注册在Application.Popover中,并不在 SuperView 层级里,命令无法靠 Bubble 传播。此时用 CommandBridge.cs 搭桥:

CommandBridge bridge = CommandBridge.Connect (owner, remote, Command.Accept, Command.Activate); bridge.Dispose (); // 拆除订阅

桥的实现要点(均可从源码确认):

  1. 订阅远程视图的完成事件AcceptAcceptedActivateActivated,其余命令 →CommandNotBound
  2. CommandRouting.Bridged重新进入完整管道:桥调用的是View.InvokeCommand而非RaiseAccepted/RaiseActivated,因此会完整走RaiseAccepting/RaiseActivating → TryDispatchToTarget → TryBubbleUp → RaiseAccepted/RaiseActivated
  3. TryDispatchToTargetBridged路由有守卫:防止桥接命令向下分发到所有者的 CommandView(桥是"向上"带命令,不是"向下");
  4. 保留ValuesValues = e.Context?.Values ?? [],远程层级累积的值对所有者的订阅者可见;
  5. 两端都是弱引用,不会阻止 GC;桥是单向的,双向路由需要建两座桥。

一个必须牢记的桥接限制(command.md 以重要提示标注):由于桥订阅的是远程视图的事后事件Activated/Accepted),远程侧的状态变更已经发生,所有者在Activating/Accepting中设置args.Handled = true无法撤销远程侧已发生的变更,框架会发出BridgedCancellation追踪警告。需要取消语义时,应改用直接包含关系(SuperView/SubView +CommandsToBubbleUp)而非桥。

Notifications 与 Workflow:CWP 的运转单元

最后两个词把 CWP 的"阶段"和"通知"概念固定下来:

术语含义
Notifications在每个阶段触发以通知观察者的事件(如DrawingTextKeyDownActivatingOrientationChanging)和虚方法(如OnDrawingTextOnKeyDownOnActivatingOnOrientationChanging)。
WorkflowCWP 中一系列阶段的序列,可能是多阶段的(如View.Draw中的渲染)、线性的(如View.Keyboard中的按键处理)、按单元执行的(如View.Command中的命令执行),或事件驱动的(如Application.Keyboard的按键处理、OrientationHelper的属性变更)。

这里体现了 Terminal.Gui 的一个关键设计惯例:每个可干预的阶段都成对暴露"事件 + 虚方法"。事件面向外部订阅者(松耦合),虚方法面向子类重写(继承式扩展),且调用顺序固定为虚方法在前、事件在后,保证子类获得第一优先级。命名惯例则完全可由词表的-ing/-ed对推导:<Action>ing为可取消的前置通知,<Action>ed为不可取消的后置通知,虚方法对应为On<Action>ing/On<Action>ed

Action一词则特指"带参数调用但不返回值"的委托类型,在 Terminal.Gui 中用于简单回调——例如Shortcut.ActionOnActivated中被调用(见 command.md 的 Shortcut Dispatch 一节),是最轻量的定制点。

完整术语表速查

以下为 events-lexicon.md 中 18 个术语的完整汇总,供日常查阅:

TermMeaning
ActionA delegate type that represents a method that can be called with specific parameters but returns no value. Used for simple callbacks in Terminal.Gui.
Bridge/BridgingRouting a command across a non-containment boundary (e.g.,MenuBarItemPopoverMenu).CommandBridgesubscribes to a remote view's completion events and re-raises them on the owner withCommandRouting.Bridged.
Bubble/BubblingPropagating a commandupwardfrom a SubView to its SuperView. Opt-in viaCommandsToBubbleUp. The upward direction is always "bubble" — never "dispatch."
Cancel/Cancelling/CancelledApplies to scenarios where something can be cancelled. Changing theOrientationof aSlideris cancelable.
CancellationMechanisms to halt a phase or workflow in the Cancellable Work Pattern, such as settingCancel/Handledproperties in event arguments or returningboolfrom virtual methods.
CommandA pattern that encapsulates a request as an object, allowing for parameterization and queuing of requests.
ContextData passed to observers for informed decision-making in the Cancellable Work Pattern, such asDrawContext(drawing),Key(keyboard),ICommandContext(commands), orCancelEventArgs<Orientation>(orientation).
Default BehaviorA standard implementation for each phase in the Cancellable Work Pattern, such asDrawText(drawing),InvokeCommands(keyboard and application-level),RaiseActivating(commands), or updating a property (OrientationHelper).
Dispatch/DispatchingSending a commanddownwardfrom a SuperView to a specific SubView. The downward direction is always "dispatch" — never "bubble."DispatchDownsends with bubbling suppressed;TryDispatchToTargetusesGetDispatchTargetandConsumeDispatchto automate dispatch for composite views.
EventA notification mechanism that allows objects to communicate when something of interest occurs. Terminal.Gui uses events extensively for UI interactions.
Handle/Handling/HandledApplies to scenarios where an event can either be handled by an event listener (or override) vs not handled. Events that originate from a user action like mouse moves and key presses are examples.
InvokeThe act of calling or triggering an event, action, or method.
ListenThe act of subscribing to or registering for an event to receive notifications when it occurs.
NotificationsEvents (e.g.,DrawingText,KeyDown,Activating,OrientationChanging) and virtual methods (e.g.,OnDrawingText,OnKeyDown,OnActivating,OnOrientationChanging) raised at each phase to notify observers in the Cancellable Work Pattern.
RaiseThe act of triggering an event, notifying all registered event handlers that the event has occurred.
RoutingTheCommandRoutingenum describes how a command is being routed:Direct(local invocation),BubblingUp(upward to SuperView),DispatchingDown(downward to SubView), orBridged(across non-containment boundary). Carried onICommandContext.Routing.
WorkflowA sequence of phases in the Cancellable Work Pattern, which may be multi-phase (e.g., rendering inView.Draw), linear (e.g., key processing inView.Keyboard), per-unit (e.g., command execution inView.Command), or event-driven (e.g., key handling inApplication.Keyboard, property changes inOrientationHelper).

继续深入:相关文档与源码索引

掌握词表之后,按以下路径深入阅读效果最佳:

  • 事件深度指南:CWP 在 Terminal.Gui 中的具体实现配方——含-ing/-ed事件取舍规则、CWPPropertyHelper/CWPWorkflowHelper四个 Recipe、事件参数类型表、IValue<T>接口与命令上下文使用示例。
  • 命令深度指南:Command 路由的完整架构——含命令路由图、DefaultActivateHandler/DefaultAcceptHandler/DefaultHotKeyHandler的逐步流程、Dispatch 与 Bubble 的机制、CommandBridge用法与限制、以及命令路由追踪(TraceCategory.Command)。
  • CWP 概念文档:模式的通用定义、结构组件与操作流程图,以及不依赖框架的泛化示例。
  • 总词表:除 Events 外,还收录了 Arrangement、Configuration、Drawing、Layout、Navigation、Scrolling 六类词表,是浏览整个框架术语的统一入口。

源码层面的关键落点(均已在前文引用):CommandRouting.cs、ICommandContext.cs、CommandBridge.cs、CWPPropertyHelper.cs、CWPWorkflowHelper.cs、ValueChangingEventArgs.cs、ValueChangedEventArgs.cs、ResultEventArgs.cs、CancelEventArgs.cs。对照词表阅读这些文件,即可在几分钟内建立对 Terminal.Gui 事件系统"一词一实现"的完整认知。

  • UI组件
  • 跨平台
  • 桌面应用

【免费下载链接】Terminal.Gui

Cross Platform Terminal UI toolkit for .NET

项目地址:https://gitcode.com/gh_mirrors/te/Terminal.Gui
点击查看免费下载

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询