前往小程序,Get更优阅读体验!
立即前往
发布
社区首页 >专栏 >使用 Hosting 构建 WPF 程序 - prism 篇

使用 Hosting 构建 WPF 程序 - prism 篇

作者头像
jgrass
发布2024-12-25 18:41:30
发布2024-12-25 18:41:30
7800
代码可运行
举报
文章被收录于专栏:蔻丁杂记
运行总次数:0
代码可运行

使用 Hosting 构建 WPF 程序 - Stylet 篇 中,使用 Hosting + Stylet 的方式,构建了一个 WPF 框架, 本文用于记录使用 .NET Generic Host + Prism 构建 WPF 所需的修改,仅供参考。

示例代码:Jasongrass/Demo.AppHostPrism: WPF + Prism + Hosting

🍕 1 初始化构建

新建一个 WPF 项目,修改 .csproj 和 App.cs

代码语言:javascript
代码运行次数:0
复制
<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>    <OutputType>WinExe</OutputType>    <TargetFramework>net8.0-windows</TargetFramework>    <Nullable>enable</Nullable>    <ImplicitUsings>enable</ImplicitUsings>    <UseWPF>true</UseWPF>  </PropertyGroup>
  <ItemGroup>    <PackageReference Include="Prism.Core" Version="9.0.537" />    <PackageReference Include="Prism.Wpf" Version="9.0.537" />    <PackageReference Include="Prism.DryIoc" Version="9.0.537" />    <PackageReference Include="DryIoc.Microsoft.DependencyInjection" Version="6.2.0" />    <PackageReference Include="Microsoft.Extensions.Hosting" Version="9.0.0" />    <PackageReference Include="CommunityToolkit.Mvvm" Version="8.4.0-preview3" />  </ItemGroup>
  <ItemGroup>    <ApplicationDefinition Remove="App.xaml" />    <Page Include="App.xaml" />  </ItemGroup>
  <ItemGroup>    <Compile Update="App.startup.cs">    <DependentUpon>App.xaml</DependentUpon>    </Compile>    <Compile Update="App.static.cs">    <DependentUpon>App.xaml</DependentUpon>    </Compile>  </ItemGroup>
</Project>

App.xamlApplicationDefinition 修改为 Page,移除自启动特性,待会添加手动的 Main 函数启动。

下面这里的修改,只是增加了两个文件,App.static.csApp.startup.cs,让他们看起来属于 App.xaml,具体内容见后面。

代码语言:javascript
代码运行次数:0
复制
<ItemGroup>  <Compile Update="App.startup.cs">    <DependentUpon>App.xaml</DependentUpon>  </Compile>  <Compile Update="App.static.cs">    <DependentUpon>App.xaml</DependentUpon>  </Compile></ItemGroup>

同时,App.xaml 内的 App,需要修改成 PrismApplication

代码语言:javascript
代码运行次数:0
复制
<prism:PrismApplication    x:Class="WpfAppPrismT1.App"    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"    xmlns:local="clr-namespace:WpfAppPrismT1"    xmlns:prism="http://prismlibrary.com/">    <Application.Resources>
    </Application.Resources></prism:PrismApplication>

🍕 2 Main 方法与服务注册

以下是 App.static.cs 的内容。

