Loading

使用 Hosting 构建 WPF 程序

.NET Generic Host 是一个通用的应该程序构建方式,不只是适用于 asp.net core,可以用在任何 .NET 项目中。

具体支持的 .NET 框架可以看这里 NuGet Gallery | Microsoft.Extensions.Hosting 8.0.1

.NET Generic Host - .NET | Microsoft Learn

本文用于记录使用 .NET Generic Host 来管理 WPF(使用 Stylet)项目所需的修改,逐步更新,仅供参考。

模板代码:JasonGrass/WpfAppTemplate1: WPF + Stylet + Hosting

1 添加引用

// csproj
  <ItemGroup>
    <PackageReference Include="Stylet" Version="1.3.7" />
    <PackageReference Include="Microsoft.Extensions.Hosting" Version="9.0.0-rc.2.24473.5" />
  </ItemGroup>

2 修改 App.xaml

因为要手动使用 Main 函数,不使用默认的 App 的加载,需要将 App.xaml 从 ApplicationDefinition 修改成 Page。

// csproj
  <ItemGroup>
    <ApplicationDefinition Remove="App.xaml" />
    <Page Include="App.xaml" />
  </ItemGroup>

删除 App.xaml 中的 StartUrl,修改成 Stylet 的 ApplicationLoader

// App.xaml
<Application
    x:Class="WpfAppTemplate1.App"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="clr-namespace:WpfAppTemplate1"
    xmlns:s="https://github.com/canton7/Stylet">
    <Application.Resources>
        <s:ApplicationLoader>
            <s:ApplicationLoader.Bootstrapper>
                <local:Bootstrapper />
            </s:ApplicationLoader.Bootstrapper>
        </s:ApplicationLoader>
    </Application.Resources>
</Application>

3 App.xaml.cs

添加 Main 函数,创建 IHostBuilder 并运行,然后运行 App.

在 CreateHostBuilder 中,可以扩展所需的 hosting 相关的服务,如配置,日志等。

public partial class App : Application
{
    private static IServiceProvider? _serviceProvider;

    public static IServiceProvider ServiceProvider
    {
        get => _serviceProvider!;
        private set => _serviceProvider = 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();
    }

    public static T? GetService<T>()
        where T : class
    {
        return ServiceProvider.GetService<T>();
    }

    private static IHostBuilder CreateHostBuilder(string[] args)
    {
        var builder = Host.CreateDefaultBuilder(args)
            .ConfigureServices(serviceCollection =>
            {
                serviceCollection.AddSingleton(_ => Current.Dispatcher);
            });

        return builder;
    }
}

4 Stylet Bootstrapper

因为不在使用 Stylet 自带的 IOC 容器,而使用微软的,需要手动添加一个 MicrosoftDependencyInjectionBootstrapper

这里的代码,可以直接从官方给的模板中复制过来.

public class MicrosoftDependencyInjectionBootstrapper<TRootViewModel> : BootstrapperBase
    where TRootViewModel : class
{
    private ServiceProvider? _serviceProvider;
    private TRootViewModel? _rootViewModel;

    protected virtual TRootViewModel RootViewModel =>
        this._rootViewModel ??= (TRootViewModel)this.GetInstance(typeof(TRootViewModel));

    public IServiceProvider ServiceProvider => this._serviceProvider!;

    protected override void ConfigureBootstrapper()
    {
        var services = new ServiceCollection();
        this.DefaultConfigureIoC(services);
        this.ConfigureIoC(services);
        this._serviceProvider = services.BuildServiceProvider();
    }

    /// <summary>
    /// Carries out default configuration of the IoC container. Override if you don't want to do this
    /// </summary>
    protected virtual void DefaultConfigureIoC(IServiceCollection services)
    {
        var viewManagerConfig = new ViewManagerConfig()
        {
            ViewFactory = this.GetInstance,
            ViewAssemblies = new List<Assembly>() { this.GetType().Assembly },
        };

        services.AddSingleton<IViewManager>(new ViewManager(viewManagerConfig));
        services.AddTransient<MessageBoxView>();

        services.AddSingleton<IWindowManagerConfig>(this);
        services.AddSingleton<IWindowManager, WindowManager>();
        services.AddSingleton<IEventAggregator, EventAggregator>();

        services.AddTransient<IMessageBoxViewModel, MessageBoxViewModel>(); // Not singleton!
        // Also need a factory
        services.AddSingleton<Func<IMessageBoxViewModel>>(() => new MessageBoxViewModel());
    }

    /// <summary>
    /// Override to add your own types to the IoC container.
    /// </summary>
    protected virtual void ConfigureIoC(IServiceCollection services) { }

    public override object GetInstance(Type type)
    {
        return this.ServiceProvider.GetRequiredService(type);
    }

    protected override void Launch()
    {
        base.DisplayRootView(this.RootViewModel);
    }

    public override void Dispose()
    {
        base.Dispose();

        ScreenExtensions.TryDispose(this._rootViewModel);
        if (this._serviceProvider != null)
        {
            this._serviceProvider.Dispose();
        }
    }
}

然后新建当前项目的 Bootstrapper,在这里可以做一些 View 相关的配置和服务注册。

需要注意的是,如果使用 Stylet 默认的 IOC 容器,会自动收集所有的 View 和 ViewModel,但如果使用其他的 IOC 框架,就丢失了这个能力。

需要自己手动注册服务,如下面的

services.AddSingleton<RootViewModel>();
services.AddSingleton<RootView>();

暂时没有找到快捷的解决方案,可能需要手写一个 Analyzer 来做预编译处理,自动收集并注册依赖。当然,也可以直接使用 autofac 这样的库,但运行时加载的性能不如预编译处理。

详见: 使用 roslyn 的 Source Generator 自动完成依赖收集和注册

public class Bootstrapper : MicrosoftDependencyInjectionBootstrapper<RootViewModel>
{
    protected override void OnStart()
    {
        // This is called just after the application is started, but before the IoC container is set up.
        // Set up things like logging, etc
    }

    protected override void Configure()
    {
        // This is called after Stylet has created the IoC container, so this.Container exists, but before the
        // Root ViewModel is launched.
        // Configure your services, etc, in here
    }

    protected override void ConfigureIoC(IServiceCollection services)
    {
        base.ConfigureIoC(services);
        services.AddSingleton<RootViewModel>();
        services.AddSingleton<RootView>();
    }

    protected override void OnLaunch()
    {
        // This is called just after the root ViewModel has been launched
        // Something like a version check that displays a dialog might be launched from here
    }

    protected override void OnExit(ExitEventArgs e)
    {
        // Called on Application.Exit
    }

    protected override void OnUnhandledException(DispatcherUnhandledExceptionEventArgs e)
    {
        // Called on Application.DispatcherUnhandledException
    }
}

参考

canton7/Stylet: A very lightweight but powerful ViewModel-First MVVM framework for WPF for .NET Framework and .NET Core, inspired by Caliburn.Micro.

如何在WPF项目中使用Hosting管理应用的配置、日志、服务等_哔哩哔哩_bilibili

原文链接:https://www.cnblogs.com/jasongrass/p/18540534

posted @ 2024-11-11 20:44  J.晒太阳的猫  阅读(7)  评论(0编辑  收藏  举报