Dependency Injection in .NET Core 3.0 for WPF

梦想的初衷 提交于 2021-02-20 05:45:22

问题


I’m quite familiar with ASP.NET Core and the support for dependency injection out of the box. Controllers can require dependencies by adding a parameter in their constructor. How can dependencies be achieved in WPF UserControls? I tried adding a parameter to the constructor, but that didn’t work. I love the IOC concept and would prefer to bring this forward to WPF.


回答1:


I have recently come across this requirement to my project and I solved it this way.

For Dependency Injection in .NET Core 3.0 for WPF. After you create a WPF Core 3 project in your solution, you need to install/add NuGet packages:

Microsoft.Extensions.DependencyInjection

In my case, I created a class called LogBase that I want to use for logging, so in your App class, add the following (and ya this is just a basic example):

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();
}

In your App.xaml, add Startup="OnStartup" so it looks like this:

<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>

So in your MainWindow.xaml.cs, you inject ILogBase in the constructor like this:

private readonly ILogBase _log;

public MainWindow(ILogBase log)
{
    _log = log;

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

In my LogBase class, I use any logger I like in my way.

I have added all this together in this GitHub repo.


Meanwhile, I have been asked how to use injection inside user control. I come up with this solution if some one get the benefit of it. Check it here.




回答2:


In WPF, you use a pattern called Model-View-ViewModel (MVVM for short). Your dependencies are injected into the view-model (using same IoC frameworks as you use in ASP.NET, e.g. AutoFac) and your views (UserControls) are registered as data templates to your view-models.

In that way, you structure your application around view-models and the views (that depend on the view models) are resolved as if the view-model depended on the view. Views may access their view-model through their DataContext property. So you can use the view-model as a facade to inject whatever to your views.




回答3:


Good question, one cannot have controls without non parameterized constructor in xaml. If you want, you need to instantiate it from code, but xaml won't call that constructor.




回答4:


You have to use code behind in that case. But think twice if it is really necessary to have a dependency in UserControl or any other class instantiated from xaml.




回答5:


Generally speaking, you don't. You use dependency injection in your view models and you then use data binding to bind your views to those instead.

That's not to say it can't be done. MVVM Light, for example, creates an injector class and then declares an instance of it in App.xaml, which is almost the same as declaring a global variable:

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

Windows and user controls that are part of the visual tree can bind to application resources, so in that framework, the main window typically binds to the view model like so:

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

...where Main is a property of the locator class:

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

This is not very good IoC because it puts all of your injectables in a single class. In practice, you would break it up into specialized factories etc at various levels.

But seriously, don't do any of this. Use DI in your view model layer and use loose data-binding to couple it to your view. This will allow you to exploit the full power of dependency injection, partly by de-coupling it from a layer where it's not needed and partly by allowing flexibility to re-configure your dependencies for different environments i.e. web, desktop, mobile and especially unit testing where views don't get created at all.

(Disclaimer: I haven't yet used .NET Core for WPF, so what I've presented here may be a bit .NET-specific, but the general principles remain).



来源:https://stackoverflow.com/questions/54877352/dependency-injection-in-net-core-3-0-for-wpf

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!