首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >AvaloniaUI -使用基于组合根的DI系统将ViewModels注入视图的正确方法是什么?

AvaloniaUI -使用基于组合根的DI系统将ViewModels注入视图的正确方法是什么?
EN

Stack Overflow用户
提问于 2022-02-14 23:01:40
回答 1查看 2.3K关注 0票数 4

我是Avalonia/ WPF,Xaml和桌面开发的新手,所以请原谅并澄清我演示的任何相关误解。我将继续学习现有的文件,但我有一个困难的时间寻找材料,以解决问题,我被困住了。

我试图在我的Avalonia应用程序中实现一个基于构造函数注入的复合根依赖注入系统,使用推荐的MVVM模式和相关的Avalonia项目模板。我对Microsoft.Extensions.DependencyInjection包有些熟悉,所以一直在尝试使用这个系统。

在基于这个DI框架和其他框架的WPF和Avalonia教程之间,我试图拼凑出一个可行的解决方案。我认为我已经从概念上解决了一些问题,比如注册服务和ViewModels,并为这些类设置构造函数,这样框架就可以在实例化时将依赖项注入到这些类中。然而,我陷入困境的地方是如何实现视图类的构造函数注入。

我尝试将MainWindow和MainWindowViewModel注册为服务:

代码语言:javascript
运行
复制
// App.axaml.cs
public partial class App : Application
    {
        private IServiceProvider _services;
        
        public override void Initialize()
        {
            AvaloniaXamlLoader.Load(this);
        }

        
        public override void OnFrameworkInitializationCompleted()
        {
            ConfigureServiceProvider();

            if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
            {
                desktop.MainWindow = _services.GetService<MainWindow>();
            }
            
            base.OnFrameworkInitializationCompleted();
        }
        
        
        private void ConfigureServiceProvider()
        {
            var services = ConfigureServices();
            _services = services.BuildServiceProvider();
        }
        
        private static IServiceCollection ConfigureServices()
        {
            var services = new ServiceCollection();
            
            services.AddTransient<MainWindow>();
            services.AddTransient<MainWindowViewModel>();

            return services;
        }
    }

然后,目标是能够通过构造函数将MainWindowViewModel类注入MainWindow类,然后将该参数分配给MainWindow视图类的DataContext属性:

代码语言:javascript
运行
复制
// MainWindow.axaml.cs
public partial class MainWindow : Window
    {
        public MainWindow(MainWindowViewModel viewModel)
        {
            DataContext = viewModel;
            InitializeComponent();
#if DEBUG
            this.AttachDevTools();
#endif
        }

        private void InitializeComponent()
        {
            AvaloniaXamlLoader.Load(this);
        }
    }

但是,这会引发以下错误:

代码语言:javascript
运行
复制
  MainWindow.axaml(1, 2): [XAMLIL] Unable to find public constructor for type MyApp.Client:MyApp.Client.Views.MainWindow() Line 1, position 2.

如果没有无参数的构造函数,则视图似乎无法被实例化,然而,这似乎会阻止构造函数注入。

我很可能对ViewModels和视图之间的关系有一些根本性的误解。我遇到了许多示例,其中ViewModels没有在服务容器中注册,而是直接在视图构造函数中实例化并分配给DataContext属性。我宁愿避免这种做法。

同时,我遇到的每个教程都演示了将ViewModels注入到相应的视图类中,它们使用Service模式来实现,其中DI服务容器被显式传递(或作为全局对象调用),ViewModel从容器中显式解析。

有人能告诉我任何示例、源代码或教程,它们演示如何通过构造函数正确地将ViewModels注入视图吗?这是可能的吗?我是否可以在MainWindow.axaml文件中修改某些内容以启用所需的行为?谢谢你一次又一次地感谢你澄清我可能产生的任何误解。

仅供参考,以下是MainWindow标记:

代码语言:javascript
运行
复制
// MainWindow.axaml
<Window xmlns="https://github.com/avaloniaui"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:vm="using:MyApp.Client.ViewModels"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
        x:Class="MyApp.Client.Views.MainWindow"
        x:DataType="vm:MainWindowViewModel"
        x:CompileBindings="True"
        Icon="/Assets/avalonia-logo.ico"
        Title="MyApp">

    <TextBlock Text="{Binding Greeting}" HorizontalAlignment="Center" VerticalAlignment="Center"/>

</Window>
EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2022-02-15 11:53:03

视图模型通过DataContext而不是构造函数注入与视图相关联。请注意,单视图可以重用(特别是在处理虚拟化列表时)。

一般来说,您的DI不应该知道大部分视图部分,它应该只关注ViewModel和较低层。

与通过DI创建不同,视图通常通过视图定位器被绑定到ContentControl的其他视图定位,例如。

代码语言:javascript
运行
复制
<ContentControl Content="{Binding MySubViewModel} />

(您可以在avalonia.mvvm模板中找到一个简单的视图定位器,您可以根据需要对其进行调优)。当您需要从视图模型代码中显示一个新的顶级视图时,它们通常实现某种窗口管理器,它管理顶级视图,并且可以通过DI从视图模型访问。

代码语言:javascript
运行
复制
    public class ViewManager : IViewManager
    {
        private Window CreateWindowForModel(object model)
        {
            foreach (var template in Application.Current.DataTemplates)
            {
                if (template.Match(model))
                {
                    var control = template.Build(model);
                    if (control is Window w)
                        return w;
                    return new Window { Content = control };
                }
            }

            throw new KeyNotFoundException("Unable to find view for model: " + model);
        }

        public void ShowWindow(object model) => CreateWindowForModel(model).Show();
    }

然后将IViewManager实现添加到DI中。

请注意,这种方法对于所有XAML框架都是可重用的,并且可以完全重用各种平台之间的视图模型(例如,如果您想用Xamarin实现移动UI,用Avalonia实现桌面),只需几个UI特定服务。

票数 4
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/71119254

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档