33

How to define dependency injection in Winforms C#?

Interface ICategory:

public interface ICategory
{
    void Save();
}

Class CategoryRepository:

public class CategoryRepository : ICategory
{

    private readonly ApplicationDbContext _context;

    public CategoryRepository(ApplicationDbContext contex)
    {
        _context = contex;
    }
    public void Save()
    {
        _context.SaveChanges();
    }
}

Form1:

public partial class Form1 : Form
{
    private readonly  ICategury _ic;
    public Form1(ICategury ic)
    {
        InitializeComponent();
    _ic=ic
    }

    private void button1_Click(object sender, EventArgs e)
    {
    Form2 frm= new Form2();
    frm.show();
}
 }

Form2:

public partial class Form2 : Form
{
    private readonly  ICategury _ic;
    public Form2(ICategury ic)
    {
        InitializeComponent();
    _ic=ic
    }
 }

Problem?

  1. Definition of dependency injection in Program.cs

    Application.Run(new Form1());
    
  2. Definition of dependency injection at the time of Form 2 call

    Form2 frm= new Form2();
    frm.show();
    
Reza Aghaei
  • 120,393
  • 18
  • 203
  • 398
Mohsen Tarak
  • 331
  • 1
  • 3
  • 5
  • 2
    I'm not sure what is your question? If you want this to work automatically you probably have to use a DI framework for this. Or you can just pass the concrete type manually in the ctor of `new Form1(new ConcreteCategory())` – Charles Dec 24 '21 at 20:01

3 Answers3

61

How to use Dependency Injection (DI) in Windows Forms (WinForms)

To use DI in a WinForms .NET 5 or 6 you can do the following steps:

  1. Create a WinForms .NET Application

  2. Install Microsoft.Extensions.Hosting package (which gives you a bunch of useful features like DI, Logging, Configurations, and etc.)

  3. Add a new interface, IHelloService.cs:

    public interface IHelloService
    {
        string SayHello();
    }
    
  4. Add a new implementation for your service HelloService.cs:

    public class HelloService : IHelloService
    {
        public string SayHello()
        {
            return "Hello, world!";
        }
    }
    
  5. Modify the Program.cs:

    //using Microsoft.Extensions.DependencyInjection;
    static class Program
    {
        [STAThread]
        static void Main()
        {
            Application.SetHighDpiMode(HighDpiMode.SystemAware);
            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);
    
            var host = CreateHostBuilder().Build();
            ServiceProvider = host.Services;
    
            Application.Run(ServiceProvider.GetRequiredService<Form1>());
        }
        public static IServiceProvider ServiceProvider { get; private set; }
        static IHostBuilder CreateHostBuilder()
        {
            return Host.CreateDefaultBuilder()
                .ConfigureServices((context, services)=>{
                    services.AddTransient<IHelloService, HelloService>();
                    services.AddTransient<Form1>();
                });
        }
    }
    

Now you can inject IHelloService in Form1 and use it:

//using Microsoft.Extensions.DependencyInjection;
public partial class Form1 : Form
{
    private readonly IHelloService helloService;

    public Form1(IHelloService helloService)
    {
        InitializeComponent();
        this.helloService = helloService;
        MessageBox.Show(helloService.SayHello());
    }
}

If you want to show Form2 using DI, you first need to register it services.AddTransient<Form2>();, then depending to the usage of Form2, you can use either of the following options:

  • If you only need a single instance of Form2 in the whole life time of Form1, then you can inject it as a dependency to the constructor of Form1 and store the instance and show it whenever you want.

    But please pay attention: it will be initialized just once, when you open Form1 and it will not be initialized again. You also should not dispose it, because it's the only instance passed to Form1.

    public Form1(IHelloService helloService, Form2 form2)
    { 
         InitializeComponent();
             form2.ShowDialog();
    }
    
  • If you need multiple instances of Form2 or you need to initialize it multiple times, then you may get an instance of it like this:

    using (var form2 = Program.ServiceProvider.GetRequiredService<Form2>())
         form2.ShowDialog();
    
