- UI组件
- 跨平台
- 桌面应用
【免费下载链接】Terminal.Gui
Cross Platform Terminal UI toolkit for .NET
Terminal.Gui 的事件系统横跨Cancellable Work Pattern(可取消工作模式,CWP)、Command 命令系统与视图绘制、键盘、鼠标等众多子系统,因此官方维护了一份精确定义的"事件词表"(Events Lexicon),统一了Event、Raise、Bubble、Dispatch、Bridge等 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 关系有何不同""Handled与Cancel各自适用什么场景"等关键概念。
这套词表服务于哪三个体系
在深入词条之前,先明确这套术语的三块应用土壤,它们共同构成了 Terminal.Gui 的事件世界观:
- Cancellable Work Pattern(CWP):一种"默认执行 + 外部定制 + 可取消"的工作流模式,以事件为主、虚方法为辅。词表中的
Cancel、Cancellation、Context、Default Behavior、Notifications、Workflow均直接源于该模式,概念性定义见 cancellable-work-pattern.md。 - Command 命令系统:用
Command枚举作为用户操作的标准化词汇(Activate、Accept、HotKey、Cut、Paste、Save等 50 余个),并借助CommandRouting在视图层级中传播。词表中的Command、Dispatch、Bubble、Bridge、Routing全部属于这一体系,详见 command.md。 - 视图核心流程:绘制(
View.Draw)、键盘(View.Keyboard)、命令(View.Command)等内置工作流是 CWP 在框架中的具体应用,词表中的Event、Listen、Invoke、Raise、Handle贯穿其间。
换句话说:CWP 定义了"何时通知、如何取消",Command 体系定义了"通知什么、往哪个方向走",而词表统一了两者的话语体系。
通知的基础四词:Event / Listen / Invoke / Raise
事件机制的本质是"观察者模式":一个对象在感兴趣的事情发生时通知其他对象。词表用四个词精确区分了这一流程的不同环节:
| 术语 | 含义 |
|---|---|
| Event | 一种通知机制,允许对象在感兴趣的事情发生时相互通信。Terminal.Gui 在 UI 交互中大量使用事件。 |
| Listen | 订阅或注册某个事件以接收通知的行为。 |
| Invoke | 调用或触发一个事件、Action 或方法的行为。 |
| Raise | 触发事件、通知所有已注册的事件处理器"事件已发生"的行为。 |
在代码层面,这四个词的对应关系非常清晰:
- Event即字段形式的
event EventHandler<TEventArgs>?声明,例如View上的Accepting/Activated、Slider的OrientationChanging; - Listen即
+=订阅,例如button.Accepted += (_, _) => DoTheThing ();; - Invoke指框架内部主动调用处理逻辑,例如
View.InvokeCommand (Command.Activate); - Raise指
RaiseActivating/RaiseActivated这类专门负责触发事件的方法(词表在Notifications与Default Behavior条目中将其与虚方法并列,见下文)。
一个值得注意的实现约定(见 events.md 的 Recipe 3):虚方法默认必须是无操作(no-op),事件触发必须发生在独立的Raise*方法中,而不是虚方法内部。正确的顺序永远是:先调用虚方法(子类优先)→ 再触发事件(外部订阅者)→ 最后执行默认行为。
反馈与干预:Handle、Cancel 与 Cancellation
事件不仅用于"通知",还用于"干预"。词表用Handle和Cancel两个家族区分两种不同的干预语义:
| 术语 | 含义 |
|---|---|
| Handle/Handling/Handled | 适用于"事件可以由监听者(或重写)处理,也可以不处理"的场景。典型例子是源于用户操作的事件,如鼠标移动和按键。 |
| Cancel/Cancelling/Cancelled | 适用于"某些事情可以被取消"的场景,例如改变Slider的Orientation。 |
| 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回答"能否干预",Context与Default 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]) }两个细节值得注意:
Source是WeakReference<View>,目的是防止命令传播期间因视图被释放而产生内存泄漏。安全访问方式是args.Context?.Source?.TryGetTarget (out View? view)。Values是一条只追加的值链:每个实现了IValue的视图在命令传播时把自己的值追加进来,顺序从最内层(发起者)到最外层。Value只是Values[^1]的便捷访问器。用 LINQ 按类型搜索(ctx.Values?.FirstOrDefault (v => v is Schemes))是在深层级联中定位具体值的惯用做法。
Default Behavior:CWP 的三元结构
Default Behavior是 CWP 得以"开箱即用"的保证。CWP 的核心结构是"默认执行 + 定制 + 取消"三元组:即使没有任何外部代码介入,每个阶段也有一条标准实现路径保证系统照常运转。在 CWPPropertyHelper.ChangeProperty 中可以看到完整的默认行为链:
- 值相等 → 直接返回
false(无变更); onChanging虚方法 +changingEvent事件,任一取消则返回false;- 非空校验(
NewValue对非可空引用类型不能为 null,否则抛InvalidOperationException); doWork写入后备字段并更新相关状态(先做工作、再发 Changed 事件);onChanged虚方法 +changedEvent事件通知完成。
命令传播的四方向:Command / Dispatch / Bubble / Bridge / Routing
如果说前两节是 CWP 的"时间轴"(阶段先后),那么本节是 Command 体系的"空间轴"(传播方向)。词表用一组动词精确区分了命令在视图层级中的四种运动方式:
| 术语 | 含义 |
|---|---|
| Command | 一种将请求封装为对象的模式,允许对请求进行参数化和排队。详见 command.md。 |
| Dispatch/Dispatching | 从 SuperView向下把命令发送到特定 SubView。向下的方向永远是 "dispatch",绝不叫 "bubble"。DispatchDown发送时抑制冒泡;TryDispatchToTarget借助GetDispatchTarget和ConsumeDispatch为组合视图自动化分发。 |
| Bubble/Bubbling | 命令从 SubView向上传播到其 SuperView。通过CommandsToBubbleUp选择启用。向上的方向永远是 "bubble"——绝不叫 "dispatch"。 |
| Bridge/Bridging | 跨非包含边界路由命令(例如MenuBarItem↔PopoverMenu)。CommandBridge订阅远程视图的完成事件,并以CommandRouting.Bridged在所有者上重新触发。 |
| Routing | CommandRouting枚举描述命令的路由方式:Direct(本地调用)、BubblingUp(向上到 SuperView)、DispatchingDown(向下到 SubView)、Bridged(跨非包含边界)。由ICommandContext.Routing携带。 |
Routing:用一个判别式枚举取代两个布尔标志
词表所指的CommandRouting定义于 CommandRouting.cs。从源码注释看,它取代了旧的临时布尔标志IsBubblingUp与IsBubblingDown,把方向信息收敛为一个判别式(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拥有的SubMenu(PopoverMenu)注册在Application.Popover中,并不在 SuperView 层级里,命令无法靠 Bubble 传播。此时用 CommandBridge.cs 搭桥:
CommandBridge bridge = CommandBridge.Connect (owner, remote, Command.Accept, Command.Activate); bridge.Dispose (); // 拆除订阅桥的实现要点(均可从源码确认):
- 订阅远程视图的完成事件:
Accept→Accepted,Activate→Activated,其余命令 →CommandNotBound; - 以
CommandRouting.Bridged重新进入完整管道:桥调用的是View.InvokeCommand而非RaiseAccepted/RaiseActivated,因此会完整走RaiseAccepting/RaiseActivating → TryDispatchToTarget → TryBubbleUp → RaiseAccepted/RaiseActivated; TryDispatchToTarget对Bridged路由有守卫:防止桥接命令向下分发到所有者的 CommandView(桥是"向上"带命令,不是"向下");- 保留
Values链:Values = e.Context?.Values ?? [],远程层级累积的值对所有者的订阅者可见; - 两端都是弱引用,不会阻止 GC;桥是单向的,双向路由需要建两座桥。
一个必须牢记的桥接限制(command.md 以重要提示标注):由于桥订阅的是远程视图的事后事件(Activated/Accepted),远程侧的状态变更已经发生,所有者在Activating/Accepting中设置args.Handled = true无法撤销远程侧已发生的变更,框架会发出BridgedCancellation追踪警告。需要取消语义时,应改用直接包含关系(SuperView/SubView +CommandsToBubbleUp)而非桥。
Notifications 与 Workflow:CWP 的运转单元
最后两个词把 CWP 的"阶段"和"通知"概念固定下来:
| 术语 | 含义 |
|---|---|
| Notifications | 在每个阶段触发以通知观察者的事件(如DrawingText、KeyDown、Activating、OrientationChanging)和虚方法(如OnDrawingText、OnKeyDown、OnActivating、OnOrientationChanging)。 |
| Workflow | CWP 中一系列阶段的序列,可能是多阶段的(如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.Action在OnActivated中被调用(见 command.md 的 Shortcut Dispatch 一节),是最轻量的定制点。
完整术语表速查
以下为 events-lexicon.md 中 18 个术语的完整汇总,供日常查阅:
| Term | Meaning |
|---|---|
| Action | A 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/Bridging | Routing a command across a non-containment boundary (e.g.,MenuBarItem↔PopoverMenu).CommandBridgesubscribes to a remote view's completion events and re-raises them on the owner withCommandRouting.Bridged. |
| Bubble/Bubbling | Propagating a commandupwardfrom a SubView to its SuperView. Opt-in viaCommandsToBubbleUp. The upward direction is always "bubble" — never "dispatch." |
| Cancel/Cancelling/Cancelled | Applies to scenarios where something can be cancelled. Changing theOrientationof aSlideris cancelable. |
| Cancellation | Mechanisms to halt a phase or workflow in the Cancellable Work Pattern, such as settingCancel/Handledproperties in event arguments or returningboolfrom virtual methods. |
| Command | A pattern that encapsulates a request as an object, allowing for parameterization and queuing of requests. |
| Context | Data passed to observers for informed decision-making in the Cancellable Work Pattern, such asDrawContext(drawing),Key(keyboard),ICommandContext(commands), orCancelEventArgs<Orientation>(orientation). |
| Default Behavior | A 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/Dispatching | Sending 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. |
| Event | A notification mechanism that allows objects to communicate when something of interest occurs. Terminal.Gui uses events extensively for UI interactions. |
| Handle/Handling/Handled | Applies 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. |
| Invoke | The act of calling or triggering an event, action, or method. |
| Listen | The act of subscribing to or registering for an event to receive notifications when it occurs. |
| Notifications | Events (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. |
| Raise | The act of triggering an event, notifying all registered event handlers that the event has occurred. |
| Routing | TheCommandRoutingenum 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. |
| Workflow | A 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
相关推荐
N_m3u8DL-RE 实操教程:一条命令搞定 m3u8 下载,7 个关键命令从安装到直播定时录制
N_m3u8DL RE 实操教程:一条命令搞定 m3u8 下载,7 个关键命令从安装到直播定时录制 浏览器里只给你一个 m3u8 或 MPD 链接,你想把视频完
CLI音视频Terminal.Gui 布局词汇表与概念精解:从 Frame、Adornment 到 Viewport 的完整布局体系
Terminal.Gui 布局词汇表与概念精解:从 Frame、Adornment 到 Viewport 的完整布局体系 Terminal.Gui 的布局系统是
UI组件跨平台桌面应用ego-browser 命令找不到:PATH 配置与 ~/.local/bin 修复方案
ego browser 命令找不到:PATH 配置与 ~/.local/bin 修复方案 ➤ 最可能根因 :shell 的 PATH 中缺少 ~/.local/
AI 技能浏览器控制
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考