C#界面框架Avalonia中使用依赖注入

Avalonia

  • 定义
    Avalonia 是一个跨平台的.NET 用户界面框架。它允许开发者使用 C# 和 XAML(可扩展应用程序标记语言)来构建桌面、移动和 Web 应用程序。类似于 Windows Presentation Foundation (WPF) 和 Universal Windows Platform (UWP),但具有更广泛的平台兼容性。
  • 平台支持
    它支持 Windows、Linux、macOS 等桌面操作系统,并且通过一些额外的工作和适配,也能够用于移动设备(如 Android 和 iOS,不过在移动方面还在不断完善)和 WebAssembly,实现了真正的跨平台开发。
  • 性能方面
    Avalonia 在性能上有不错的表现。它采用了高效的渲染管道,能够快速地绘制界面元素。例如,在处理复杂的图形界面和大量数据的可视化展示时,它可以有效地利用硬件加速,减少卡顿现象。

Microsoft.Extensions.DependencyInjection

  • 概述
    Microsoft.Extensions.DependencyInjection是.NET 中的一个轻量级的依赖注入 (Dependency Injection,简称 DI)容器框架。它提供了一种在应用程序中管理对象及其依赖关系的方式,是构建可维护、可测试和松散耦合应用程序的关键组件。
    依赖注入是一种设计模式,它允许将对象的创建和其依赖对象的提供从对象本身分离出来。这使得代码更加模块化,更容易进行单元测试和替换实现。

服务生命周期(Service Lifecycle)

  • Transient(临时)
    每次从服务容器中请求一个临时服务时,都会创建一个新的服务实例。这对于无状态的服务或者每次使用都需要独立状态的服务非常有用。例如,一个处理网络请求的服务,每次请求可能都需要一个新的实例来处理不同的数据。
    假设我们有一个DataProcessor服务,它被注册为临时服务。当在不同的地方多次请求这个服务时,每次都会得到一个全新的DataProcessor实例。
  • Scoped(作用域)
    在一个作用域内,同一个服务实例会被共享。作用域通常与请求或者业务操作的范围相关。例如,在一个 Web 请求的上下文中,同一个作用域内的服务实例是相同的。
    以一个 Web API 应用为例,在处理一个 HTTP 请求的过程中,所有在该请求作用域内请求的 Scoped 服务(比如数据库连接服务)会使用同一个实例,当新的请求到来时,会创建新的服务实例。
  • Singleton(单例)
    整个应用程序生命周期内,只有一个服务实例会被创建并共享。这适用于那些需要在整个应用中保持状态一致的服务,比如全局配置服务或者共享资源管理器。
    例如,一个AppSettings服务,用于读取和管理应用程序的配置信息。如果将其注册为单例服务,那么在整个应用运行期间,所有需要访问配置信息的地方都将使用同一个AppSettings实例。

示例

安装依赖注入容器

csharp 复制代码
install-package Microsoft.Extensions.DependencyInjection

注册

在Avalonia的App.xaml的OnFrameworkInitializationCompleted事件中添加容器初始化和构建代码

  • 在 Avalonia 中,OnFrameworkInitializationCompleted是一个重要的生命周期事件。它标志着 Avalonia 框架的初始化过程已经完成,应用程序的主窗口和相关资源已经基本准备就绪。这个事件发生在应用程序启动过程的后期阶段,对于执行一些依赖于框架初始化完成后的操作非常关键。

以下示例将添加Redis和第三方Avalonia样式库SukiUI作为参考

csharp 复制代码
public MainWindow(ISukiToastManager sukiToastManager, ISukiDialogManager sukiDialogManager)

构建部分

csharp 复制代码
ServiceProvider _services;

