2024-07-12
한어Русский языкEnglishFrançaisIndonesianSanskrit日本語DeutschPortuguêsΕλληνικάespañolItalianoSuomalainenLatina
This article uses the WPF Gallery project to learn the concepts of dependency injection and how to perform dependency injection in WPF.
Dependency Injection (DI) is a design pattern used to implement the Inversion of Control (IoC) principle. The main purpose of dependency injection is to transfer the creation of objects and the management of dependencies between objects from within the object to an external container or framework, thereby improving the maintainability, testability and flexibility of the code.
Core Concepts of Dependency Injection
Types of Dependency Injection
Constructor Injection: Dependent objects are passed through the class constructor.
public class OrderService
{
private readonly IProductRepository _productRepository;
public OrderService(IProductRepository productRepository)
{
_productRepository = productRepository;
}
}
Property Injection: Dependent objects are passed through the public properties of the class.
public class OrderService
{
public IProductRepository ProductRepository { get; set; }
}
Method Injection: The dependent objects are passed through the class method parameters.
public class OrderService
{
public void ProcessOrder(IProductRepository productRepository)
{
// 使用 productRepository 处理订单
}
}
Dependency Injection (DI) is a design pattern that allows objects to be created and managed from dependencies between objects to external containers or frameworks. There are several important reasons and advantages for performing dependency injection:
This article uses the WPF Gallery project to learn how to use dependency injection in WPF. The code address is:
https://github.com/microsoft/WPF-Samples/blob/main/SampleApplications/WPFGallery
This project implements dependency injection using these two packages:
First, check the content in App.xaml.cs:
public partial class App : Application
{
private static readonly IHost _host = Host.CreateDefaultBuilder()
.ConfigureServices((context, services) =>
{
services.AddSingleton<INavigationService, NavigationService>();
services.AddSingleton<MainWindow>();
services.AddSingleton<MainWindowViewModel>();
services.AddTransient<DashboardPage>();
services.AddTransient<DashboardPageViewModel>();
services.AddTransient<ButtonPage>();
services.AddTransient<ButtonPageViewModel>();
services.AddTransient<CheckBoxPage>();
services.AddTransient<CheckBoxPageViewModel>();
services.AddTransient<ComboBoxPage>();
services.AddTransient<ComboBoxPageViewModel>();
services.AddTransient<RadioButtonPage>();
services.AddTransient<RadioButtonPageViewModel>();
services.AddTransient<SliderPage>();
services.AddTransient<SliderPageViewModel>();
services.AddTransient<CalendarPage>();
services.AddTransient<CalendarPageViewModel>();
services.AddTransient<DatePickerPage>();
services.AddTransient<DatePickerPageViewModel>();
services.AddTransient<TabControlPage>();
services.AddTransient<TabControlPageViewModel>();
services.AddTransient<ProgressBarPage>();
services.AddTransient<ProgressBarPageViewModel>();
services.AddTransient<MenuPage>();
services.AddTransient<MenuPageViewModel>();
services.AddTransient<ToolTipPage>();
services.AddTransient<ToolTipPageViewModel>();
services.AddTransient<CanvasPage>();
services.AddTransient<CanvasPageViewModel>();
services.AddTransient<ExpanderPage>();
services.AddTransient<ExpanderPageViewModel>();
services.AddTransient<ImagePage>();
services.AddTransient<ImagePageViewModel>();
services.AddTransient<DataGridPage>();
services.AddTransient<DataGridPageViewModel>();
services.AddTransient<ListBoxPage>();
services.AddTransient<ListBoxPageViewModel>();
services.AddTransient<ListViewPage>();
services.AddTransient<ListViewPageViewModel>();
services.AddTransient<TreeViewPage>();
services.AddTransient<TreeViewPageViewModel>();
services.AddTransient<LabelPage>();
services.AddTransient<LabelPageViewModel>();
services.AddTransient<TextBoxPage>();
services.AddTransient<TextBoxPageViewModel>();
services.AddTransient<TextBlockPage>();
services.AddTransient<TextBlockPageViewModel>();
services.AddTransient<RichTextEditPage>();
services.AddTransient<RichTextEditPageViewModel>();
services.AddTransient<PasswordBoxPage>();
services.AddTransient<PasswordBoxPageViewModel>();
services.AddTransient<ColorsPage>();
services.AddTransient<ColorsPageViewModel>();
services.AddTransient<LayoutPage>();
services.AddTransient<LayoutPageViewModel>();
services.AddTransient<AllSamplesPage>();
services.AddTransient<AllSamplesPageViewModel>();
services.AddTransient<BasicInputPage>();
services.AddTransient<BasicInputPageViewModel>();
services.AddTransient<CollectionsPage>();
services.AddTransient<CollectionsPageViewModel>();
services.AddTransient<MediaPage>();
services.AddTransient<MediaPageViewModel>();
services.AddTransient<NavigationPage>();
services.AddTransient<NavigationPageViewModel>();
services.AddTransient<TextPage>();
services.AddTransient<TextPageViewModel>();
services.AddTransient<DateAndTimePage>();
services.AddTransient<DateAndTimePageViewModel>();
services.AddTransient<StatusAndInfoPage>();
services.AddTransient<StatusAndInfoPageViewModel>();
services.AddTransient<SamplesPage>();
services.AddTransient<SamplesPageViewModel>();
services.AddTransient<DesignGuidancePage>();
services.AddTransient<DesignGuidancePageViewModel>();
services.AddTransient<UserDashboardPage>();
services.AddTransient<UserDashboardPageViewModel>();
services.AddTransient<TypographyPage>();
services.AddTransient<TypographyPageViewModel>();
services.AddSingleton<IconsPage>();
services.AddSingleton<IconsPageViewModel>();
services.AddSingleton<SettingsPage>();
services.AddSingleton<SettingsPageViewModel>();
services.AddSingleton<AboutPage>();
services.AddSingleton<AboutPageViewModel>();
}).Build();
[STAThread]
public static void Main()
{
_host.Start();
App app = new();
app.InitializeComponent();
app.MainWindow = _host.Services.GetRequiredService<MainWindow>();
app.MainWindow.Visibility = Visibility.Visible;
app.Run();
}
}
What is IHost?
In C#,IHost
Is an interface that is used in .NET to build and configure applicationsHost
Concept of abstraction.IHost
The interface defines a collection of services and components required to start, run, and manage an application. It is commonly used in ASP.NET Core applications, but it also applies to other types of .NET applications, such as console applications or WPF programs.
IHost
Interface byHostBuilder
Class implementation that provides the creation and configurationIHost
Instance method.HostBuilder
Allows you to add various services such as logging, configuration, dependency injection container, etc., and configure the start and stop behavior of the application.
Provides convenience methods for creating Microsoft.Extensions.Hosting.IHostBuilder instances using preconfigured defaults.
Returns an IHostBuilder.
Adds a service to the container. This operation can be called multiple times and the results are cumulative.
The parameter configureDelegate means to configure the delegate of Microsoft.Extensions.DependencyInjection.IServiceCollection.
This collection will be used to construct a System.IServiceProvider.
The delegate requires two parameter types, HostBuilderContext and IServiceCollection, and has no return value.
A Lambda expression that satisfies the delegate type is passed in here.
In C#,() => {}
Is a syntax for Lambda expressions. Lambda expressions are lightweight delegate wrappers that allow you to define an anonymous method and pass it as a parameter to a method that supports delegates or expression trees.
Lambda expressions provide a concise way to define methods, and they are particularly useful when you need to pass methods as parameters to other methods.
When adding services, there are two life cycles, AddSingleton, AddTransient and AddScoped.
These methods define the lifecycle of a service, that is, how service instances are created and managed in your application.
AddSingleton
AddTransient
AddScoped
Using these services
In the Main function:
start up_host
,pass_host.Services.GetRequiredService<MainWindow>();
Get the MainWindow instance.
Take the MainWindow class as an example and look at the MainWindow constructor in MainWindow.xaml.cs:
public MainWindow(MainWindowViewModel viewModel, IServiceProvider serviceProvider, INavigationService navigationService)
{
_serviceProvider = serviceProvider;
ViewModel = viewModel;
DataContext = this;
InitializeComponent();
Toggle_TitleButtonVisibility();
_navigationService = navigationService;
_navigationService.Navigating += OnNavigating;
_navigationService.SetFrame(this.RootContentFrame);
_navigationService.Navigate(typeof(DashboardPage));
WindowChrome.SetWindowChrome(
this,
new WindowChrome
{
CaptionHeight = 50,
CornerRadius = default,
GlassFrameThickness = new Thickness(-1),
ResizeBorderThickness = ResizeMode == ResizeMode.NoResize ? default : new Thickness(4),
UseAeroCaptionButtons = true
}
);
this.StateChanged += MainWindow_StateChanged;
}
After removing the content not related to this topic, it looks like this:
public MainWindow(MainWindowViewModel viewModel, IServiceProvider serviceProvider, INavigationService navigationService)
{
_serviceProvider = serviceProvider;
ViewModel = viewModel;
_navigationService = navigationService;
}
Have you noticed that you don’t need to create these objects yourself? The creation of these objects is managed by the dependency injection container. When these objects are needed, they can be injected through the constructor as we do now.
Without dependency injection, it might look like this:
public MainWindow()
{
_serviceProvider = new IServiceProvider();
ViewModel = new MainWindowViewModel();
_navigationService = new INavigationService();
}
This article first introduces the concept of dependency injection, then explains why dependency injection is needed, and finally learns how to use dependency injection in WPF through the WPF Gallery project.
1、[WPF-Samples/Sample Applications/WPFGallery at main · microsoft/WPF-Samples (github.com)](https://github.com/microsoft/WPF-Samples/tree/main/Sample Applications/WPFGallery)