2

This my code behind form ExpressionOfNeeds

private readonly IExpressionOfNeeds _expressionOfNeeds;
public FrmExpressionOfNeeds(IExpressionOfNeeds expressionOfNeeds)
{
    InitializeComponent();
    _expressionOfNeeds = expressionOfNeeds;
}
private async void FrmExpressionOfNeeds_Load(object sender, EventArgs e)
{
    GCData.DataSource = await _expressionOfNeeds.GetAllExpressionOfNeeds();
}

and this my code behind MainForm

private readonly IExpressionOfNeeds _expressionOfNeeds;
private readonly IService2 _service2;
private readonly IService3 _service3;
//and so on and so forth
public XtraMain()
{
    InitializeComponent();
}
private void bbtnExpressionOfNeeds_ItemClick(object sender, ItemClickEventArgs e)
{
    FrmExpressionOfNeeds frme = new(_expressionOfNeeds)
    {
        Name = "FrmExpressionOfNeeds"

    };
    ViewChildForm(frme);
}
private void bbtnForm2_ItemClick(object sender, ItemClickEventArgs e)
{
    Form2 frme = new(_service2)
    {
        Name = "Form2"

    };
    ViewChildForm(frme);
}
private void bbtnForm3_ItemClick(object sender, ItemClickEventArgs e)
{
    Form3 frme = new(_service3)
    {
        Name = "Form3"

    };
    ViewChildForm(frme);
}

and so on and so forth
and this is my code behind Program class

static void Main()
{
    var builder = new HostBuilder()
                 .ConfigureServices((hostContext, services) =>
                 {
                     services.AddScoped<XtraMain>();
                     services.AddSingleton<ISqlDataAccess, SqlDataAccess>();
                     services.AddSingleton<IExpressionOfNeeds, ExpressionOfNeeds>();
                 });
    var host = builder.Build();
    using (var serviceScope = host.Services.CreateScope())
    {
        var services = serviceScope.ServiceProvider;
        var mainform = services.GetRequiredService<XtraMain>();
        Application.Run(mainform);
    }
}

the problem is that the value of _expressionOfNeeds is always null and I can't find a way to Initialize it
Update
I have lots of forms and lots of Interfaces
I've only limited it to one example so the code isn't too big.

M.Bouabdallah
  • 530
  • 10
  • 29
  • 1
    I've explained the situation here: [How to use dependency injection in WinForms](https://stackoverflow.com/a/70476716/3110834) – Reza Aghaei Feb 26 '22 at 01:23

2 Answers2

2

You are almost there. You have registered the Form and services into DI container, but forgot to inject the interfaces into your form's constructor.

You have to either add dependent interfaces to the form's constructor (preferred solution), or get an instance of them from the service provider later when you need.

You can find a step by step example in the following post:

Assuming you have a MainForm which has a dependency to IMyServie, then you should have a constructor like this:

IMyService _myService;
public MainForm(IMyService myService)
{
    _myService = myService;
}

Then once you register MainForm and IMyService into the DI container and get the instance from service provider, everything will work as expected. Here is the main entry point:

static class Program
{
    public static IServiceProvider ServiceProvider { get; private set; }

    [STAThread]
    static void Main()
    {
        ApplicationConfiguration.Initialize();
        var host = CreateHostBuilder().Build();
        ServiceProvider = host.Services;
        Application.Run(ServiceProvider.GetRequiredService<MainForm>());
    }

    static IHostBuilder CreateHostBuilder()
    {
        return Host.CreateDefaultBuilder()
            .ConfigureServices((context, services)=>{
                services.AddTransient<IMyService, MyService>();
                services.AddTransient<MainForm>();
            });
    }
}

If for some reason (like what I explained in the linked answer) you need to get an instance of a service without injecting it in the constructor, then you can use Program.ServiceProvider.GetRequiredService<SomeFormOrService>().

Reza Aghaei
  • 120,393
  • 18
  • 203
  • 398
  • 1
    If for some reason (like what I explained in the linked answer) you need to get an instance of a service without injecting it in the constructor, then you can use `Program.ServiceProvider.GetRequiredService()`. Just keep in mind to register dependencies. – Reza Aghaei Feb 26 '22 at 05:50
  • I will try it and let you know – M.Bouabdallah Feb 26 '22 at 11:56
  • sorry for the late reply ,it works too. – M.Bouabdallah Mar 08 '22 at 19:09
  • No problem. Thanks for the feedback. As I mentioned in the answer as well, even in your implementation in the question, you are almost there. You have registered the Form and services into DI container, but forgot to inject the interfaces into your form's constructor. – Reza Aghaei Mar 08 '22 at 19:22
1

I would suggest creating a factory to get the forms needed by the main form

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

assuming the types to be created in this case all derive from Form

the implementation will use the service provider to resolve provided form type

public class FormFactory: IFormFactory {
    private readonly IServiceProvider services;

    public FormFactory(IServiceProvider services) {
        this.services = services;
    }

    public TForm Create<TForm>() where TForm : Form {
        return services.GetRequiredService<TForm>();
    }
}

The main form will need to depend on the factory so that it can create the forms as needed

//...

private readonly IFormFactory factory;

public XtraMain(IFormFactory factory) {
    InitializeComponent();
    this.factory = factory;
}
private void bbtnExpressionOfNeeds_ItemClick(object sender, ItemClickEventArgs e) {
    FrmExpressionOfNeeds frme = factory.Create<FrmExpressionOfNeeds>();
    frme.Name = "FrmExpressionOfNeeds";
    ViewChildForm(frme);
}
private void bbtnForm2_ItemClick(object sender, ItemClickEventArgs e) {
    Form2 frme = factory.Create<Form2>();
    frme.Name = "Form2";
    ViewChildForm(frme);
}
private void bbtnForm3_ItemClick(object sender, ItemClickEventArgs e) {
    Form3 frme = factory.Create<Form3>();
    frme.Name = "Form3";
    ViewChildForm(frme);
}

//...

Make sure everything to be resolved by the service provider is registered at startup

static void Main() {
    var builder = new HostBuilder()
                 .ConfigureServices((hostContext, services) => {
                    services.AddScoped<XtraMain>();
                    services.AddTransient<FrmExpressionOfNeeds>();
                    services.AddTransient<Form2>();
                    services.AddTransient<Form3>();
                    services.AddSingleton<ISqlDataAccess, SqlDataAccess>();
                    services.AddSingleton<IExpressionOfNeeds, ExpressionOfNeeds>();
                    services.AddSingleton<IFormFactory, FormFactory>();
                 });
    var host = builder.Build();
    using (var serviceScope = host.Services.CreateScope()) {
        IServiceProvider services = serviceScope.ServiceProvider;
        XtraMain mainform = services.GetRequiredService<XtraMain>();
        Application.Run(mainform);
    }
}

That way all dependencies can be resolved and injected by the container regardless of how many forms and interfaces there are.

Nkosi
  • 235,767
  • 35
  • 427
  • 472