Model-View-Presenter in WinForms

Darren Young picture Darren Young · Jan 25, 2011 · Viewed 45.4k times · Source

I am trying to implement the MVP method for the first time, using WinForms.

I am trying to understand the function of each layer.

In my program I have a GUI button that when clicked upon opens a openfiledialog window.

So using MVP, the GUI handles the button click event and then calls presenter.openfile();

Within presenter.openfile(), should that then delegate the opening of that file to the model layer, or as there is no data or logic to process, should it simply act on the request and open the openfiledialog window?

Update: I have decided to offer a bounty as I feel I need further assistance on this, and preferably tailored to my specific points below, so that I have context.

Okay, after reading up on MVP, I have decided to implement the Passive View. Effectively I will have a bunch of controls on a Winform that will be handled by a Presenter and then the tasks delegated to the Model(s). My specific points are below:

  1. When the winform loads, it has to obtain a treeview. Am I correct in thinking that the view should therefore call a method such as: presenter.gettree(), this in turn will delegate to the model, which will obtain the data for the treeview, create it and configure it, return it to the presenter, which in turn will pass to the view which will then simply assign it to, say, a panel?

  2. Would this be the same for any data control on the Winform, as I also have a datagridview?

  3. My App, has a number of model classes with the same assembly. It also supports a plugin architecture with plugins that need to be loaded at startup. Would the view simply call a presenter method, which in turn would call a method that loads the plugins and display the information in the view? Which tier would then control the plugin references. Would the view hold references to them or the presenter?

  4. Am I correct in thinking that the view should handle every single thing about presentation, from treeview node colour, to datagrid size, etc?

I think that they are my main concerns and if I understand how the flow should be for these I think I will be okay.

Answer

Johann Gerell picture Johann Gerell · Jan 27, 2011

This is my humble take on MVP and your specific issues.

First, anything that a user can interact with, or just be shown, is a view. The laws, behavior and characteristics of such a view is described by an interface. That interface can be implemented using a WinForms UI, a console UI, a web UI or even no UI at all (usually when testing a presenter) - the concrete implementation just doesn't matter as long as it obeys the laws of its view interface.

Second, a view is always controlled by a presenter. The laws, behavior and characteristics of such a presenter is also described by an interface. That interface has no interest in the concrete view implementation as long as it obeys the laws of its view interface.

Third, since a presenter controls its view, to minimize dependencies there's really no gain in having the view knowing anything at all about its presenter. There's an agreed contract between the presenter and the view and that's stated by the view interface.

The implications of Third are:

  • 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. I prefer to accomplish this with constructor injection on the concrete presenter.
  • The view has no idea what presenter is controlling it; it'll just never be provided any presenter.

For your issue, the above could look like this in somewhat simplified code:

interface IConfigurationView
{
    event EventHandler SelectConfigurationFile;

    void SetConfigurationFile(string fullPath);
    void Show();
}

class ConfigurationView : IConfigurationView
{
    Form form;
    Button selectConfigurationFileButton;
    Label fullPathLabel;

    public event EventHandler SelectConfigurationFile;

    public ConfigurationView()
    {
        // UI initialization.

        this.selectConfigurationFileButton.Click += delegate
        {
            var Handler = this.SelectConfigurationFile;

            if (Handler != null)
            {
                Handler(this, EventArgs.Empty);
            }
        };
    }

    public void SetConfigurationFile(string fullPath)
    {
        this.fullPathLabel.Text = fullPath;
    }

    public void Show()
    {
        this.form.ShowDialog();        
    }
}

interface IConfigurationPresenter
{
    void ShowView();
}

class ConfigurationPresenter : IConfigurationPresenter
{
    Configuration configuration = new Configuration();
    IConfigurationView view;

    public ConfigurationPresenter(IConfigurationView view)
    {
        this.view = view;            
        this.view.SelectConfigurationFile += delegate
        {
            // The ISelectFilePresenter and ISelectFileView behaviors
            // are implicit here, but in a WinForms case, a call to
            // OpenFileDialog wouldn't be too far fetched...
            var selectFilePresenter = Gimme.The<ISelectFilePresenter>();
            selectFilePresenter.ShowView();
            this.configuration.FullPath = selectFilePresenter.FullPath;
            this.view.SetConfigurationFile(this.configuration.FullPath);
        };
    }

    public void ShowView()
    {
        this.view.SetConfigurationFile(this.configuration.FullPath);
        this.view.Show();
    }
}

In addition to the above, I usually have a base IView interface where I stash the Show() and any owner view or view title that my views usually benefit from.

To your questions:

1. When the winform loads, it has to obtain a treeview. Am I correct in thinking that the view should therefore call a method such as: presenter.gettree(), this in turn will delegate to the model, which will obtain the data for the treeview, create it and configure it, return it to the presenter, which in turn will pass to the view which will then simply assign it to, say, a panel?

I would call IConfigurationView.SetTreeData(...) from IConfigurationPresenter.ShowView(), right before the call to IConfigurationView.Show()

2. Would this be the same for any data control on the Winform, as I also have a datagridview?

Yes, I would call IConfigurationView.SetTableData(...) for that. It's up to the view to format the data given to it. The presenter simply obeys the view's contract that it wants tabular data.

3. My App, has a number of model classes with the same assembly. It also supports a plugin architecture with plugins that need to be loaded at startup. Would the view simply call a presenter method, which in turn would call a method that loads the plugins and display the information in the view? Which tier would then control the plugin references. Would the view hold references to them or the presenter?

If the plugins are view-related, then the views should know about them, but not the presenter. If they are all about data and model, then the view shouldn't have anything to do with them.

4. Am I correct in thinking that the view should handle every single thing about presentation, from treeview node colour, to datagrid size, etc?

Yes. Think about it as the presenter providing XML that describes data and the view that takes the data and applies a CSS stylesheet to it. In concrete terms, the presenter might call IRoadMapView.SetRoadCondition(RoadCondition.Slippery) and the view then renders the road in red color.

What about data for clicked nodes?

5. If when I click on the treenodes, should I pass through the specific node to the presenter and then from that the presenter would work out what data it needs and then asks the model for that data, before presenting it back to the view?

If possible, I would pass all data needed to present the tree in a view in one shot. But if some data is too large to be passed from the beginning or if it's dynamic in its nature and needs the "latest snapshot" from the model (via the presenter), then I would add something like event LoadNodeDetailsEventHandler LoadNodeDetails to the view interface, so that the presenter can subscribe to it, fetch the details of the node in LoadNodeDetailsEventArgs.Node (possibly via its ID of some kind) from the model, so that the view can update its shown node details when the event handler delegate returns. Note that async patterns of this might be needed if fetching the data might be too slow for a good user experience.