jrn6270
jrn6270

Reputation: 179

When and how to create presenters using windows forms with MVP and dependency injection

This is a question regarding dependency injection (DI) using Lamar, with windows forms (C#) using the model-view-presenter (MVP) pattern and how (and when) to initialize presenter objects.

I have a solution split up into three projects:

  1. Infrastructure
  2. Domain
  3. Presentation

In the presentation project, I have my forms and user controls, which are separated using the MVP-pattern.

In the Program.cs file for the presentation project, I define my container using Lamar and create my main view as:

var container = new Container(x =>
{
    x.AddSingleton<IInterfaceFromDomainProject, ClassFromDomainProject>();
    x.AddSingleton<IMainView, MainView>();
    x.AddSingleton<IMainPresenter, MainPresenter>();
    x.AddSingleton<ISubView, SubView>();
    x.AddSingleton<ISubPresenter, SubPresenter>();
});

Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
var mainView = new MainView();
Application.Run(mainView);

This will resolve any dependencies my presenters and views may have.

For the MVP-pattern I am using the observing presenter style, which is explained here. I like this specific style because it completely decouples knowledge of the presenters from the views. An example of this can be found here.

This means that the constructors for my views (forms and user controls) doesn't take any paramaters. This allows me to drag and drop user controls into my forms when designing it.

For example, if my MainView (which is a form) has a tab control in it, I can drag and drop the SubView (which is an user control) into a tab page of the tab control.

All the logic (what data to present etc.) are handled in the presenters. This means that my presenters constructors takes interfaces from the domain project as parameters, as well as the interface of the concrete view.

My main view:

public interface IMainView
{
    event EventHandler MyCustomEvent;
    void ShowMessage(); 
}

public partial class MainView : Form, IMainView
{
    public event EventHandler MyCustomEvent;

    public MainView()
    {
        InitializeComponent();
    }

     private void button_Click(object sender, EventArgs e)
    {
        MyCustomEvent.Invoke(sender, EventArgs.Empty);
    }

    public void ShowMessage()
    {
        MessageBox.Show("Hello!");
    }
}

My main presenter:

public interface IMainPresenter
{
    void ShowMessageHandler(object sender, EventArgs e);
    void ShowData();
}

public class MainPresenter: IMainPresenter
{
    private readonly IMainView _view;
    private readonly IInterfaceFromDomainProject _foo;

    public MainPresenter(IMainView view, IInterfaceFromDomainProject foo)
    {
        _view = view;
        _foo = foo;

        _view.MyCustomEvent += ShowMessageHandler;
    }

    public void ShowMessageHandler(object sender, EventArgs e)
    {
        _view.ShowMessage();
    }

    public void ShowData()
    {
        // Do something with _foo. Get data and display it in its view.
    }
}

From the previous link:

Question

Based on this implementation of MVP and DI, how and when do I create my presenters? They are dependent on interfaces from the domain project, which is why they are used in the lamar-container. Am I supposed to call var mainPresenter = new MainPresenter(container.GetRequiredService<IMainView>(), /* get service for all required interfaces*/); for all my presenters in Program.cs?

Am I misunderstanding something regarding DI or the MVP pattern?

Edit

The var mainPresenter = new MainPresenter(container.GetRequiredService<IMainView>() /* get service for all required interfaces*/); doesn't work and i get a NullReferenceException: 'Object reference not set to an instance of an object.' on the MyCustomEvent.Invoke(sender, EventArgs.Empty); (I know I should use ?.).

The only way to do it is to call:

var mainView = new MainView();
var mainPresenter = new MainPresenter(mainView);

I have seen other implementations of MVP, where the presenter is created in the constructor for the concrete view, but how do i pass the necessary interfaces to the presenter? E.g.:

public partial class MainView : Form, IMainView
{
    public MainView()
    {
        InitializeComponent();
        var presenter = new MainPresenter(this, /* How to pass interfaces here? */)
    }
}

Upvotes: 3

Views: 1194

Answers (1)

Roland Buergi
Roland Buergi

Reputation: 1217

Your question is very broad and not easy to answer in such a post. You are asking about overall UI architecture, probably because you want to modularize your user interface. This is generally difficult to achieve with Winforms as, by its core design, everything is attached to the main window, and views can only hardly be decoupled from components such as presenters or view models.

If you want to achieve modularity of your UI, I would recommend you to switch technology to Windows Presentation Framework (WPF), which has a much better, modular, architecture and supports MVP, MVVM, event tunneling and much more.

If you want to study how to build modular applications, a good starting point might be the prism library, which highlights most of the concepts. You will also find answers on how to use dependency injection, how to create presenters, etc. Please have a look here: https://prismlibrary.com/docs/wpf/view-composition.html

Upvotes: 2

Related Questions