Using Host for Dependency Injection in WPF
Runtime Environment:
- .NET 10
- Microsoft.Extensions.Hosting 10.0.5
What is Dependency Injection
Dependency injection, as the name suggests, is a design pattern that delegates the management of object dependencies to external sources and injects them when needed, replacing internal instantiation to reduce coupling.
In .NET, Host is used to manage object dependencies, along with features such as configuration management and logging. This article primarily focuses on dependency injection, without going into excessive detail about other features.
How to Use Dependency Injection in WPF
In WPF, the application entry point is App.xaml.cs. To allow Host to span the entire lifecycle of a WPF application, we need to build and start the Host here.
Prerequisites
Install the necessary NuGet package, as .NET does not include Host by default and requires manual addition.
dotnet add package Microsoft.Extensions.Hosting --version 10.0.5Modify App.xaml
Remove the default startup window and let Host manage it.
<Application x:Class="WpfApp1.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<!-- Remove the default StartupUri="MainWindow.xaml" -->
</Application>Modify App.xaml.cs
In the code-behind, register objects that require dependency injection into the Host, then build and start the Host.
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using System.Windows;
namespace WpfApp1;
public partial class App : Application
{
// Global host: the only core object for the entire application
public static IHost AppHost { get; private set; }
// Application startup event
protected override async void OnStartup(StartupEventArgs e)
{
base.OnStartup(e);
// 1. Create Host builder
var builder = Host.CreateApplicationBuilder();
// 2. Register all windows and services
ConfigureServices(builder.Services);
// 3. Build the host
AppHost = builder.Build();
// 4. Start the host (non-blocking mode)
await AppHost.StartAsync();
// 5. Get registered objects from the Host
var mw = AppHost.Services.GetRequiredService<MainWindow>();
// 6. Display the main window
mw.Show();
}
// Service registration: write all objects that need automatic creation here
private void ConfigureServices(IServiceCollection services)
{
// Register main window
services.AddSingleton<MainWindow>();
}
// Application exit event
protected override async void OnExit(ExitEventArgs e)
{
await AppHost.StopAsync();
AppHost.Dispose();
base.OnExit(e);
}
}Three Service Lifetimes Supported by Host
Regarding object dependencies, Host supports three service lifetimes:
- Transient: Creates a new instance for each request
- Scoped: Shares one instance within each scope
- Singleton: Shares one instance throughout the entire application lifetime
In the code above, the singleton lifetime is used, meaning only one instance will be created during the entire WPF application lifecycle.
// 1. Transient: creates a new object each time it's used
services.AddTransient<MyTool>();
// 2. Scoped: shares one object within a scope
services.AddScoped<MainViewModel>();
// 3. Singleton: creates only 1 object for the entire application
services.AddSingleton<MainWindow>();When registering object dependencies, you can specify an interface and its concrete implementation class. Taking transient lifetime as an example:
services.AddTransient<IMyTool, MyTool>();When detecting a need to inject a dependency of type IMyTool, it will instantiate an object of type MyTool and provide it to the requester.
Simple Usage Demonstration
Inject a ViewModel into MainWindow as its data context.
Create a New ViewModel
using System.ComponentModel;
using System.Runtime.CompilerServices;
namespace WpfApp1;
public class MainWindowViewModel : INotifyPropertyChanged
{
// ... specific content omitted ...
}Register in ConfigureServices()
services.AddSingleton<MainWindow>();
services.AddTransient<MainWindowViewModel>();Constructor Injection in MainWindow
using System.Windows;
namespace WpfApp1;
public partial class MainWindow : Window
{
public MainWindow(MainWindowViewModel viewModel)
{
InitializeComponent();
DataContext = viewModel;
}
}