Edit

Use the .NET Generic Host in a Windows Forms app

The .NET Generic Host provides a standardized way to configure and run applications with built-in support for dependency injection (DI), configuration, and logging. Windows Forms apps don't include Generic Host integration by default, but you can add it. This article shows how to set up the Generic Host in a Windows Forms app to inject services into your forms.

Prerequisites

Set up the Generic Host

The setup differs slightly between C# and Visual Basic. In C#, configure the host directly in Program.cs. In Visual Basic, use the Application Framework's startup and shutdown events in ApplicationEvents.vb.

Configure the host in Program.cs alongside ApplicationConfiguration.Initialize():

  1. Call ApplicationConfiguration.Initialize() to configure WinForms defaults, including visual styles, high DPI mode, and default fonts.
  2. Build the host with CreateApplicationBuilder and register services.
  3. Start the host and resolve the main form from DI.
  4. Pass the form to Run.
  5. Stop and dispose of the host after the form closes.

The following code shows the complete Program.cs:

using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;

namespace HostBuilderApp;

static class Program
{
    [STAThread]
    static void Main()
    {
        ApplicationConfiguration.Initialize();

        var builder = Host.CreateApplicationBuilder();

        builder.Services.AddHostedService<SampleLifecycleService>();
        builder.Services.AddTransient<Form1>();
        builder.Services.AddSingleton<IGreetingService, GreetingService>();

        IHost host = builder.Build();

        host.Start();

        Form1 mainForm = host.Services.GetRequiredService<Form1>();
        Application.Run(mainForm);

        host.StopAsync().GetAwaiter().GetResult();
        host.Dispose();
    }
}

Register and consume services

With the host configured, register custom services and inject them into your forms. To create and register a service:

  1. Define a service interface:

    public interface IGreetingService
    {
        string GetGreeting();
    }
    
    Public Interface IGreetingService
        Function GetGreeting() As String
    End Interface
    
  2. Create a class that implements the interface. The GreetingService class injects IConfiguration to read the greeting message from appsettings.json:

    public class GreetingService : IGreetingService
    {
        private readonly IConfiguration _configuration;
    
        public GreetingService(IConfiguration configuration)
        {
            _configuration = configuration;
        }
    
        public string GetGreeting()
        {
            return _configuration.GetValue<string>("GreetingMessage")
                ?? "Hello, World!";
        }
    }
    
    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 message Is Nothing Then
                Return "Hello, World!"
            End If
    
            Return message
        End Function
    
    End Class
    
  3. Register the interface and implementation on the builder's Services property, as shown in the Set up the Generic Host section.

Run a hosted service

The Generic Host can also run background services that participate in the application lifecycle. Implement IHostedService to receive callbacks when the host starts and stops. To add a hosted service:

  1. Create a class that implements IHostedService. The following class writes to the debug output when the host starts and stops:

    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. Register the service with AddHostedService on the builder's Services property, as shown in the Set up the Generic Host section.

The host calls StartAsync on startup and StopAsync on shutdown, so the debug output appears in the Output window in Visual Studio.

Consume services in a form

Because Form1 is resolved from the DI container, constructor injection works directly:

  1. Add constructor parameters for each service the form needs.
  2. Store the injected services in private fields.
  3. Use the services in event handlers or other methods.
public partial class Form1 : Form
{
    private readonly ILogger<Form1> _logger;
    private readonly IGreetingService _greetingService;

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

        _logger = logger;
        _greetingService = greetingService;
    }

    private void ButtonGreet_Click(object sender, EventArgs e)
    {
        string greeting = _greetingService.GetGreeting();
        lblGreeting.Text = greeting;
        _logger.LogInformation("Greeting displayed: {Greeting}", greeting);
    }
}
Public Class Form1

    Private _logger As ILogger(Of Form1)
    Private _greetingService As IGreetingService

    Sub New(ILogger As ILogger(Of Form1), greetingService As IGreetingService)
        InitializeComponent()
        _logger = ILogger
        _greetingService = greetingService
    End Sub

    Private Sub ButtonGreet_Click(sender As Object, e As EventArgs) Handles btnGreet.Click
        Dim greeting As String = _greetingService.GetGreeting()
        lblGreeting.Text = greeting
        _logger.LogInformation("Greeting displayed: {Greeting}", greeting)
    End Sub

End Class

Add configuration

CreateApplicationBuilder automatically loads appsettings.json from the output directory. To add a configuration file:

  1. Create an appsettings.json file in the project root with your configuration values:

    {
      "GreetingMessage": "Hello from the Generic Host!"
    }
    
  2. Set CopyToOutputDirectory to PreserveNewest in the project file so appsettings.json is copied to the output directory:

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