.net 如何在WPF中向用户控件注入依赖项

6ioyuze2  于 2023-10-21  发布在  .NET
关注(0)|答案(7)|浏览(139)

在WPF中,透明地将依赖项(使用IOC容器)注入到用户控件的最佳方法是什么?
我假设用户控件是窗口或其他用户控件的XAML的一部分。我也认为父母(不管是谁)不应该对此负责。从父级手动注入依赖项的解决方案对我来说看起来不够干净。我想避免显式地管理组件的依赖关系,因为这会破坏IOC的概念。
是不是在创建逻辑树时引发的任何事件,这样我就可以拦截它并注入我的依赖项?
编辑:依赖关系我也指ViewModel,Controller,Presenter(无论使用什么模式)
谢谢,安德烈

x7yiwoj4

x7yiwoj41#

在WPF中处理依赖关系的最佳方法是遵循MVVM pattern
简而言之,您不会直接将依赖项注入到User Controls(View)中,而是注入到它们的DataContext(ViewModel)中。

p5cysglq

p5cysglq2#

FrameworkElement有一个Initialized事件,您可以挂接并注入依赖项。你应该测试它是否对你的场景来说来得足够早。

3phpmpom

3phpmpom3#

我这样做的方式是有一个整体的应用程序类,它将依赖项注入到你的视图模型类中(假设你使用MVVM设计模式?)-使用像Unity这样的DI容器。请参阅WPF应用程序框架(https://github.com/jbe2277/waf),其中包含您所描述的此类场景的示例。

nwlqm0z1

nwlqm0z14#

现在有了内置的DI支持,很容易实现这一点。
1.在您的应用中设置服务提供商:

using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;

public partial class App
    {
        private static readonly IHost _host = Host
            .CreateDefaultBuilder()
            .ConfigureAppConfiguration(c => { c.SetBasePath(Path.GetDirectoryName(Assembly.GetEntryAssembly()!.Location)!); })
            .ConfigureServices((context, services) =>
            {
                // register your services
                services.AddScoped<ISomething, Something>();
                services.AddScoped<MyUserControlViewModel>();
            })
            .Build();

        /// <summary>
        /// Gets registered service.
        /// </summary>
        /// <typeparam name="T">Type of the service to get.</typeparam>
        /// <returns>Instance of the service or <see langword="null"/>.</returns>
        public static T GetService<T>()
            where T : class
        {
            var service = _host.Services.GetService(typeof(T)) as T;
            return service!;
        }

        /// <summary>
        /// Occurs when the application is loading.
        /// </summary>
        private void OnStartup(object sender, StartupEventArgs e)
        {
            _host.Start();
        }

        /// <summary>
        /// Occurs when the application is closing.
        /// </summary>
        private async void OnExit(object sender, ExitEventArgs e)
        {
            await _host.StopAsync();

            _host.Dispose();
        }
    }

1.在用户控件中
请注意,您不能将VM注入到您的用户控件中,因为当您在页面中使用用户控件时,将调用无参数构造器,因此只需从服务提供程序获取它

public partial class MyUserControl : UserControl
{
    public MyUserControlViewModel ViewModel { get; }

    public OrgSwitcherControl()
    {        
        ViewModel = App.GetService<MyUserControlViewModel>();
        DataContext = this;
        InitializeComponent();
    }
}

使用页面,你可以直接注入VM(当然你也必须在服务集合中注册xaml页面)。

5ssjco0h

5ssjco0h5#

我认为唯一可行的解决方案是使用“服务模式”。你的视图模型是可注入的,但控件/窗口本身不是。因此,您可以使用IOC容器来管理VM的依赖关系,并将任何业务逻辑依赖关系(例如服务)注入到视图模型中
例如

用户控制

public partial class MyControl : UserControl
{

    public MyControl ()
    {
        InitializeComponent();       
        Vm = VmLocator.GetVm<MyControlVm >();
       
    }

    public MyControlVm Vm { get; set; }
}

视图模型

public class MyControlVm: VmBase{
    private MyDataService  _service;
    public ICommand DoWork => new ActionCommand(OnDoWork)
    MyControlVm(MyDataService service){
      _service=service;
    }
    private async Task OnDoWork(){
        await _service.DoWorkAsync();
    }
}

Bootstrap

public  static class Bootstrapper {
       public  statvoid Configure(IServiceCollection collection)
        {
    
            collection     
                .AddTransient<MyDataService >()
                .AddTransient<MyOtherDataService>()
        }
   }

有了这个约定,你可以享受DI的好处,但也有一些警告。不幸的是,我不认为有一种方法可以控制框架元素本身如何被WPF示例化并覆盖该行为
希望这有帮助

ecfsfe2w

ecfsfe2w6#

我也曾与这种思维障碍作斗争:
我也认为父母(不管是谁)不应该对此负责。
那谁会呢IoC的要点是其他东西(父,视图模型,其他东西,...)定义了依赖关系。

bmp9r5qi

bmp9r5qi7#

解决这个问题的一个可能的方法是使用“ViewModel First”方法,并使用约定而不是配置。

相关问题