尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

WPF UI 模块接口契约全解:以 Abstractions 为核心的依赖架构与 API 边界设计

WPF UI 模块接口契约全解:以 Abstractions 为核心的依赖架构与 API 边界设计 WPF UI 模块接口契约全解以 Abstractions 为核心的依赖架构与 API 边界设计【免费下载链接】wpfuiWPF UI provides the Fluent experience in your known and loved WPF framework. Intuitive design, themes, navigation and new immersive controls. All natively and effortlessly.项目地址: https://gitcode.com/GitHub_Trending/wp/wpfui本文基于仓库文档 docs/architecture/MODULE-INTERFACES.md对应 WPF UI v4.2.0展开逐模块剖析 WPF UI 解决方案的公共 API 表面与内部实现边界并结合仓库源码验证每个模块的真实依赖关系、类型清单与接口语义。读完本文你将掌握 WPF UI 各 NuGet 包Core、Abstractions、DependencyInjection、Tray、SyntaxHighlight 等之间谁依赖谁、谁对外暴露什么、谁必须保持内部的完整契约以及如何正确使用导航抽象接口进行解耦开发。一、文档定位一份模块契约规格书在 WPF UI 这样由多个独立 NuGet 包组成的解决方案中仅靠源码很难快速回答三个问题每个模块对外承诺了哪些公共 API哪些类型只是内部实现细节模块之间的依赖方向是否可控MODULE-INTERFACES.md正是为解决这三个问题而存在。它按模块定义了Public公共vs Internal内部API 表面充当模块间通信与面向消费者的 API 契约规范。文档头部明确标注其对应版本为WPF UI v4.2.0因此本文的版本与 TFM 信息均以该版本为基准并在涉及 csproj 细节处对照仓库当前源码核实。整体依赖关系可用以下依赖图概括原文 mermaid 图从图中可以读出三个关键设计信号Wpf.Ui.Abstractions处于依赖金字塔的塔尖——Core 和 DependencyInjection 都只依赖它而它自身零外部依赖只有 Core 允许触碰 Win32 interop 与 WPF 框架内部图中标注 Internal: Win32, InteropTray、SyntaxHighlight 等卫星包只向下依赖 Core彼此不产生横向依赖ToastNotifications、FlaUI、FontMapper 被划入 Non-Distributable不随正式发布分发其中 Toast 甚至是占位实现。下文按模块逐一展开并结合源码验证每张类型表的准确性。二、Wpf.Ui.Abstractions零依赖的契约层项值NuGet 包WPF-UI.AbstractionsTFMsnet10.0、net9.0、net8.0、net462、netstandard2.1、netstandard2.0文档列示仓库 Wpf.Ui.Abstractions.csproj 实际还额外包含net481、net472即net10.0;net9.0;net8.0;net481;net472;net462;netstandard2.1;netstandard2.0依赖无零外部依赖csproj 中没有任何 PackageReference该模块完全公共——6 个公共类型无内部类型因为它存在的意义就是定义跨模块的契约表面。这 6 个类型逐一解析如下。2.1 INavigationViewPageProvider页面解析服务的抽象public interface INavigationViewPageProvider { object? GetPage(Type pageType); }定义于 INavigationViewPageProvider.cs。它把如何根据页面 Type 拿到页面实例从导航逻辑中剥离出来——默认实现走反射构造而使用 DI 时则由容器解析。这一抽象正是 WPF UI 支持依赖注入导航的关键钩子。2.2 NavigationViewPageProviderExtensions强类型便捷方法NavigationViewPageProviderExtensions.cs 提供两个泛型扩展方法GetPageTPage()调用GetPage(typeof(TPage))并作类型转换未找到时返回nullGetRequiredPageTPage()未找到时直接抛出NavigationException(${typeof(TPage)} page not found.)适用于页面必须存在的场景。2.3 NavigationException导航失败的标准异常NavigationException.cs 是sealed class提供两个构造函数仅消息以及异常 消息保留 inner exception。它被设计为跨模块共用的导航错误信号——例如上面的GetRequiredPage就会抛它。2.4 INavigableViewT视图与 ViewModel 的显式关联public interface INavigableViewout T { T ViewModel { get; } }见 Controls/INavigableView.cs。该接口用于视图的 ViewModel 与 DataContext 分离的场景导航系统可以通过ViewModel属性显式获取页面所属的 ViewModel该 ViewModel 可选实现INavigationAware以参与导航生命周期。out T的协变声明使派生视图可以向上转型为INavigableViewBaseVM。2.5 INavigationAware 与 NavigationAware导航生命周期回调Controls/INavigationAware.cs 定义了导航通知契约注意两个方法都是异步的Task OnNavigatedToAsync()——导航进入完成后触发Task OnNavigatedFromAsync()——导航离开前触发。而 Controls/NavigationAware.cs 是它的抽象基类实现为调用方提供了同步的OnNavigatedTo()/OnNavigatedFrom()虚拟钩子同时把异步方法封装为调用同步钩子后返回Task.CompletedTask。这样既统一了异步契约又免去了普通场景手写Task的负担——子类只需覆写同步方法即可。三、Wpf.UiCore唯一的集成点项值NuGet 包WPF-UITFMsnet10.0-windows、net9.0-windows、net8.0-windows、net481、net472、net462与 Wpf.Ui.csproj 完全一致依赖Wpf.Ui.AbstractionsProjectReference、Microsoft.Windows.CsWin32构建期PrivateAssetsall、System.Memorycsproj 中还可见UseWPFtrue、EnableWindowsTargetingtrue以及随包内嵌的 Fluent System Icons 字体资源FluentSystemIcons-Filled.ttf/FluentSystemIcons-Regular.ttf。Core 是唯一被允许依赖 Win32 interop 与 WPF 框架内部的模块它承载了 WPF UI 的全部主功能面。3.1 公共命名空间总览命名空间内容Wpf.UiUiApplication、服务接口与实现Wpf.Ui.Controls77 个 Fluent Design 控件NavigationView、ContentDialog、NumberBox、TabView、FluentWindow 等见 Controls 目录Wpf.Ui.AppearanceApplicationThemeManager、ApplicationAccentColorManager、SystemThemeWatcher、WindowBackgroundManagerAppearanceWpf.Ui.Converters18 个IValueConverter实现ConvertersWpf.Ui.MarkupControlsDictionary、ThemesDictionary、SymbolIconExtension、FontIconExtension、ImageIconExtensionMarkupWpf.Ui.Extensions14 个扩展方法类ExtensionsWpf.Ui.InputIRelayCommand、IRelayCommandT、RelayCommandTInput3.2 六大公共服务接口Service Facade 模式接口实现底层包装对象INavigationServiceNavigationServiceINavigationView控件IContentDialogServiceContentDialogServiceContentDialog控件ISnackbarServiceSnackbarServiceSnackbar控件IThemeServiceThemeServiceApplicationThemeManager静态类ITaskBarServiceTaskBarServiceCOMITaskbarList4INavigationWindow—承载 NavigationView 的窗口接口这是文档中最值得注意的设计服务接口全部包装具体控件或静态管理器。例如 INavigationService.cs 的注释明确写道通过INavigationViewPageProvider服务可以在 WPF UI 导航中使用依赖注入模式。其Navigate(Type)、Navigate(Type, object?)、Navigate(string)、Navigate(string, object?)、NavigateWithHierarchy等重载都把INavigationViewPageProvider视为首选路径。从源码看这种服务包装控件的机制非常直白NavigationService通过SetNavigationControl(INavigationView navigation)绑定控件实例而INavigationView接口在 Controls/NavigationView/INavigationView.cs 暴露了SetPageProviderService(INavigationViewPageProvider)实现位于 NavigationView.Navigation.cspublic void SetPageProviderService(INavigationViewPageProvider navigationViewPageProvider) _pageService navigationViewPageProvider;在实际导航时若_pageService已注入控件会优先调用它获取页面实例NavigationView.Navigation.cs否则退回到 NavigationViewActivator.cs 中的反射构造逻辑该文件甚至会在无法构造页面时提示请使用ControlsServices初始化库或使用INavigationViewPageProvider且不要启用 Cache/Precache。这一调用链清晰印证了 Abstractions 契约在 Core 内部的实际落地。3.3 Internal / 应被 Internal 的命名空间契约的灰色地带命名空间状态说明Wpf.Ui.Interop当前为 public托管 Win32 包装UnsafeNativeMethods、PInvoke建议内化Wpf.Ui.Win32当前为 public操作系统版本工具类建议内化文档给出明确的告警Wpf.Ui.Interop与Wpf.Ui.Win32暴露的是裸 P/Invoke 声明属于实现细节消费者不应依赖这些命名空间未来主版本应将其标记为internal。这一点在源码中同样可验证——Interop 目录下是PInvoke.cs、UnsafeNativeMethods.cs、UnsafeReflection.csWin32 下是Utilities.cs它们确实是面向 Win32 层的基础设施而非面向 UI 消费者的 API。四、Wpf.Ui.DependencyInjection轻量 DI 桥接层项值NuGet 包WPF-UI.DependencyInjectionTFMsnet10.0、net9.0、net8.0、net462、netstandard2.1、netstandard2.0仓库 Wpf.Ui.DependencyInjection.csproj 同样额外含net481、net472依赖Wpf.Ui.Abstractions、Microsoft.Extensions.DependencyInjection.Abstractions3.1.0公共类型仅 2 个无内部类型ServiceCollectionExtensions——ServiceCollectionExtensions.cs 提供链式扩展方法public static IServiceCollection AddNavigationViewPageProvider(this IServiceCollection services) { _ services.AddSingleton INavigationViewPageProvider, DependencyInjectionNavigationViewPageProvider (); return services; }DependencyInjectionNavigationViewPageProvider——DependencyInjectionNavigationViewPageProvider.cs 是INavigationViewPageProvider的容器实现仅一行核心逻辑public object? GetPage(Type pageType) { return serviceProvider.GetService(pageType); }注意 csproj 中它是通过ProjectReference 指向 Abstractions、而非 Core 的——这正是文档第 4 条跨模块规则DI 包只依赖 Abstractions永不依赖 Wpf.Ui的源码级印证。这条规则的意义在于DI 包保持极度轻量仅引入Microsoft.Extensions.DependencyInjection.Abstractions一个包引用并且不耦合 WPF 框架因此在非 Windows 目标netstandard2.0等上也能编译分发。五、Wpf.Ui.Tray系统托盘模块项值NuGet 包WPF-UI.TrayTFMsnet10.0-windows、net9.0-windows、net8.0-windows、net481、net472、net462依赖Wpf.Ui、System.Drawing.Common5.1 公共类型4 个类型类别说明INotifyIconService接口托盘图标管理服务契约NotifyIconService类INotifyIconService的实现NotifyIcon控件可在 XAML 中声明式使用的托盘图标控件RoutedNotifyIconEvent委托托盘图标交互事件委托源码验证INotifyIconService.cs 定义了Id、IsRegistered、TooltipText、ContextMenu、Icon等属性以及Register()、Unregister()、SetParentWindow(Window)方法NotifyIconService.cs 在内部委托给InternalNotifyIconManager完成实际注册其Register()在设置了ParentWindow时会以窗口句柄为宿主调用internalNotifyIconManager.Register(ParentWindow)。5.2 内部类型7 个类型类别说明INotifyIcon接口托盘图标操作的内部抽象TrayHandler类Shell32Shell_NotifyIconP/Invoke 封装TrayManager类托盘图标生命周期管理TrayData结构体原生托盘图标数据结构Hicon结构体图标句柄包装NotifyIconEventHandler委托内部事件处理器InternalNotifyIconManager类主题感知的托盘图标管理从目录结构可完整对应Internal/InternalNotifyIconManager.cs、Interop/Shell32.cs、Interop/User32.cs、Interop/Libraries.cs、INotifyIcon.cs、Hicon.cs、TrayData.cs、TrayHandler.cs、TrayManager.cs、NotifyIconEventHandler.cs。公共面4 类与内部实现7 类几乎 1:2这正是公共 API 精简、实现细节封闭的模块设计样板对外只暴露服务 控件 事件委托把 Shell API 交互全部封在internal。六、Wpf.Ui.SyntaxHighlight代码高亮模块项值NuGet 包WPF-UI.SyntaxHighlightTFMsnet10.0-windows、net9.0-windows、net8.0-windows、net481、net472、net462依赖Wpf.Ui6.1 公共类型2 个CodeBlock控件——见 Controls/CodeBlock.cs继承自ContentControl通过SyntaxContent依赖属性承载格式化后的代码内容并暴露ButtonCommandIRelayCommand支持控件按钮交互SyntaxHighlightDictionary——XAML 标记扩展提供语法高亮样式资源字典Markup/SyntaxHighlightDictionary.cs。6.2 内部类型2 个Highlighter——基于正则表达式的语法高亮引擎SyntaxLanguage——支持的语言标识枚举。该模块同时随包提供 Fira Code 字体资源Fonts/FiraCode-Regular.ttf与Highlighter.cs高亮实现。公共面仅 2 个类型说明它刻意把高亮怎么算锁在内部只向消费者暴露CodeBlock控件与资源字典。七、Non-Distributable 模块三个特殊存在7.1 Wpf.Ui.ToastNotifications明确标注的占位实现项值NuGet 包WPF-UI.ToastNotifications依赖无独立 stub公共类型ToastSTUB——所有方法抛NotImplementedException源码完全证实了文档描述Toast.cs 中Show()的注释写着// TODO: Implement native Toast without external libraries方法体直接throw new NotImplementedException();。文档对此给出 Warning该模块是未实现的占位符建议要么实现、要么移除见 RECOMMENDATIONS.md。对于使用者这意味着当前版本不应在正式产品中调用 WPF-UI.ToastNotifications。7.2 Wpf.Ui.FlaUIUI 自动化测试桥项值TFMsnet10.0-windows、net9.0-windows、net8.0-windows、net481依赖FlaUI.Core公共类型AutoSuggestBox——针对Wpf.Ui.Controls.AutoSuggestBox的 FlaUI 自动化元素包装它与集成测试相关仓库 tests/Wpf.Ui.Gallery.IntegrationTests 中的 UI 自动化测试如NavigationTests.cs、TitleBarTests.cs正是这类桥接类型的典型消费方。7.3 Wpf.Ui.FontMapper构建期代码生成工具项值类型构建期控制台工具非可分发的库TFMsnet10.0依赖无公共类型无Program.cs 的作用是从 Fluent System Icons 字体的 JSON 数据生成SymbolRegular与SymbolFilled两个枚举即 Controls/SymbolRegular.cs 与 Controls/SymbolFilled.cs 的来源。它在运行时不被任何项目引用只服务于图标枚举与字体字形保持同步的工程化目标。八、跨模块接口规则五条铁律文档用五条规则收束整个契约体系值得逐一展开规则 1Abstractions 是唯一的共享契约。模块间需要通信时必须经由Wpf.Ui.Abstractions的接口如INavigationViewPageProvider、INavigationAware禁止直接穿透到另一模块的具体实现类。规则 2Core 是唯一的集成点。只有Wpf.Ui允许依赖 Win32 interop 与 WPF 框架内部。这解释了为何Tray里的Shell_NotifyIconP/Invoke 是放在 Tray 自己的Interop/目录内封装、而不是下沉到 Core——但即便如此Tray 的对外契约仍然只有 4 个公共类型P/Invoke 细节全部 internal。规则 3卫星包只向下依赖。Tray与SyntaxHighlight只依赖 Core二者永不互相依赖杜绝了卫星包成环的架构腐化。规则 4DI 包只依赖 Abstractions。Wpf.Ui.DependencyInjection必须保持不依赖Wpf.Ui以维持轻量。前面已经通过 csproj 的 ProjectReference 验证过这一点。这也意味着DI 包本身不包含任何 WPF 类型因此它可以在netstandard2.0这类无 WPF 的目标框架上编译。规则 5内部命名空间不是 API 表面。Wpf.Ui.Interop与Wpf.Ui.Win32中的类型虽然是public的但在契约意义上属于实现细节。文档明确建议未来主版本将它们内化internalize避免消费者误把 P/Invoke 裸声明当作稳定 API 使用。九、对消费者与贡献者的实践启示综合文档与源码可以提炼出几条可直接落地的工程结论接入依赖注入导航的标准姿势services.AddNavigationViewPageProvider()注册容器实现 → 将INavigationService/INavigationView与页面控件对接 → 页面通过INavigationAware或继承NavigationAware接收导航生命周期。可参考仓库 samples/Wpf.Ui.Demo.Mvvm 与 src/Wpf.Ui.Gallery 中的Services/ApplicationHostService.cs的装配方式。区分稳定 API与实现细节Wpf.Ui.Controls、Wpf.Ui.Appearance、六大服务接口属于稳定面而Wpf.Ui.Interop、Wpf.Ui.Win32以及 Tray/SyntaxHighlight 的 internal 类型随时可能在不破坏主版本号的情况下变动不要直接依赖。版本与目标框架意识Abstractions/DI 因零 WPF 依赖而支持netstandard2.0/2.1Core/Tray/SyntaxHighlight 等因依赖 WPF 或 Win32 仅支持*-windows与net4x。选用包时需按目标框架匹配以 Wpf.Ui.Abstractions.csproj、Wpf.Ui.csproj 等 csproj 中的TargetFrameworks为准。关注契约演进信号ToastNotifications处于待实现或待移除状态、Interop/Win32被标记为未来应 internal——这两处都是下一主版本可能发生 breaking change 的高风险区升级时需特别留意。十、总结MODULE-INTERFACES.md虽然只是一份文档但它准确刻画了 WPF UI v4.2.0 的架构骨架以零依赖的Abstractions为契约根、以Wpf.Ui为唯一 Win32 集成点、卫星包单向向下依赖、DI 桥保持纯 Abstractions 依赖。本文结合仓库 csproj 与核心源码逐一验证了文档中的类型表、依赖关系与调用链INavigationViewPageProvider→DependencyInjectionNavigationViewPageProvider→NavigationView的SetPageProviderService→_pageService.GetPage使这份契约规格不仅可读而且可验证、可实操。对 WPF UI 的使用者而言理解这套模块边界就是理解哪些 API 可以放心使用、哪些只是实现细节、升级时该警惕什么。【免费下载链接】wpfuiWPF UI provides the Fluent experience in your known and loved WPF framework. Intuitive design, themes, navigation and new immersive controls. All natively and effortlessly.项目地址: https://gitcode.com/GitHub_Trending/wp/wpfui创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表