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.
5 Answers
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.
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.
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).