Table of Contents

English | 日本語 | 简体中文 | 繁體中文 | 한국어 |

扩展点

ZYC.Framework 的扩展大多通过模块和 Autofac 注册。模块随 Host 加载,注册服务或 UI 贡献;随后 Shell 从这些注册中组合菜单、Tab、工作区操作、状态栏项、任务栏菜单项、拖放动作和 Aspire 资源。

扩展点地图

扩展点 注册位置 运行时消费方
模块生命周期 ModuleBase.RegisterAsync, LoadAsync, AfterLoadedAsync Host 启动和模块加载器。
URI Tab ITabItemFactoryManager.RegisterFactory<T>() TabManager.InternalNavigateAsync(...)
简单视图 Tab ISimpleTabItemFactoryManager.Register(...) 内置 SimpleTabItemFactory
主菜单 IMainMenuManager, IMainMenuItemsProvider MainMenuManager 和主菜单 View。
窗口标题栏 IWindowTitleManager, IWindowTitleExtendManager WindowTitleView
工作区菜单 IWorkspaceMenuManager WorkspaceMenuView
工作区上下文菜单 Manager IWorkspaceContextMenuManager Manager 存在并提供排序;在接线上下文菜单表面时使用。
Tab 头部右键菜单 ITabItemHeaderContextMenuItemView TabItemHeaderContextMenuItemsResolver
状态栏 IStatusBarManager, IStatusBarItemsProvider StatusBarManager
任务栏菜单 ITaskbarMenuManager TaskbarContextMenu
配置/状态 IConfig, IState ModuleTools.RegisterAllFromAssembly(...)
事件 IEventAggregator 运行时发布/订阅总线。
Toast IToastManager, IToast Toast 弹出宿主。
拖放 IDropActionProvider DropOrchestrator
Aspire 资源 IExtensionResourcesProvider AspireService.Build(...)
CLI 选项 ModuleBase.RegisterCommandLineOption(...) ZYC.Framework.CLI 根命令。

模块生命周期

用模块生命周期决定每类注册放在哪里:

方法 用途
RegisterAsync(ContainerBuilder builder) 必须在容器构建前完成的 Autofac 注册。
LoadAsync(ILifetimeScope lifetimeScope) 运行时贡献,例如 Tab 工厂、菜单项、状态栏 Provider、Aspire 资源注册。
AfterLoadedAsync(ILifetimeScope lifetimeScope) 需要所有模块都加载完之后再执行的跨模块工作。
RegisterCommandLineOption(...) 模块拥有的 CLI 参数。

大多数 UI 模块只需要 LoadAsync

URI Tab

URI Tab 是暴露功能表面的主要方式。注册一个 ITabItemFactory,通常使用 ILifetimeScope 上的辅助方法:

public override Task LoadAsync(ILifetimeScope lifetimeScope)
{
    lifetimeScope.RegisterTabItemFactory<ReportsTabItemFactory>();
    return Task.CompletedTask;
}

当路由可以用 URI 部件表达时,使用 TabItemRouteAttribute。当判断需要服务、文件类型检查或更复杂策略时,覆盖 CheckUriMatchedAsync

只有在很小的单视图场景中,才使用 RegisterSimpleTabItemFactory(...),也就是一个 UserControl 可以直接打开,不需要专门的路由模型。

主菜单

根菜单内置 File、View、Tools、Extensions 和 About Provider。模块菜单项通常注册到其中一个 Provider 下:

public override Task LoadAsync(ILifetimeScope lifetimeScope)
{
    lifetimeScope.RegisterExtensionsMainMenuItem<ReportsMainMenuItem>();
    return Task.CompletedTask;
}

菜单排序先按 Anchor,再按同一组内的 PriorityMainMenuManager 会在 Anchor 组之间插入分隔符,并递归排序子项。

只有当模块需要一个包含多个子命令的父菜单时,才创建模块自己的 IMainMenuItemsProvider。如果只有一个命令,把 IMainMenuItem 注册到现有 Provider 即可。

窗口标题栏

紧凑的 command-style title-bar buttons 使用 IWindowTitleManager;更丰富的标题栏内容使用 IWindowTitleExtendManager。扩展项实现 IWindowTitleExtendItem,并返回要显示在标题栏中的 view object。

public override Task LoadAsync(ILifetimeScope lifetimeScope)
{
    lifetimeScope.Resolve<IWindowTitleExtendManager>()
        .RegisterItem<ReportsWindowTitleItem>();

    return Task.CompletedTask;
}

这个 surface 适合需要靠近 window chrome 常驻显示的 module-owned status 或 account controls。用户收藏类快捷入口应使用 quick bar items,而不是模块私有标题栏 UI。

工作区菜单

如果命令需要出现在工作区编号旁边的可见下拉菜单中,使用 IWorkspaceMenuManager

public override Task LoadAsync(ILifetimeScope lifetimeScope)
{
    lifetimeScope.Resolve<IWorkspaceMenuManager>()
        .RegisterItem<ReportsWorkspaceMenuItem>();

    return Task.CompletedTask;
}