public override void OnFrameworkInitializationCompleted()
{
    var collection = new ServiceCollection();
    collection.AddCommonServices();
    collection.AddDistributedCache();

    _services = collection.BuildServiceProvider();

    var vm = _services.GetRequiredService<MainWindowViewModel>();
    var toastService = _services.GetRequiredService<ISukiToastManager>();
    var dialogService = _services.GetRequiredService<ISukiDialogManager>();
    if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
    {
        desktop.MainWindow = _services.GetRequiredService<MainWindow>();
    }
    else if (ApplicationLifetime is ISingleViewApplicationLifetime singleViewPlatform)
    {
        singleViewPlatform.MainView = new MainWindow(toastService, dialogService)
        {
            DataContext = vm,
        };
    }
    base.OnFrameworkInitializationCompleted();
}

注册部分

csharp 复制代码
public static void AddCommonServices(this IServiceCollection collection)
{
    collection.AddSingleton<MainWindowViewModel>();
    collection.AddSingleton<MainWindow>(s => new MainWindow(
        s.GetRequiredService<ISukiToastManager>(),
        s.GetRequiredService<ISukiDialogManager>()
    )
    {
        DataContext = s.GetRequiredService<MainWindowViewModel>(),
    });
    collection.AddSingleton<ISukiToastManager>(s => DialogExManager.GetToastManager());
    collection.AddSingleton<ISukiDialogManager>(s => DialogExManager.GetDialogManager());
}

public static void AddDistributedCache(this IServiceCollection collection)
{
    var connection = ConnectionMultiplexer.Connect("127.0.0.1:6379");

    var redis = connection.GetDatabase();

    collection.AddSingleton(redis);
    collection.AddSingleton(connection);
    collection.AddSingleton<IDistributedCahce, RedisCache>(s =>
    {
        return new RedisCache(s.GetRequiredService<IDatabase>());
    });
}

public static class DialogExManager
{
    static ISukiToastManager ToastManager = new SukiToastManager();

    public static ISukiToastManager GetToastManager() => ToastManager;

    static ISukiDialogManager DialogManager = new SukiDialogManager();

    public static ISukiDialogManager GetDialogManager() => DialogManager;
}

注意: Avalonia如果启用了ViewLocator,那么ViewModel和View将会自动绑定,如下

csharp 复制代码
public class ViewLocator : IDataTemplate
{
    public Control? Build(object? param)
    {
        if (param is null)
            return null;

        var name = param.GetType().FullName!.Replace("ViewModel", "View", StringComparison.Ordinal);
        var type = Type.GetType(name);

        if (type != null)
        {
            return (Control)Activator.CreateInstance(type)!;
        }

        return new TextBlock { Text = "Not Found: " + name };
    }

    public bool Match(object? data)
    {
        return data is ViewModelBase;
    }
}

若未启用ViewLocator,则自行在打开或复制Window的时候,对DataContext进行赋值

相关推荐
唐青枫12 小时前
C#.NET Random 深入解析:随机数生成原理与最佳实践
c#·.net
永远有缘13 小时前
Java、Python、C# 和 C++ 在函数定义语法上的主要区别
java·c++·python·c#
JosieBook17 小时前
【.NET】WinForm中如何调整DataGridView控件的列宽?
.net
yue00820 小时前
C#理论学习-WinForm实践开发教程总结
开发语言·学习·c#
追逐时光者1 天前
一款基于 .NET WinForm 开源、轻量且功能强大的节点编辑器,采用纯 GDI+ 绘制无任何依赖库仅仅100+Kb
后端·.net
睡前要喝豆奶粉1 天前
多表分页联查——EF Core方式和Dapper方式
c#·.netcore
格兰芬多呼神护卫1 天前
python实现Latex格式的公式转OMML并写入word
python·c#·word
chao1898441 天前
C 文件操作全解速览
服务器·c语言·c#
月巴月巴白勺合鸟月半1 天前
一个DevExpress的Docx文件处理的Bug的解决
c#·bug
necessary6531 天前
从工行“余额归零”事件看CAP定理:当金融系统在一致性与可用性之间做出选择
分布式·金融·wpf·可用性测试