.NET Core 3.0中WPF的依赖注入

19

我对ASP.NET Core以及开箱即用的依赖注入支持非常熟悉。控制器可以通过在构造函数中添加参数来请求依赖项。但是,如何在WPF UserControls中实现依赖项呢?我尝试向构造函数添加参数,但这并没有起作用。我喜欢IOC的概念,并希望将其引入到WPF中。


你可以参考这个答案。https://dev59.com/2l8e5IYBdhLWcg3wyM0r#25524753 - YantingChen
1
@YantingChen 这个例子展示了如何使用NInject,我希望能找到一个使用.NET Core依赖注入的例子。 - user842818
5个回答

45
我最近在我的项目中遇到了这个需求,并且我用这种方式解决了它。
对于在.NET Core 3.0中使用WPF的依赖注入。在你的解决方案中创建一个WPF Core 3项目后,你需要安装/添加NuGet包:
Microsoft.Extensions.DependencyInjection

在我的情况下,我创建了一个名为LogBase的类,我想用它来进行日志记录,所以在你的App类中,添加以下内容(是的,这只是一个基本的例子):
private readonly ServiceProvider _serviceProvider;

public App()
{
    var serviceCollection = new ServiceCollection();
    ConfigureServices(serviceCollection);
    _serviceProvider = serviceCollection.BuildServiceProvider();
}
    
private void ConfigureServices(IServiceCollection services)
{
    services.AddSingleton<ILogBase>(new LogBase(new FileInfo($@"C:\temp\log.txt")));
    services.AddSingleton<MainWindow>();
}
    
private void OnStartup(object sender, StartupEventArgs e)
{
    var mainWindow = _serviceProvider.GetService<MainWindow>();
    mainWindow.Show();
}

在你的App.xaml中,添加Startup="OnStartup",使其看起来像这样:
<Application x:Class="VaultDataStore.Wpf.App"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:local="clr-namespace:VaultDataStore.Wpf"
             Startup="OnStartup">
    <Application.Resources>
        
    </Application.Resources>
</Application>

所以在你的MainWindow.xaml.cs文件中,你可以像这样在构造函数中注入ILogBase:
private readonly ILogBase _log;

public MainWindow(ILogBase log)
{
    _log = log;

    ...etc.. you can use _log over all in this class

在我的LogBase类中,我可以按照自己的方式使用任何我喜欢的日志记录器。
我已经将所有这些内容都整合到了这个GitHub仓库中。
同时,有人问我如何在用户控件中使用注入。我想出了一个解决方案,如果有人从中受益,可以在这里查看

4

maytham-ɯɐɥʇʎɐɯ的回答中提到,从"App"类和"Startup"方法配置依赖注入是好的,但我实现的IServiceProvider作为属性需要改进。

        public IServiceProvider ServiceProvider { get; private set; }

下一步是“如何注入ViewModel”?
我不喜欢在窗口构造函数中(在代码后台)进行ViewModel注入。我更干净的解决方案是创建一个用于在XAML中使用的ViewModel提供程序。为此,您可以将其实现为MarkupExtension。
public class ViewModelProvider : MarkupExtension
{
    #region ctor
    public ViewModelProvider()
    {
    }

    public ViewModelProvider(Type viewModelType)
    {
        ViewModelType = viewModelType;
    }
    #endregion ctor

    #region properties
    public Type ViewModelType { get; set; }
    #endregion properties

    #region methods
    public override object ProvideValue(IServiceProvider serviceProvider)
    {
        return ((App)Application.Current).ServiceProvider.GetService(ViewModelType);
    }
    #endregion methods
}

然后在XAML中,您可以向ViewModelProvider请求要使用的ViewModel类型

DataContext="{local:ViewModelProvider local:MainViewModel}"

我实际上更喜欢这种方式,而不是得票最高的答案。 - Irwene

2

这是一个好问题,如果在XAML中没有非参数化构造函数,就不能有控件。如果你需要,你可以从代码中实例化它,但XAML不会调用该构造函数。

"Original Answer"翻译成中文是"最初的回答"。


1
对于我正在进行的项目类型来说,从代码中实例化控件可能并不是一个坏主意。 - user842818

2
在WPF中,你使用一种称为Model-View-ViewModel(简称MVVM)的模式。你的依赖项被注入到视图模型中(使用与ASP.NET相同的IoC框架,例如AutoFac),并且你的视图(UserControls)被注册为数据模板到你的视图模型中。
这样,你可以围绕视图模型构建应用程序,而视图(依赖于视图模型)则被解析为如果视图模型依赖于视图一样。视图可以通过其DataContext属性访问其视图模型。因此,你可以使用视图模型作为门面来向你的视图注入任何内容。

好的,我创建了一个具有ISimulatorConnection依赖项的ViewModel。我使用内置的IOC容器,ViewModel应该在哪里和如何构建? - user842818
这并没有什么神奇的。在你的组合根/程序入口中,你只需新建一个主窗口,并将其DataContext分配给你的主视图模型,该模型是从你的IoC框架中解析出来的,即container.Resolve<IMainViewModel>()。自从DI变得流行以来,WPF并没有得到很多维护。 - Robert Jørgensgaard Engdahl

1
通常情况下,不需要这样做。您可以在视图模型中使用依赖注入,然后使用数据绑定将视图绑定到它们。这并不是说不能这样做。例如,MVVM Light创建一个注入器类,然后在App.xaml中声明其实例,这几乎与声明全局变量相同。
<Application.Resources>
    <ResourceDictionary>
        <vm:ViewModelLocator x:Key="Locator" d:IsDataSource="True" xmlns:vm="clr-namespace:MyMvvmProject.ViewModel" />
    </ResourceDictionary>
</Application.Resources>

Windows和用户控件是可绑定应用程序资源的视觉树的一部分,因此在该框架中,主窗口通常会这样绑定到视图模型:

<Window x:Class="MyMvvmProject.MainWindow"
    ...
    DataContext="{Binding Source={StaticResource Locator}, Path=Main}">

...其中 Main 是定位器类的一个属性:

public MainViewModel Main
{
    get
    {
        return ServiceLocator.Current.GetInstance<MainViewModel>();
    }
}

这不是很好的IoC,因为它将所有可注入对象放在一个类中。在实践中,您需要将其分解为各种专业工厂等级别。但是,认真地说,请不要这样做。在您的视图模型层中使用DI,并使用松散的数据绑定将其与视图耦合。这将使您能够充分利用依赖注入的全部功能,部分原因是将其从不需要的层中解耦,另一部分原因是允许灵活地重新配置不同环境(例如Web、桌面、移动和特别是单元测试,在其中根本不创建视图)的依赖项。 (免责声明:我尚未在WPF上使用.NET Core,因此我在此提供的内容可能有点.NET特定,但一般原则仍然适用)。

那就是解决方案,只需将 ServiceProvider 放入 ViewModelLocator(我称其为 ApplicationKernel,因为我使用 Ninject 的时间)中,然后你就完成了。应用程序可以在需要在代码背后使用资源时从中检索它。 - Orace

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接