Utiliser l’hôte générique .NET dans une application WPF

L’hôte générique .NET offre un moyen standardisé de configurer et d’exécuter des applications avec prise en charge intégrée de l’injection de dépendances (DI), de la configuration et de la journalisation. Les applications WPF n’incluent pas l’intégration du Générateur d’hôtes par défaut, mais vous pouvez l’ajouter. Cet article explique comment configurer l’hôte générique dans une application WPF pour injecter des services dans vos fenêtres et utiliser l’infrastructure d’hébergement .NET complète.

Prerequisites

Configurer l’hôte générique

Pour intégrer l’hôte générique à votre application WPF :

  1. Supprimez StartupUri du fichier XAML de l’application et connectez les gestionnaires d’événements Startup et Exit :

    <Application x:Class="HostBuilderApp.App"
                 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                 Startup="Application_Startup"
                 Exit="Application_Exit">
        <Application.Resources>
        </Application.Resources>
    </Application>
    
  2. Générez l’hôte dans le code-behind.

    La Application_Startup méthode crée l’hôte avec CreateApplicationBuilder, inscrit les services sur la Services propriété, démarre l’hôte et affiche la fenêtre principale :

    private async void Application_Startup(object sender, StartupEventArgs e)
    {
        var builder = Host.CreateApplicationBuilder();
    
        builder.Services.AddHostedService<SampleLifecycleService>();
        builder.Services.AddSingleton<IGreetingService, GreetingService>();
        builder.Services.AddSingleton<MainWindow>();
    
        _host = builder.Build();
    
        await _host.StartAsync();
    
        MainWindow mainWindow = _host.Services.GetRequiredService<MainWindow>();
        mainWindow.Show();
    }
    
    Private Async Sub Application_Startup(sender As Object, e As StartupEventArgs)
        Dim builder = Host.CreateApplicationBuilder()
    
        builder.Services.AddHostedService(Of SampleLifecycleService)()
        builder.Services.AddSingleton(Of IGreetingService, GreetingService)()
        builder.Services.AddSingleton(Of MainWindow)()
    
        _host = builder.Build()
    
        Await _host.StartAsync()
    
        Dim mainWindow As MainWindow = _host.Services.GetRequiredService(Of MainWindow)()
        mainWindow.Show()
    End Sub
    
  3. Arrêtez et supprimez l’hôte lorsque l’application quitte pour nettoyer les ressources :

    private async void Application_Exit(object sender, ExitEventArgs e)
    {
        using (_host)
        {
            await _host.StopAsync();
        }
    }
    
    Private Async Sub Application_Exit(sender As Object, e As ExitEventArgs)
        Using _host
            Await _host.StopAsync()
        End Using
    End Sub
    

Créer un service

La Services propriété de la section précédente inscrit les services auprès du conteneur d’adresses de domaine. Pour créer un service personnalisé :

  1. Définissez une interface de service :

    public interface IGreetingService
    {
        string GetGreeting();
    }
    
    Public Interface IGreetingService
    
        Function GetGreeting() As String
    
    End Interface
    
  2. Créez une classe qui implémente l’interface. La GreetingService classe accepte IConfiguration par injection de constructeur pour lire les valeurs à partir de appsettings.json:

    public class GreetingService(IConfiguration configuration) : IGreetingService
    {
        public string GetGreeting() =>
            configuration.GetValue<string>("GreetingMessage")
            ?? "Hello from the Generic Host!";
    }
    
    Public Class GreetingService
        Implements IGreetingService
    
        Private ReadOnly _configuration As IConfiguration
    
        Public Sub New(configuration As IConfiguration)
            _configuration = configuration
        End Sub
    
        Public Function GetGreeting() As String Implements IGreetingService.GetGreeting
            Dim message As String = _configuration.GetValue(Of String)("GreetingMessage")
    
            If String.IsNullOrEmpty(message) Then
                Return "Hello from the Generic Host!"
            End If
    
            Return message
        End Function
    
    End Class
    

Exécuter un service hébergé

