Home > .NET Core, C#, Windows Forms > Using HostBuilder, ServiceProvider and Dependency Injection with Windows Forms on .NET Core 3

Using HostBuilder, ServiceProvider and Dependency Injection with Windows Forms on .NET Core 3

09/03/2020

In the last posts we talked about how to use .NET Core 3.0 Dependency Injection and Service Provider with WPF. But also Windows Forms is supported by .NET Core 3.x, so it’s time to show how to use the same concepts of HostBuilder, Service Provider and Dependency Injection with this application model.

First of all, after creating a .NET Core Windows Forms application, we need to add the NuGet package Microsoft.Extensions.Hosting to the project. It will allow us to use HostBuilder and, moreover, it automatically imports a bunch of other required packages. Now let’s open the Program.cs file and add the following code:

private static void Main()
{
    // ...
    var host = Host.CreateDefaultBuilder()
             .ConfigureAppConfiguration((context, builder) =>
             {
                 // Add other configuration files...
                 builder.AddJsonFile("appsettings.local.json", optional: true);
             })
             .ConfigureServices((context, services) =>
             {
                 ConfigureServices(context.Configuration, services);
             })
             .ConfigureLogging(logging =>
             {
                 // Add other loggers...
             })
             .Build();

    var services = host.Services;
    var mainForm = services.GetRequiredService<MainForm>();
    Application.Run(mainForm);
}

private static void ConfigureServices(IConfiguration configuration,
    IServiceCollection services)
{
    // ...
    services.AddSingleton<MainForm>();
}

HostBuilder configuration at lines 4-18 follows the same structure we have already presented in the previous article, so refer to it for more information. After that, at lines 21 we retrive the MainForm we added in the Service Collection (line 29) and finally we start the application using the Application.Run method (line 22).

We can now run the application: everything will work as expected. And now we can leverage all the features that .NET Core 3 provides. Let’s add also a file named appsettings.json to the root folder of the project. Set its Build Action property to Content and Copy to Output Directory to Copy if newer:

{
  "AppSettings": {
    "StringSetting": "Value",
    "IntegerSetting": 42,
    "BooleanSetting": true
  }
}

This file is automatically loaded and made available to the application by the CreateDefaultBuilder method we saw before. Then, we create an AppSettings.cs file to hold configuration settings. This file will map the settings that we write in appsettings.json:

public class AppSettings
{
    public string StringSetting { get; set; }
 
    public int IntegerSetting { get; set; }
 
    public bool BooleanSetting { get; set; }
}

Moreover, create also a sample service with its interface:

public interface ISampleService
{
    string GetCurrentDate();
}
 
public class SampleService : ISampleService
{
    public string GetCurrentDate() => DateTime.Now.ToLongDateString();
}

Now we must register these services in the IoC Container, as usual:

private void ConfigureServices(IConfiguration configuration,
    IServiceCollection services)
{
    services.Configure<AppSettings>
            (configuration.GetSection(nameof(AppSettings)));

    services.AddScoped<ISampleService, SampleService>();

    //...
}

Remember that the MainForm itself is in the IoC Container. So, when we get it from the Service Provider, it will automatically be injected with all the required services. We just need to modify its constructor:

private readonly IServiceProvider serviceProvider;
private readonly ISampleService sampleService;
private readonly AppSettings settings;

public MainForm(IServiceProvider serviceProvider,
                ISampleService sampleService,
                IOptions<AppSettings> settings)
{
    InitializeComponent();

    this.serviceProvider = serviceProvider;
    this.sampleService = sampleService;
    this.settings = settings.Value;
}

Running this code, we’ll obtain a result like the following:

The .NET Core 3.0 Windows Forms application with dependecies injected

The .NET Core 3.0 Windows Forms application with dependecies injected

Finally, let’s try to add a second Form to the application and open it using a button from the Main Form. But, before doing that, remember that, at the time of writing, the Windows Forms Designer for .NET Core is available only in Visual Studio 16.5 Preview. If you haven’t it yet, you can refer to the complete sample you’ll find at the end of the article.

So, register the new form in the Service Collection (inside Program.cs file):

private static void ConfigureServices(IConfiguration configuration,
    IServiceCollection services)
{
    // ...
    services.AddTransient<SecondForm>();
}

At line 5 we register the Form as transient dependency, meaning that, everytime we try to get a reference to it, we’ll get a new instance. Of course, we can use AddScoped or AddSingleton as well. Keep in mind that, in the context of a desktop application, Scoped and Singleton get always the same instance, because in this case we have a scope as long as our application runs.

Then, in MainForm.cs add the code to open the new Form when clicking the button:

private void OpenSecondFormButton_Click(object sender, EventArgs e)
{
    var form = serviceProvider.GetRequiredService<SecondForm>();
    form.ShowDialog(this);
}

We use the ServiceProvider we passed to the MainForm constructor to get a reference to SecondForm and we call its ShowDialog method (lines 3-4). But SecondForm is itself in the IoC Container, so in turn it can receive the dependencies it needs in the constructor:

public partial class SecondForm : Form
{
    private readonly ISampleService sampleService;
    private readonly AppSettings settings;

    public SecondForm(ISampleService sampleService,
                      IOptions<AppSettings> settings)
    {
        InitializeComponent();

        this.sampleService = sampleService;
        this.settings = settings.Value;
    }
}
Opening a second Form with the Service Provider

Opening a second Form with the Service Provider

You can download the sample app using the link below:

Using HostBuilder, ServiceProvider and Dependency Injection with Windows Forms on .NET Core 3

Categories: .NET Core, C#, Windows Forms
  1. Mike Hudgell
    10/03/2020 at 12:46

    May be worth looking at https://github.com/dapplo/Dapplo.Microsoft.Extensions.Hosting which has some easy ways of using WinForms etc

  1. 10/03/2020 at 12:18
Comments are closed.