Reza Aghaei
  • 120,393
  • 18
  • 203
  • 398
  • What would `AddScoped` do in a WinForms app? How long would the object live? – Charles Dec 24 '21 at 23:03
  • 1
    @Charles I expect it work like `AddSingleton`, because of having a single scope. – Reza Aghaei Dec 24 '21 at 23:31
  • While this is technically correct (I upvoted this answer), it should be noted that WinForms doesn't have a global "form factory" that could use the container. This particular solution where the container is a public static property of the Program class suffers from the well known Service Locator antipattern. A slightly better approach would be to hide the form construction behind a stable facade and only modify its internals from the Composite Root. I could create a blog entry on that soon – Wiktor Zychla Dec 28 '21 at 16:19
  • 1
    @WiktorZychla Thanks for the feedback. I assume this answer is a good-enough start point for those who are interested to use DI in windows forms. For a real world application, more architectural consideration should be taken into account by the future readers. I also have the same idea of publishing a blog post to extend the topic a bit, but I'm not sure if I publish it very soon – Reza Aghaei Dec 28 '21 at 16:42
  • In the case of needing multiple `Form2` instances, how would you dispose `form2`? – Dan Friedman Dec 30 '21 at 23:01
  • 1
    @DanFriedman, the second bullet point is showing how you should dispose the when you show them using `ShowDialog`. But if you are going to show them using `Show` then you do not need to (and should not) dispose the instances. – Reza Aghaei Dec 31 '21 at 00:54
  • 1
    @DanFriedman, you may find my post on this topic useful: [Do I need to Dispose a Form after closing the Form?](https://stackoverflow.com/a/39501121/3110834) – Reza Aghaei Dec 31 '21 at 00:55
  • 1
    @RezaAghaei: just to your information, I've wrote a [blog entry on that](https://www.wiktorzychla.com/2022/01/winforms-dependency-injection-in-net6.html). The approach covers multiple cases, in particular, not only services but other free parameters can be required by a form constructor. Thus, the entry is too long to make an answer out of it. To the OP - please consult the blog entry as well. Regards. – Wiktor Zychla Jan 08 '22 at 21:53
  • 1
    @WiktorZychla Thanks for letting me know, I'll take a look. – Reza Aghaei Jan 10 '22 at 15:00
  • @MohsenTarak It looks like the post answers the question. It would be great if you accept the answer; it makes the answer more useful and more trusted for future readers. – Reza Aghaei Aug 02 '22 at 14:33
  • I hot this problem any help please? Unable to resolve service for type 'SMSS.EFModels.SchoolEFContext' while attempting to activate 'SMSS.Repository.Implementations.ClassRepository'.' – Akrem Hammami Aug 09 '22 at 09:47
  • 1
    @Akrem For each service to resolve, you first need to register it; For example `services.AddDbContext ....` or manually `services.AddTransient ...` – Reza Aghaei Aug 09 '22 at 09:51
  • How would you deal with this if you need this service in a user-control, which is placed on a form? I'm not talking about a dbcontext, but a service containing certain logic – Pieterjan Jun 26 '23 at 08:41
  • @Pieterjan Well, usually your user control should rely only on models and databinding; but just in case if you want to use a service inside the user control (to keep the design-time support which needs parameterless constructor) you can pass the IService as a property to your user control. You can also expose the ServiceProvider if your user controls are internal to your project. – Reza Aghaei Jun 26 '23 at 11:11
10

There's another approach than the one described by Reza in his answer. This answer of mine was originally published on my blog, however, since Stack Overflow is the primary source of information for most of us, the blog post linked in a comment below Reza's answer could easily be missed.

Here goes then. This solution is based on the Local Factory pattern.

We'll start with a form factory

public interface IFormFactory
{
    Form1 CreateForm1();
    Form2 CreateForm2();
}

public class FormFactory : IFormFactory
{
    static IFormFactory _provider;

    public static void SetProvider( IFormFactory provider )
    {
        _provider = provider;
    }

    public Form1 CreateForm1()
    {
        return _provider.CreateForm1();
    }

    public Form2 CreateForm2()
    {
        return _provider.CreateForm2();
    }
}

From now on, this factory is the primary client's interface to creating forms. The client code is no longer supposed to just call

var form1 = new Form1();

No, it's forbidden. Instead, the client should always call

var form1 = new FormFactory().CreateForm1();

(and similarily for other forms).

Note that while the factory is implemented, it doesn't do anything on its own! Instead it delegates the creation to a somehow mysterious provider which has to be injected into the factory. The idea behind this is that the provider will be injected, once, in the Composition Root which is a place in the code, close to the startup, and very high in the application stack so that all dependencies can be resolved there. So, the form factory doesn't need to know what provider will be ultimately injected into it.

This approach has a significant advantage - depending on actual requirements, different providers can be injected, for example you could have a DI-based provider (we'll write it in a moment) for an actual application and a stub provider for unit tests.

Anyway, let's have a form with a dependency:

public partial class Form1 : Form
{
    private IHelloWorldService _service;

    public Form1(IHelloWorldService service)
    {
        InitializeComponent();

        this._service = service;
    }
}

This form depends on a service and the service will be provided by the constructor. If the Form1 needs to create another form, Form2, it does it it a way we already discussed:

var form2 = new FormFactory().CreateForm2();

Things become more complicated, though, when a form needs not only dependant services but also, just some free parameters (strings, ints etc.). Normally, you'd have a constructor

public Form2( string something, int somethingElse ) ...

but now you need something more like

public Form2( ISomeService service1, IAnotherService service2, 
              string something, int somethingElse ) ...

This is something we should really take a look into. Look once again, a real-life form possibly needs

  • some parameters that are resolved by the container
  • other parameters that should be provided by the form creator (not known by the container!).

How do we handle that?

To have a complete example, let's then modify the form factory

public interface IFormFactory
{
    Form1 CreateForm1();
    Form2 CreateForm2(string something);
}

public class FormFactory : IFormFactory
{
    static IFormFactory _provider;

    public static void SetProvider( IFormFactory provider )
    {
        _provider = provider;
    }

    public Form1 CreateForm1()
    {
        return _provider.CreateForm1();
    }

    public Form2 CreateForm2(string something)
    {
        return _provider.CreateForm2(something);
    }
}

And let's see how forms are defined

public partial class Form1 : Form
{
    private IHelloWorldService _service;

    public Form1(IHelloWorldService service)
    {
        InitializeComponent();

        this._service = service;
    }

    private void button1_Click( object sender, EventArgs e )
    {
        var form2 = new FormFactory().CreateForm2("foo");
        form2.Show();
    }
}

public partial class Form2 : Form
{
    private IHelloWorldService _service;
    private string _something;

    public Form2(IHelloWorldService service, string something)
    {
        InitializeComponent();

        this._service = service;
        this._something = something;

        this.Text = something;
    }
}

Can you see a pattern here?

  • whenever a form (e.g. Form1) needs only dependand services, it's creation method in the FormFactory is empty (dependencies will be resolved by the container).
  • whenever a form (e.g. Form2) needs dependand services and other free parameters, it's creation method in the FormFactory contains a list of arguments corresponding to these free parameters (service dependencies will be resolved by the container)

Now finally to the Composition Root. Let's start with the service

public interface IHelloWorldService
{
    string DoWork();
}

public class HelloWorldServiceImpl : IHelloWorldService
{
    public string DoWork()
    {
        return "hello world service::do work";
    }
}

Note that while the interface is supposed to be somewhere down in the stack (to be recognized by everyone), the implementation is free to be provided anywhere (forms don't need reference to the implementation!). Then, follows the starting code where the form factory is finally provided and the container is set up

internal static class Program
{
    [STAThread]
    static void Main()
    {
        var formFactory = CompositionRoot();

        ApplicationConfiguration.Initialize();
        Application.Run(formFactory.CreateForm1());
    }

    static IHostBuilder CreateHostBuilder()
    {
        return Host.CreateDefaultBuilder()
            .ConfigureServices((context, services) => {
                services.AddTransient<IHelloWorldService, HelloWorldServiceImpl>();
                services.AddTransient<Form1>();
                services.AddTransient<Func<string,Form2>>(
                    container =>
                        something =>
                        {
                            var helloWorldService = 
                                container.GetRequiredService<IHelloWorldService>();
                            return new Form2(helloWorldService, something);
                        });
            });
    }

    static IFormFactory CompositionRoot()
    {
        // host
        var hostBuilder = CreateHostBuilder();
        var host = hostBuilder.Build();

        // container
        var serviceProvider = host.Services;

        // form factory
        var formFactory = new FormFactoryImpl(serviceProvider);
        FormFactory.SetProvider(formFactory);

        return formFactory;
    }
}

public class FormFactoryImpl : IFormFactory
{
    private IServiceProvider _serviceProvider;

    public FormFactoryImpl(IServiceProvider serviceProvider)
    {
        this._serviceProvider = serviceProvider;
    }

    public Form1 CreateForm1()
    {
        return _serviceProvider.GetRequiredService<Form1>();
    }

    public Form2 CreateForm2(string something)
    {
        var _form2Factory = _serviceProvider.GetRequiredService<Func<string, Form2>>();
        return _form2Factory( something );
    }
}

First note how the container is created with Host.CreateDefaultBuilder, an easy task. Then note how services are registered and how forms are registered among other services.

This is straightforward for forms that don't have any dependencies, it's just

services.AddTransient<Form1>();

However, if a form needs both services and free parameters, it's registered as ... form creation function, a Func of any free parameters that returns actual form. Take a look at this

services.AddTransient<Func<string,Form2>>(
    container =>
        something =>
        {
            var helloWorldService = container.GetRequiredService<IHelloWorldService>();
            return new Form2(helloWorldService, something);
        });

That's clever. We register a form factory function using one of registration mechanisms that itself uses a factory function (yes, a factory that uses another factory, a Factception. Feel free to take a short break if you feel lost here). Our registered function, the Func<string, Form2> has a single parameter, the something (that corresponds to the free parameter of the form constructor) but its other dependencies are resolved ... by the container (which is what we wanted).

This is why the actual form factory needs to pay attention of what it resolves. A simple form is resolved as follows

return _serviceProvider.GetRequiredService<Form1>();

where the other is resolved in two steps. We first resolve the factory function and then use the creation's method parameter to feed it to the function:

var _form2Factory = _serviceProvider.GetRequiredService<Func<string, Form2>>();
return _form2Factory( something );

And, that's it. Whenever a form is created, is either

new FormFactory().CreateForm1();

for "simple" forms (with service dependencies only) or just

new FormFactory().CreateForm2("foo");

for forms that need both service dependncies and other free parameters.

Wiktor Zychla
  • 47,367
  • 6
  • 74
  • 106
  • 2
    This is a spectacular answer which I have upvoted. I am about to try using it but I want to make one observation - the thing here that I had to read several times to get was this line of code in the form factory: "static IFormFactory _provider;" This is what allows us to register the provider once at startup and have it available to FormFactory instances which are created by individual forms through something other than DI. It took me a few times reading through before I figured that piece out. – EGP Dec 19 '22 at 14:42
  • 1
    @EGP: thanks, this is extremely handy pattern, for some reason not-that-often mentioned. – Wiktor Zychla Dec 19 '22 at 16:32
  • 1
    I think maybe it is not mentioned as often because there are other ways to do everything it does and this pattern is a little tough to follow every piece of it. But is is definitely the cleanest and most consistent with the way we do DI in a web application. I personally wish we could have something built in for Windows forms similar to MVC routes and ConfigureWebHostDefaults. I doubt that will ever happen unless a third party writes it – EGP Dec 20 '22 at 18:03
  • @WiktorZychla Incredibly useful answer. I am just implementing this in a project but cannot work out what the correct syntax should be for a form that has more than one parameter? Could you please enlighten me. Thanks – Bandito Jan 12 '23 at 15:50
  • 1
    @Bandito: You need a `Func` assuming you have 4 arguments. – Wiktor Zychla Jan 12 '23 at 15:53
  • @WiktorZychla Thanks, but what should the syntax then be for this part `container => something =>` – Bandito Jan 12 '23 at 16:09
  • 2
    @Bandito: container => ( p1, p2, p3, p4 ) => ... It's just a function of a container that creates a factory function of your arguments – Wiktor Zychla Jan 12 '23 at 17:10
  • @WiktorZychla Many thanks. That's the bit I couldn't get my head around! This has greatly improved my project. – Bandito Jan 12 '23 at 17:31
  • Wow, I am surprised this answer is so well received. It is still using service locator antipattern (except inside factory), doesnt provide much more benefits for testing (you usually build a test version of host builder in your test project anyway), and adds a ton of extra complexity for not much apparent benefit. Not mentioning that transient on Forms might cause memory leaks (applies to prev. answer too) – Tanuki Jan 30 '23 at 16:26
  • @Tanuki: it's definitely not the locator, the container is only setup in the CR and absent from the stack. Also, the benefit is clear, the approach lets both services and other free parameters be passed to forms which other approaches miss. And last, you could have provided your own approach which you haven't. Disappointing but thanks anyway. – Wiktor Zychla Jan 30 '23 at 16:40
  • @Tanuki what would be your solution for WinForms please? I'm researching how to get the most "correct" implementation for this myself. The factories mentioned so far are the only ones that don't error out. – Richard Griffiths May 02 '23 at 11:42
  • @RichardGriffiths The previous answer is closer to how I would do it. This answer is too complex for my tastes. My approach (and problem) is different: I use a background service to control the forms, instead of creating 1 form and then call the 2nd one from it. – Tanuki May 04 '23 at 13:10
5

Just wanted to add this here too as an alternative pattern for the IFormFactory. This is how I usually approach it.

The benefit is you don't need to keep changing your IFormFactory interface foreach form that you add and each set of parameters.

Load all forms when application starts and pass arguments to the show method or some other base method you can define on your forms.

internal static class Program
{
    public static IServiceProvider ServiceProvider { get; private set; }
  
    [STAThread]
    static void Main()
    {
        ApplicationConfiguration.Initialize();
        ServiceProvider = CreateHostBuilder().Build().Services;
        Application.Run(ServiceProvider.GetService<Form1>());
    }
   
    static IHostBuilder CreateHostBuilder()
    {
        return Host.CreateDefaultBuilder()
            .ConfigureServices((context, services) => {
                services.AddSingleton<IFormFactory,FormFactory>();
                services.AddSingleton<IProductRepository, ProductRepository>();

                 //Add all forms
                var forms = typeof(Program).Assembly
                .GetTypes()
                .Where(t => t.BaseType ==  typeof(Form))
                .ToList();

                forms.ForEach(form =>
                {
                    services.AddTransient(form);
                });
            });
    }
}

Form Factory

public interface IFormFactory
{
    T? Create<T>() where T : Form;
}

public class FormFactory : IFormFactory
{
    private readonly IServiceScope _scope;

    public FormFactory(IServiceScopeFactory scopeFactory) 
    {
        _scope = scopeFactory.CreateScope();
    }

    public T? Create<T>() where T : Form
    {
        return _scope.ServiceProvider.GetService<T>();
    }
} 

Form 1

public partial class Form1 : Form
{
    private readonly IFormFactory _formFactory;
    public Form1(IFormFactory formFactory)
    {
        InitializeComponent();
        _formFactory = formFactory;
    }

    private void button_Click(object sender, EventArgs e)
    {
        var form2 = _formFactory.Create<Form2>();
        form2?.Show(99);
    }
}

Form 2

public partial class Form2 : Form
{
    private readonly IProductRepository _productRepository;

    public Form2(IProductRepository productRepository)
    {
        InitializeComponent();
        _productRepository = productRepository;
    }

    public void Show(int recordId)
    {
        var product = _productRepository.GetProduct(recordId);

        //Bind your controls etc
        this.Show();
    }
}
TheBeast
  • 175
  • 2
  • 6