Model-View-Presenter in Winforms

Model-View-Presenter in WinForms

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.

How to Structure a C# WinForms Model-View-Presenter (Passive View) Program?

Abstraction is good, but it's important to remember that at some point something has to know a thing or two about a thing or two, or else we'll just have a pile of nicely abstracted legos sitting on the floor instead of them being assembled into a house.

An inversion-of-control/dependency injection/flippy-dippy-upside-down-whatever-we're-calling-it-this-week container like Autofac can really help in piecing this all together.

When I throw together a WinForms application, I usually end up with a repeating pattern.

I'll start with a Program.cs file that configures the Autofac container and then fetches an instance of the MainForm from it, and shows the MainForm. Some people call this the shell or the workspace or the desktop but at any rate it's "the form" that has the menu bar and displays either child windows or child user controls, and when it closes, the application exits.

Next is the aforementioned MainForm. I do the basic stuff like drag-and-dropping some SplitContainers and MenuBars and such in the Visual Studio visual designer, and then I start getting fancy in code: I'll have certain key interfaces "injected" into the MainForm's constructor so that I can make use of them, so that my MainForm can orchestrate child controls without really having to know that much about them.

For example, I might have an IEventBroker interface that lets various components publish or subscribe to "events" like BarcodeScanned or ProductSaved. This allows parts of the application to respond to events in a loosely coupled way, without having to rely on wiring up traditional .NET events. For example, the EditProductPresenter that goes along with my EditProductUserControl could say this.eventBroker.Fire("ProductSaved", new EventArgs<Product>(blah)) and the IEventBroker would check its list of subscribers for that event and call their callbacks. For example, the ListProductsPresenter could listen for that event and dynamically update the ListProductsUserControl that it is attached to. The net result is that if a user saves a product in one user control, another user control's presenter can react and update itself if it happens to be open, without either control having to be aware of each other's existence, and without the MainForm having to orchestrate that event.

If I'm designing an MDI application, I might have the MainForm implement an IWindowWorkspace interface that has Open() and Close() methods. I could inject that interface into my various presenters to allow them to open and close additional windows without them being aware of the MainForm directly. For example, the ListProductsPresenter might want to open an EditProductPresenter and corresponding EditProductUserControl when the user double-clicks a row in a data grid in a ListProductsUserControl. It can reference an IWindowWorkspace--which is actually the MainForm, but it doesn't need to know that--and call Open(newInstanceOfAnEditControl) and assume that the control was shown in the appropriate place of the application somehow. (The MainForm implementation would, presumably, swap the control into view on a panel somewhere.)

But how the hell would the ListProductsPresenter create that instance of the EditProductUserControl? Autofac's delegate factories are a true joy here, since you can just inject a delegate into the presenter and Autofac will automagically wire it up as if it were a factory (pseudocode follows):


public class EditProductUserControl : UserControl
{
public EditProductUserControl(EditProductPresenter presenter)
{
// initialize databindings based on properties of the presenter
}
}

public class EditProductPresenter
{
// Autofac will do some magic when it sees this injected anywhere
public delegate EditProductPresenter Factory(int productId);

public EditProductPresenter(
ISession session, // The NHibernate session reference
IEventBroker eventBroker,
int productId) // An optional product identifier
{
// do stuff....
}

public void Save()
{
// do stuff...
this.eventBroker.Publish("ProductSaved", new EventArgs(this.product));
}
}

public class ListProductsPresenter
{
private IEventBroker eventBroker;
private EditProductsPresenter.Factory factory;
private IWindowWorkspace workspace;

public ListProductsPresenter(
IEventBroker eventBroker,
EditProductsPresenter.Factory factory,
IWindowWorkspace workspace)
{
this.eventBroker = eventBroker;
this.factory = factory;
this.workspace = workspace;

this.eventBroker.Subscribe("ProductSaved", this.WhenProductSaved);
}

public void WhenDataGridRowDoubleClicked(int productId)
{
var editPresenter = this.factory(productId);
var editControl = new EditProductUserControl(editPresenter);
this.workspace.Open(editControl);
}

public void WhenProductSaved(object sender, EventArgs e)
{
// refresh the data grid, etc.
}
}

