【问题标题】:Dependency Injection in .NET Core 3.0 for WPF.NET Core 3.0 中的 WPF 依赖注入
【发布时间】:2019-02-26 01:55:10
【问题描述】:

我非常熟悉 ASP.NET Core 以及开箱即用的依赖注入支持。控制器可以通过在其构造函数中添加参数来要求依赖项。如何在 WPF UserControls 中实现依赖关系?我尝试向构造函数添加一个参数,但没有奏效。我喜欢 IOC 的概念,并且更愿意将其带到 WPF 中。

【问题讨论】:

  • 你可以参考这个答案。 stackoverflow.com/a/25524753/417939
  • @YantingChen 这个例子展示了使用 NInject,我希望找到一个使用 .NET Core 依赖注入的例子。

标签: c# wpf .net-core .net-core-3.0


【解决方案1】:

我最近在我的项目中遇到了这个要求,我通过这种方式解决了它。

用于 WPF 的 .NET Core 3.0 中的依赖注入。在解决方案中创建 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 类中,我使用我喜欢的任何记录器。

我已经在this GitHub repo 中添加了所有这些。


同时,有人问我如何在用户控件中使用注入。如果有人从中受益,我会提出这个解决方案。检查它here

【讨论】:

    【解决方案2】:

    在 WPF 中,您使用称为 Model-View-ViewModel(简称 MVVM)的模式。您的依赖项被注入到视图模型中(使用与您在 ASP.NET 中使用的相同的 IoC 框架,例如 AutoFac),并且您的视图(UserControls)被注册为您的视图模型的数据模板。

    这样,您围绕视图模型构建应用程序,并且视图(依赖于视图模型)被解析,就好像视图模型依赖于视图一样。视图可以通过它们的 DataContext 属性访问它们的视图模型。因此,您可以使用视图模型作为外观来向您的视图注入任何内容。

    【讨论】:

    • 好的,所以我创建了一个依赖于 ISimulatorConnection 的 ViewModel。我使用内置的 IOC 容器,ViewModel 应该如何以及在哪里构建?
    • 这没有什么魔力。在您的组合根/程序条目中,您只需新建主窗口并将其 DataContext 分配给您从 IoC 框架解决的主视图模型,即container.Resolve&lt;IMainViewModel&gt;()。自从 DI 成为主流以来,WPF 并没有得到很多维护。
    【解决方案3】:

    好问题,如果没有 xaml 中的非参数化构造函数,就无法拥有控件。如果需要,您需要从代码中对其进行实例化,但 xaml 不会调用该构造函数。

    【讨论】:

    • 对于我正在做的项目类型,从代码中实例化控件可能并不是一个坏主意。
    【解决方案4】:

    maytham-ɯɐɥʇʎɐɯ 从 App 类和 Startup 方法配置依赖注入的答案很好,除了我将 IServiceProvider 实现为属性

            public IServiceProvider ServiceProvider { get; private set; }
    

    下一步是“如何注入视图模型”?

    我不喜欢窗口构造函数中的视图模型注入(在后面的代码中) 我更简洁的解决方案是创建一个视图模型提供程序以在 xaml 中使用 为此,您可以将其实现为 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}"
    

    【讨论】:

      【解决方案5】:

      在这种情况下,您必须使用后面的代码。但是,如果真的需要在 UserControl 或从 xaml 实例化的任何其他类中具有依赖项,请三思。

      【讨论】:

        【解决方案6】:

        一般来说,你不会。您在视图模型中使用依赖注入,然后使用数据绑定将视图绑定到这些视图。

        这并不是说它不能完成。例如,MVVM Light 创建一个注入器类,然后在 App.xaml 中声明它的一个实例,这几乎与声明一个全局变量相同:

        <Application.Resources>
            <ResourceDictionary>
                <vm:ViewModelLocator x:Key="Locator" d:IsDataSource="True" xmlns:vm="clr-namespace:MyMvvmProject.ViewModel" />
            </ResourceDictionary>
        </Application.Resources>
        

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

        <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、桌面、移动,尤其是单元测试根本不会创建视图。

        (免责声明:我还没有将 .NET Core 用于 WPF,所以我在这里介绍的内容可能有点特定于 .NET,但一般原则仍然存在)。

        【讨论】:

          猜你喜欢
          • 2020-11-24
          • 1970-01-01
          • 1970-01-01
          • 2016-05-22
          • 1970-01-01
          • 2021-07-15
          • 2020-09-12
          • 2021-10-23
          • 1970-01-01
          相关资源
          最近更新 更多