Reputation: 481
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?
Definition of dependency injection in Program.cs
Application.Run(new Form1());
Definition of dependency injection at the time of Form 2 call
Form2 frm= new Form2();
frm.show();
Upvotes: 48
Views: 46814
Reputation: 136
This is my method using .NET Core 9, I hope it will be helpful.
internal static class Program
{
[STAThread]
static void Main()
{
ApplicationConfiguration.Initialize();
var services = new ServiceCollection();
ConfigureServices(services);
var servicesProvider = services.BuildServiceProvider();
Application.Run(servicesProvider.GetRequiredService<frmMain>());
}
private static void ConfigureServices(IServiceCollection services)
{
services.AddTransient<IDoWorkService, DoWorkService>();
services.AddSingleton<frmMain>();
services.AddTransient<frm2>();
}
}
//////////////////
public partial class frmMain : Form
{
private readonly IServiceProvider serviceProvider;
public frmMain(IServiceProvider serviceProvider)
{
InitializeComponent();
this.serviceProvider = serviceProvider;
}
private void button1_Click(object sender, EventArgs e)
{
var frm2 = serviceProvider.GetRequiredService<frm2>();
frm2?.Show();
}
}
//////////////////
public partial class frm2 : Form
{
private readonly IDoWorkService doWorkService;
public frm2(IDoWorkService doWorkService)
{
InitializeComponent();
this.doWorkService = doWorkService;
}
private void btnDoWork_Click(object sender, EventArgs e)
{
string result = doWorkService.DoWhatYouWant();
MessageBox.Show(result);
}
}
Upvotes: 0
Reputation: 2605
As a complement to other answers: if the form is non-modal, you might want to Dispose
of the form's dependencies when the form closes. In my case, one of the services implemented IDisposable
so calling someForm.Show()
and not disposing them meant either they are cleaned up too early (when the caller finished) or too late (when the process ended). I solved it by using the IComponent.Disposed
event.
// Usage: Inject as `NonModalFormFactory<YourFormClass> yourFormFactory` to call `yourFormFactory.Make().Show()`.
class NonModalFormFactory<TForm>(IServiceScopeFactory ScopeFactory) where T : IComponent
{
public TForm Make(params object?[]? constructorArgs)
{
// The scope will be disposed when the form does
IServiceScope scope = ScopeFactory.CreateScope();
TForm t = ActivatorUtilities.CreateInstance<TForm>(scope.ServiceProvider, constructorArgs);
t.Disposed += T_Disposed;
return t;
void T_Disposed(object? sender, EventArgs e)
{
using (scope) { ((IComponent)sender!).Disposed -= T_Disposed; }
}
}
}
Upvotes: 0
Reputation: 51
How to use Dependency Injection in WinForms
Here is a simple explanation about how to use DI in winforms .
In modern applications , DI allows us to delegate the creation of objects and their dependencies to a container . This helps keep your code loosely coupled , more maintainable .
First you have to set up the DI container in your program.cs file . This container is responsible for creating instances of your services.
internal static class Program
{
[STAThread]
static void Main()
{
ApplicationConfiguration.Initialize();
//setting up the DI container
var services = new ServiceCollection();
//registering services
ConfigureServices(services);
//build the service provider
var servicesProvider = services.BuildServiceProvider();
ServiceProviderHolder.ServiceProvider = servicesProvider;
Application.Run(new Form1());
}
public static class ServiceProviderHolder
{
public static IServiceProvider ServiceProvider { get; set; }
}
private static void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<ScheduleDBContext>();
services.AddTransient<AddMachine>();
}
}
Now when the program.cs file runs it builds the serviceprovider which knows how to create instances of all the services that is registered , along with their dependencies .
Then when the program wants to create an instance of the addMachine form with a button click you do not have to create that instance manually because the service provider does that for you . It can identify and all the dependency's of the AddMachine class , create an instance of it including all the dependancies .
Here in Form1.cs there is a button which generates an instance of the AddMachine form . And that AddMachine class has a dependancy on ScheduleDBContext . So with the help of the DI container now we do not have to create an instance of ScheduleDBContex manually and pass it to the constructor of the AddMachine .
Here is the AddMachine class .
public partial class AddMachine : Form
{
private readonly ScheduleDBContext _scheduleDBContext;
public AddMachine(ScheduleDBContext scheduleDBContext)
{
_scheduleDBContext = scheduleDBContext;
InitializeComponent();
}
And the button click event on form1 which creates an instance of the AddMachine
private void button1_Click(object sender, EventArgs e)
{
var addMachineForm = ServiceProviderHolder.ServiceProvider.GetRequiredService<AddMachine>();
addMachineForm.Show();
}
Hope someone finds this helpful .
Upvotes: 1
Reputation: 305
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();
}
}
Upvotes: 8
Reputation: 48314
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
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?
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.
Upvotes: 15
Reputation: 125342
To use DI in a WinForms .NET 5 or 6 you can do the following steps:
Create a WinForms .NET Application
Install Microsoft.Extensions.Hosting package (which gives you a bunch of useful features like DI, Logging, Configurations, and etc.)
Add a new interface, IHelloService.cs
:
public interface IHelloService
{
string SayHello();
}
Add a new implementation for your service HelloService.cs
:
public class HelloService : IHelloService
{
public string SayHello()
{
return "Hello, world!";
}
}
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();
Upvotes: 88