L’hôte générique peut également exécuter des services en arrière-plan qui participent au cycle de vie de l’application. L’hôte appelle une IHostedService implémentation au démarrage et à l’arrêt. Pour ajouter un service hébergé :

  1. Créez une classe qui implémente IHostedService. La classe suivante enregistre dans la sortie de débogage lorsque l’hôte démarre et s’arrête.

    public class SampleLifecycleService : IHostedService
    {
        public Task StartAsync(CancellationToken cancellationToken)
        {
            System.Diagnostics.Debug.WriteLine("SampleLifecycleService: Started.");
            return Task.CompletedTask;
        }
    
        public Task StopAsync(CancellationToken cancellationToken)
        {
            System.Diagnostics.Debug.WriteLine("SampleLifecycleService: Stopped.");
            return Task.CompletedTask;
        }
    }
    
    Public Class SampleLifecycleService
        Implements IHostedService
    
        Public Function StartAsync(cancellationToken As CancellationToken) As Task Implements IHostedService.StartAsync
            System.Diagnostics.Debug.WriteLine("SampleLifecycleService: Started.")
            Return Task.CompletedTask
        End Function
    
        Public Function StopAsync(cancellationToken As CancellationToken) As Task Implements IHostedService.StopAsync
            System.Diagnostics.Debug.WriteLine("SampleLifecycleService: Stopped.")
            Return Task.CompletedTask
        End Function
    
    End Class
    
  2. Inscrivez le service avec AddHostedService sur la propriété du Services constructeur, comme le montre la méthode Application_Startup dans la section Configuration de l’hôte générique.

L’hôte appelle StartAsync lors du démarrage et StopAsync lors de l’arrêt, de sorte que la sortie du débogage s’affiche dans la fenêtre Sortie de Visual Studio.

Injecter des services dans une fenêtre

Le conteneur DI injecte des dépendances dans des fenêtres enregistrées via l'injection par le constructeur. Pour consommer des services dans une fenêtre :

  1. Ajoutez des paramètres de constructeur pour chaque service dont la fenêtre a besoin.
  2. Stockez les services injectés dans des champs privés.
  3. Utilisez les services dans les gestionnaires d’événements ou d’autres méthodes.

Le code suivant montre MainWindow accepter ILogger<MainWindow> et IGreetingService par son constructeur :

public partial class MainWindow : Window
{
    private readonly ILogger<MainWindow> _logger;
    private readonly IGreetingService _greetingService;

    public MainWindow(ILogger<MainWindow> logger, IGreetingService greetingService)
    {
        InitializeComponent();

        _logger = logger;
        _greetingService = greetingService;
    }

    private void OnGetGreetingClick(object sender, RoutedEventArgs e)
    {
        _logger.LogInformation("Get Greeting button clicked.");
        GreetingText.Text = _greetingService.GetGreeting();
    }
}
Class MainWindow

    Private ReadOnly _logger As ILogger(Of MainWindow)
    Private ReadOnly _greetingService As IGreetingService

    Public Sub New(logger As ILogger(Of MainWindow), greetingService As IGreetingService)
        InitializeComponent()

        _logger = logger
        _greetingService = greetingService
    End Sub

    Private Sub OnGetGreetingClick(sender As Object, e As RoutedEventArgs)
        _logger.LogInformation("Get Greeting button clicked.")
        GreetingText.Text = _greetingService.GetGreeting()
    End Sub

End Class

Ajouter une configuration

CreateApplicationBuilder charge appsettings.json automatiquement lorsque le fichier se trouve dans le répertoire de sortie. Pour ajouter un fichier de configuration à votre projet :

  1. Créez un appsettings.json fichier à la racine du projet avec vos valeurs de configuration :

    {
      "GreetingMessage": "Hello from the .NET Generic Host!"
    }
    
  2. Définissez la valeur CopyToOutputDirectory à PreserveNewest dans le fichier de projet afin que le fichier soit copié dans le répertoire de sortie :

    <Project Sdk="Microsoft.NET.Sdk">
    
      <PropertyGroup>
        <OutputType>WinExe</OutputType>
        <TargetFramework>net10.0-windows</TargetFramework>
        <Nullable>enable</Nullable>
        <ImplicitUsings>enable</ImplicitUsings>
        <UseWPF>true</UseWPF>
      </PropertyGroup>
    
      <ItemGroup>
        <None Update="appsettings.json">
          <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
        </None>
      </ItemGroup>
    
      <ItemGroup>
        <PackageReference Include="Microsoft.Extensions.Hosting" Version="10.0.*" />
      </ItemGroup>
    
    </Project>