代码语言:javascript
代码运行次数:0
复制
public partial class App{    private static IServiceCollection? _serviceCollection;    private static IServiceProvider? _serviceProvider;    private static IContainer? _appContainer;
    public static IServiceProvider ServiceProvider    {        get => _serviceProvider!;        private set => _serviceProvider = value;    }
    public static IContainer AppContainer    {        get        {            if (_appContainer == null)            {                throw new InvalidOperationException("App Container not init");            }            return _appContainer;        }        private set        {            if (_appContainer != null)            {                throw new InvalidOperationException("App Container had been initialized");            }            _appContainer = value;        }    }
    [STAThread]    static void Main(string[] args)    {        using IHost host = CreateHostBuilder(args).Build();        ServiceProvider = host.Services;
        host.StartAsync();
        var app = new App();        app.InitializeComponent();        app.Run();    }
    private static IHostBuilder CreateHostBuilder(string[] args)    {        var builder = Host.CreateDefaultBuilder(args)            .ConfigureServices(serviceCollection =>            {                // 在这里注册的服务,通过 ServiceProvider 和 AppContainer 都可以拿到                // 因为 prism 的相关服务,是在 App 启动之后才注册,所以通过 ServiceProvider 就拿不到,需要通过 DryIoc 的 AppContainer 去拿。                _serviceCollection = serviceCollection;                ConfigureServicesBeforeAppLaunch(serviceCollection);            });
        return builder;    }
    public static T GetService<T>()        where T : class    {        var service = AppContainer.GetService<T>();
        if (service == null)        {            throw new InvalidOperationException($"Cannot get service if {typeof(T).Name}");        }
        return service;    }}

以下是 App.xaml.cs 的内容。

代码语言:javascript
代码运行次数:0
复制
public partial class App : PrismApplication{    protected override void RegisterTypes(IContainerRegistry containerRegistry)    {        containerRegistry.RegisterForNavigation<Page1View>();        containerRegistry.RegisterForNavigation<Page2View>();    }
    protected override IContainerExtension CreateContainerExtension()    {        if (_serviceCollection == null)        {            throw new InvalidOperationException(                "Application Startup Error. Cannot found microsoft dependency injection service collection"            );        }
        ConfigureServicesWhenAppLaunch(_serviceCollection);
        var container = new DryIoc.Container(CreateContainerRules());        var newContainer = container.WithDependencyInjectionAdapter(_serviceCollection);
        AppContainer = newContainer;
        return new DryIocContainerExtension(newContainer);    }
    protected override Window CreateShell()    {        return Container.Resolve<MainWindow>();    }}

以下是 App.startup.cs 的内容。

代码语言:javascript
代码运行次数:0
复制
public partial class App{    /// <summary>    /// 在这里注册的服务,通过 ServiceProvider 和 AppContainer 都可以拿到    /// </summary>    /// <param name="services"></param>    private static void ConfigureServicesBeforeAppLaunch(IServiceCollection services)    {        services.AddSingleton(_ => Current.Dispatcher);        services.AddSingleton<IMyService, MyService>();    }
    /// <summary>    /// 在这里注册的服务,只能通过 AppContainer 拿到    /// </summary>    /// <param name="services"></param>    private void ConfigureServicesWhenAppLaunch(IServiceCollection services)    {        // 在这里注册的服务,只能通过 AppContainer 拿到        // services.AddSingleton<IMyService, MyService>();    }}

2.1 使用 Microsoft.DependencyInjection 来实现 DryIoc

一些有 IOC 支持的类库的扩展方法,可能只支持 Microsoft.DependencyInjection,并且 Microsoft Hosting 中的配置,日志等依赖,也都放在 Microsoft.DependencyInjection 中, 所以这里想要使用 Microsoft.DependencyInjection 来代替默认的 DryIoc 的实现。

DryIoc.Microsoft.DependencyInjection 这个包提供了这个支持,关键代码在 CreateContainerExtension 方法中。

使用 Prism 时,同步引入 Prism.DryIoc 这个包,Prism 相关的服务,就统一被 DryIoc 管理了。

这里需要注意的是,代码中有 Microsoft.DependencyInjection 的 ServiceProvider 和 DryIoc 的 AppContainer,实际使用时,应该使用 AppContainer。 这样才能把 Prism 以及 App 启动时注册的服务纳入其中。 在实际开发时,不应该暴露 ServiceProvider 给外部使用,容易造成误操作。

具体原因还没有深究,或许是这里的 Microsoft.DependencyInjection 替换实现有点问题?

2.2 App.startup.cs

App.startup.cs 中放了 ConfigureServicesBeforeAppLaunchConfigureServicesWhenAppLaunch 两个注册服务的入口。 在实际使用中,有其实一个就可以。这里只是为了演示两个不同的服务注册时机。

🍕 3 CommunityToolkit.Mvvm

虽然使用了 Prism,但在基础的 ViewModel 的属性通知,属性校验,RelayCommand 的书写体验和功能上,感觉还是 CommunityToolkit.Mvvm 更胜一筹。

Prism 的 ViewModel 的基类 BindableBase 其实功能很简单,不如 CommunityToolkit.Mvvm 丰富。

而且,prism 对于导航操作,View 与 ViewModel 的绑定等,并不会强制要求 ViewModel 必须继承 BindableBase 这个基类,很多扩展功能,比如 INavigationAware 等,prism 的设计也是基于接口的,而不是基于 BindableBase 内部的实现。这点就非常棒,为自定义 ViewModel 的实现留足了空间。

CommunityToolkit.MvvmPrism 可以非常好地共存。

🍕 4 参考资料

Prism框架与Microsoft.Extensions.DependencyInjection的集成使用笔记 - 非法关键字 - 博客园

github - PrismLibrary/Prism

Introduction to Prism | Prism

.NET 6.0 + WPF 使用 Prism 框架实现导航 - 小码编匠 - 博客园

Prism程序入口、View ViewModel关联、数据绑定、数据校验、cmd - AJun816 - 博客园

原文链接: https://cloud.tencent.com/developer/article/2481590

本作品采用 「署名 4.0 国际」 许可协议进行许可,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文链接。

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2024年12月3日 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 🍕 1 初始化构建
  • 🍕 2 Main 方法与服务注册
    • 2.1 使用 Microsoft.DependencyInjection 来实现 DryIoc
    • 2.2 App.startup.cs
  • 🍕 3 CommunityToolkit.Mvvm
  • 🍕 4 参考资料
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档