IWorkspaceMenuItem 支持 TitleCommandSubItemsIconAnchorPriorityLocalization。当前可见的 WorkspaceMenuView 读取 IWorkspaceMenuManager.GetItems()

IWorkspaceContextMenuManager 是另一个 Manager,它按 AnchorPriority 递归排序。除非已经有上下文菜单 View 接入它,否则不要假设它的项会显示出来。

Tab 头部右键菜单

Tab 头部菜单项是注册为 ITabItemHeaderContextMenuItemView 的 WPF 菜单项 View:

[RegisterAs(typeof(ITabItemHeaderContextMenuItemView))]
internal partial class ReportsTabHeaderMenuItem :
    ITabItemHeaderContextMenuItemView
{
    public int Order => 20;
}

TabItemHeaderContextMenuItemsResolver 会解析所有已注册 View,并按 Order 排序。由于 WPF ContextMenu 是 late-bound,如果菜单项需要当前 Tab 实例,优先使用 command parameter 和已有的 ContextMenuItemBase 模式。

状态栏

状态栏扩展贡献一个 IStatusBarItemsProvider;Provider 返回一个或多个 IStatusBarItemStatusBarManager 聚合所有已注册 Provider,并按 Order 排序。

public override Task LoadAsync(ILifetimeScope lifetimeScope)
{
    lifetimeScope.Resolve<IStatusBarManager>()
        .RegisterStatusBarItemsProvider<ReportsStatusBarItemsProvider>();

    return Task.CompletedTask;
}

每个 Item 通过 StatusBarSection.LeftStatusBarSection.Right 选择显示侧。

任务栏菜单

任务栏菜单项实现 ITaskbarMenuItem,并注册到 ITaskbarMenuManager

lifetimeScope.Resolve<ITaskbarMenuManager>()
    .RegisterMenuItem(lifetimeScope.Resolve<ReportsTaskbarMenuItem>());

任务栏菜单按 Info.Anchor 分组,按 Info.Priority 排序,并递归排序子项。这个表面适合托盘/窗口级命令,不适合本应放进主菜单的功能导航。

配置与状态

任何实现 IConfigIState 的具体类型,都会在模块程序集注册期间从 settings 目录加载,并注册到 Autofac。它适合小型、可 JSON 序列化的设置和状态。

准则:

  • 用户可编辑选项放在 IConfig
  • 运行时持久化放在 IState
  • 类型保持小型,并能承受版本演进。
  • 不要把 config/state 当成大型业务数据存储。

事件与 Toast

解耦的运行时通知使用 IEventAggregator

lifetimeScope.PublishEvent(new ReportsChangedEvent());
lifetimeScope.SubscribeEvent<ReportsChangedEvent>(OnReportsChanged, onUiThread: true);

用户可见的临时反馈使用 IToastManager

toastManager.PromptMessage(ToastMessage.Info("Report exported.", localization: false));
toastManager.PromptException(exception);

事件用于协调,Toast 用于可见反馈。不要把 Toast 消息当成控制流。

拖放

拖放动作通过 IDropActionProvider 贡献。Orchestrator 会向所有 Provider 询问兼容的 DropAction,用 CanRun() 过滤,按 Id 去重,然后执行默认动作或显示选择器。

当模块需要以工作区感知的方式处理拖入的文件、路径或 Tab 负载时,使用这个扩展点。DropContext 包含目标对象、工作区 id、修饰键、屏幕坐标和取消令牌。

Aspire 资源

Aspire 扩展模块注册 IExtensionResourcesProviderAspireService.Build(...) 会解析所有 Provider,并调用每个 Provider 的 ConfigureResources(builder)

对于命令行子服务,通过 ICommandlineResourcesProvider 注册:

lifetimeScope.Resolve<ICommandlineResourcesProvider>()
    .Register(new CommandlineServiceOptions
    {
        Name = "reports-worker",
        WorkDirectory = workerDirectory,
        Command = "dotnet run"
    });

它适合由 Aspire Host 拉起的 sidecar 服务,而不是大型应用内 UI。

CLI 选项

CLI 会加载模块,并在最终确定根命令前调用 RegisterCommandLineOption(container, optionRegister)。当模块需要自己的命令行开关时使用它。

如果是完整脚手架命令,使用类似内置 zyc newzyc new-module 的显式子命令,不要把无关含义塞进普通 flag。

如何选择扩展面

目标 使用
打开功能 View URI + ITabItemFactory
添加一个顶层应用命令 现有主菜单 Provider
在一个模块父级下添加多个命令 模块自有 IMainMenuItemsProvider
添加工作区操作 IWorkspaceMenuManager
添加 Tab 头部动作 ITabItemHeaderContextMenuItemView
显示轻量运行时状态 Status bar provider
添加托盘/窗口命令 ITaskbarMenuManager
持久化小型设置或状态 IConfig / IState
协调运行时行为 IEventAggregator
显示用户反馈 IToastManager
添加拖入文件行为 IDropActionProvider
启动 sidecar 服务 Aspire resource provider