So the ListProductsPresenter knows about the Edit feature set (i.e., the edit presenter and the edit user control)--and this is perfectly fine, they go hand-in-hand--but it doesn't need to know about all of the dependencies of the Edit feature set, instead relying on a delegate provided by Autofac to resolve all of those dependencies for it.

Generally, I find that I have a one-to-one correspondence between a "presenter/view model/supervising controller" (let's not too caught up on the differences as at the end of the day they are all quite similar) and a "UserControl/Form". The UserControl accepts the presenter/view model/controller in its constructor and databinds itself as is appropriate, deferring to the presenter as much as possible. Some people hide the UserControl from the presenter via an interface, like IEditProductView, which can be useful if the view is not completely passive. I tend to use databinding for everything so the communication is done via INotifyPropertyChanged and don't bother.

But, you will make your life much easier if the presenter is shamelessly tied to the view. Does a property in your object model not mesh with databinding? Expose a new property so it does. You are never going to have an EditProductPresenter and an EditProductUserControl with one layout and then want to write a new version of the user control that works with the same presenter. You will just edit them both, they are for all intents and purpose one unit, one feature, the presenter only existing because it is easily unit testable and the user control is not.

If you want a feature to be replaceable, you need to abstract the entire feature as such. So you might have an INavigationFeature interface that your MainForm talks to. You can have a TreeBasedNavigationPresenter that implements INavigationFeature and is consumed by a TreeBasedUserControl. And you might have a CarouselBasedNavigationPresenter that also implements INavigationFeature and is consumed by a CarouselBasedUserControl. The user controls and the presenters still go hand-in-hand, but your MainForm would not have to care if it is interacting with a tree-based view or a carousel-based one, and you could swap them out without the MainForm being the wiser.

In closing, it is easy to confuse yourself. Everyone is pedantic and uses slightly different terminology to convey they subtle (and oftentimes unimportant) differences between what are similar architectural patterns. In my humble opinion, dependency injection does wonders for building composable, extensible applications, since coupling is kept down; separation of features into "presenters/view models/controllers" and "views/user controls/forms" does wonders for quality since most logic is pulled into the former, allowing it to be easily unit tested; and combining the two principles seems to really be what you're looking for, you're just getting confused on the terminology.

Or, I could be full of it. Good luck!

MVP pattern WinForms: How to correctly update UI?

Your Presenter should sync your View and your Model. The View only shows the data. The Model know's the business logic and "real" data and the Presenter link the Model data to your View. So you won't access the Richtextbox from your Model. Instead you do this from your Presenter. To stay independent you should use Interfaces. So you have no direct access to View elements in Presenter or Model.

Create an IView Interface and an IModel Interface. Both of them are
known by your Presenter.

Your example could look like this:

public class HomeView : IHomeView
{
public string Text
{
get {return richtextbox.Text;}
set {richtextbox.Text = value;}
}
}

public class HomePresenter
{
IHomeView view;
IModel model;

HomePresenter(IHomeView view, IModel model)
{
view = view;
model = model;

//Update View
view.Text = model.Text;
}

public void UpdateModel
{
model.Text = view.Text; //Set the Model Property to value from Richtextbox
}
}

public class Model : IModel
{
public string Text {get;set} //Property which represent Data from Source like DB or XML etc.
}

You find another example here.

C# WinForms Model-View-Presenter (Passive View)

Here is a simple example that demonstrates the concept of passive views using the MVP design pattern. Because we are using passive views the view has no knowledge of the presenter. The presenter will simply subscribe to events published by the view and act accordingly.

To start out we need to define a contract for our view. This is typically achieved using an interface, essentially, we want to have a very loose coupling with our view. We want the ability to switch to different views or event create mock views for unit testing.

Here is a contract that describes a simple view that will be used to display customer information

public interface ICustomerManagementView
{
void InitializeCustomers(ICustomer[] customers);
void DisplayCustomer(ICustomer customer);
event EventHandler<EventArgs<ICustomer>> SelectedCustomerChanged;
}

It exposes a single method InitializeCustomers that will be used to initialize our view with objects from our model.

We also have an event SelectedCustomerChanged that will be used by our presenter to receive notification that an action has occurred in the view.

Once we have our contract we can start to handle these interactions in our presenter.

public class CustomerManagementPresenter
{
private ICustomer _selectedCustomer;
private readonly ICustomerManagementView _managementView;
private readonly ICustomerRepository _customerRepository;

public CustomerManagementPresenter(ICustomerManagementView managementView, ICustomerRepository customerRepository)
{
_managementView = managementView;
_managementView.SelectedCustomerChanged += this.SelectedCustomerChanged;

_customerRepository = customerRepository;

_managementView.InitializeCustomers(_customerRepository.FetchCustomers());
}

private void SelectedCustomerChanged(object sender, EventArgs<ICustomer> args)
{
// Perform some logic here to update the view
if(_selectedCustomer != args.Value)
{
_selectedCustomer = args.Value;
_managementView.DisplayCustomer(_selectedCustomer);
}
}
}

In the presenter we can use another design pattern called dependency injection to provide access to our view and any model classes that we may need. In this example I have a CustomerRepository that is responsible for fetching customer details.

In the constructor we have two important lines of code, firstly we have subscribed to the SelectedCustomerChanged event in our view, it is here that we can perform associated actions. Secondly we have called InitilaizeCustomers with data from the repository.

At this point we haven't actually defined a concrete implementation for our view, all we need to do is create an object that implements ICustomerManagementView. For example in a Windows Forms application we can do the following

public partial class CustomerManagementView : Form, ICustomerManagementView
{
public CustomerManagementView()
{
this.InitializeComponents();
}

public void InitializeCustomers(ICustomer[] customers)
{
// Populate the tree view with customer details
}

public void DisplayCustomer(ICustomer customer)
{
// Display the customer...
}

// Event handler that responds to node selection
private void CustomerTreeViewAfterSelect(object sender, TreeViewEventArgs e)
{
var customer = e.Node.Tag as ICustomer;
if(customer != null)
{
this.OnSelectedCustomerChanged(new EventArgs<ICustomer>(customer));
}
}

// Protected method so that we can raise our event
protected virtual void OnSelectedCustomerChanged(EventArgs<ICustomer> args)
{
var eventHandler = this.SelectedCustomerChanged;
if(eventHandler != null)
{
eventHandler.Invoke(this, args);
}
}

// Our view will raise an event each time the selected customer changes
public event EventHandler<EventArgs<ICustomer>> SelectedCustomerChanged;
}

If we wanted to test our presentation logic we could mock our view and perform some assertions.

EDIT : Included custom event args

public class EventArgs<T> : EventArgs
{
private readonly T _value;

public EventArgs(T value)
{
_value = value;
}

public T Value
{
get { return _value; }
}
}

MVP: Is it the View or the Presenter that should know of the Model?

What's wrong if view knows model? After all UserView is made
specifically for UserModel isnt it?

Nothing. It's accepted practice in the Supervising Controller variant of the MVP pattern. The view interacts directly with the model for simple operations while more complex operations are marshalled throught the presenter. While in Passive View, everything goes through the presenter.

Additionally, see Jeremy Miller's Build your own CAB series to get a better idea on the differences between the two approaches: Supervising Controller and Passive View.

async/await Model View Presenter winforms

The core problem is that the code is using events as a strategy pattern rather than an observer pattern. There isn't much you can do with this code as it currently stands; a proper refactoring would require callback interfaces rather than events. E.g.:

// An instance of this is passed into the concrete view.
public interface IViewImplementation
{
void DoSomething();
// Or the async equivalent:
// Task DoSomethingAsync();
}

However, there are some workarounds you can apply, if that level of refactoring is unsavory. I cover such "async events" on my blog. There are a few approaches; it is possible (though awkward) to define a Task-returning event. My favorite approach, though, is Deferrals, mainly because deferrals are a concept already familiar to WinRT developers.

Winforms MVP Pattern w/ Multiple Views

First of all, you want to create a UserControl for each of the three DataGridView forms. As you are using MVP, each one should have an interface that the control inherits. For example:

public interface IDataGridView1
{
// Properties, Methods, etc...
}

public interface IDataGridView2
{
// Properties, Methods, etc...
}

public interface IDataGridView3
{
// Properties, Methods, etc...
}

Here is an example of the DataGridView1 UserControl, which inherits from its interface, and also from Control:

public class DataGridView1 : Control, IDataGridView1
{
TableLayoutPanel layoutPanel;

public DataGridView1()
{
layoutPanel = new TableLayoutPanel();
// Set up the layoutPanel.

// Rest of constructor, define your controls.

// Add your controls to layoutPanel.

// Add layoutPanel to this control.
Controls.Add(layoutPanel);
}

// Methods etc...
}

The other two DataGridViews will be similar but with their own functionality.

You could then create an interface for the MainView, which includes properties for the three DataGridViews it should contain, and methods to show one DataGridView whilst hiding the rest:

public interface IMainView
{
IDataGridView1 DataView1 { get; set; }
IDataGridView2 DataView2 { get; set; }
IDataGridView3 DataView3 { get; set; }

void ShowOnlyDataView1();
void ShowOnlyDataView2();
void ShowOnlyDataView3();

// Other methods, properties, etc...
}

The MainView class would inherit from Form and its own interface. Here I have shown the instantiated DataGridViews being passed in via the form's constructor:

public class MainView : Form, IMainView
{
public IDataGridView1 DataView1 { get; set; }
public IDataGridView2 DataView2 { get; set; }
public IDataGridView3 DataView3 { get; set; }

TableLayoutPanel layoutPanel;

public MainView(IDataGridView1 dataView1, IDataGridView2 dataView2,
IDataGridView3 dataView3)
{
this.DataView1 = dataView1;
this.DataView2 = dataView2;
this.DataView3 = dataView3;

layoutPanel = new TableLayoutPanel();
// Define your layout panel here.

// Add your controls to layoutPanel.

// Add layoutPanel to the MainView.
Controls.Add(layoutPanel);

// Rest of constructor...
}

// Hides other views and show DataView1.
public void ShowOnlyDataView1()
{
DataView2.Hide();
DataView3.Hide();
DataView1.Show();
}

// Hides other views and show DataView2.
public void ShowOnlyDataView2()
{
// Etc...
}

// Hides other views and show DataView3.
public void ShowOnlyDataView3()
{
// Etc...
}

// Other Methods etc...
}

Here is an example of the your Main method. You will want to instantiate each DataGridView and pass these into your MainView:

public static void Main(string[] args)
{
IDataModel dataModel = new DataModel();
IDataGridView1 dataView1 = new DataGridView1();
IDataGridView2 dataView2 = new DataGridView2();
IDataGridView3 dataView3 = new DataGridView3();
IMainView mainView = new MainView(dataView1, dataView2, dataView3);
DataGridPresenter1 dataPresenter1 = new DataGridPresenter1(dataView1, dataModel);
DataGridPresenter2 dataPresenter2 = new DataGridPresenter2(dataView2, dataModel);
DataGridPresenter3 dataPresenter3 = new DataGridPresenter3(dataView3, dataModel);
MainPresenter mainPresenter = new MainPresenter(mainView, dataModel);
}

Something to that effect.

So your three DataGridViews are displayed within your MainView, and all four views are accessed by their own Presenters.



Related Topics



Leave a reply



Submit