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:
- Infrastructure
- Domain
- 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:
- The presenter doesn't have any methods that the view can call, but the view has events that the presenter can subscribe to.
- The presenter knows its view. This is accomplished using constructor injection.
- The view has no idea what presenter is controlling it.
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? */)
}
}