Using DI with ViewModel classes
Most of the popular MVVM frameworks today include a DI container to manage dependencies. Because .NET now includes its own DI container, we will use that one. The .NET team has incorporated the DI container that used to be bundled with ASP.NET Core. It’s both lightweight and easy to use. Luckily, this container is now available to all types of .NET projects via a NuGet package:
- Open the project from the previous chapter or use the project in the
Start
folder in the GitHub repository for this chapter. In theMyMediaCollection
project, open NuGet Package Manager and search forMicrosoft.Extensions.Hosting
:

Figure 4.1 – Microsoft’s DI NuGet package
- Select the package and install the latest stable version. After the installation completes, close the NuGet Package Manager tab and open
App.xaml.cs
. We will make a few changes here to start using the DI container.The DI container implements DI through interfaces called
IHostBuilder
andIServiceCollection
. As the names imply, they are intended to create a collection of services for the application through a shared host. However, we can add any type of class to the container. Its use is not restricted to services.IServiceCollection
builds the container, implementing theIServiceProvider
interface. In the following steps, you will add support for DI to the application. - The first thing you should do is add a
public
property to theApp
class that makes the host container available to the project:public static IHost HostContainer { get; private set; }
Here,
get
is public, but the property has aprivate set
accessor. This restricts the creation of the container to theApp
class. Don’t forget to add the requiredusing
statements to the code:using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting;
- The next step is to create a new method that initializes the container, sets it to the
public
property, and adds our first dependency:private void RegisterComponents() { HostContainer = Host.CreateDefaultBuilder() .ConfigureServices(services => { services.AddTransient<MainViewModel>(); }).Build(); }
In the new
RegisterComponents
method, we are creatingHostContainer
and its service collection, registeringMainViewModel
as a transient (one instance per container request) object, and using theBuild
method to create and return the DI container. Although it’s not strictly required, when adding multiple types to the container, it’s a good practice to add dependent objects to the service collection first. We’ll be adding more items to the container soon. - Finally, you will call
RegisterComponents
before creating the instance ofMainWindow
in theApp.OnLaunched
event handler:protected override void OnLaunched(LaunchActivatedEventArgs args) { RegisterComponents(); m_window = new MainWindow(); m_window.Activate(); }
That’s all the code needed to create and expose the DI container to the application. Now that we are delegating the creation of MainViewModel
to the container, you can remove the property that exposes a static instance of MainViewModel
from the App
class.
Using the ViewModel controlled by the container is simple. Go ahead and open MainWindow.xaml.cs
and update the ViewModel
property to remove the initialization. Then, set the value of the ViewModel
property using HostContainer.Services.GetService
from the App
class before the call to InitializeComponent
:
public MainWindow() { ViewModel = App.HostContainer.Services .GetService<MainViewModel>(); this.InitializeComponent(); } public MainViewModel ViewModel;
If you build and run the application now, it will work just as it did before. However, now our MainViewModel
instance will be registered in the App
class and managed by the container. As new models, view models, services, and other dependencies are added to the project, they can be added to the HostContainer
in the RegisterComponents
method.
We will be adding page navigation to the app later in this chapter. First, let’s discuss the event-to-command pattern.