Using Model View Presenter in WPF

This is an old post and doesn't necessarily reflect my current thinking on a topic, and some links or images may not work. The text is preserved here for posterity.

I have owned a copy of Sams Teach Yourself WPF in 24 Hours for about a year, and I still find it an interesting book. One thing I like about this book is that doesn't just show off WPF features - it shows how to use the Model-View-Presenter pattern in doing so. The book was written by Rob Eisenberg and Christopher Bennage from BlueSpire, who are also the guys behind the Caliburn WPF framework, so they know a thing or two about the Model-View-Presenter pattern.

Like the Model-View-ViewModel pattern, we can mostly agree upon what the MVP pattern is, but in practice nearly every implementation looks different. I want to highlight some of the differences between my approach to MVP and theirs, and I have attached a sample application that demonstrates these.

Sample

As in the Teach Yourself WPF in 24 Hours book, my sample application is a contact manager. Here are the key points:

  • The application has a shell, and a docking panel like Visual Studio.
  • There are two types of views that can be opened - a Contact List, a Contact Details for an individual contact
  • It uses Composite WPF for region management and loosely coupled communication
  • It uses Autofac 2.0 Beta for IOC
  • It uses AvalonDock for tab management

You can download the code to follow along. You can also download the Teach Yourself WPF Contact Manager from the Sams website (you may need to register before the download appears).

Contact Manager Composite WPF with MVP Sample

Model-View-Presenter Differences

First let's highlight some differences in terms of approaches to the MVP pattern between the two. Martin Fowler splits the MVP pattern into Passive View and Supervising Controller, but if anything I think both approaches are Supervising Controller, just in a different way.

View->Presenter Communication

The first example is in how views and presenters communicate. In Teach Yourself WPF, views can hold a reference to the presenter - the following code is for a button click event handler:

private void Save_Click(object sender, RoutedEventArgs e)
{
    Presenter.Save();
}

An illustration of View-Presenter communication in Teach Yourself WPF

In my approach to MVP, Views should be independent of the presenter and not hold a reference to it, though they may be bound to a Model. If a view really wanted to communicate with a presenter, it would do so via raising an event:

event EventHandler SaveRequested;

private void Save_Click(object sender, RoutedEventArgs e)
{
    var handler = SaveRequested;
    if (handler != null) handler(this, EventArgs.Empty);
}

An illustration of View-Presenter communication the traditional way

(Of course, this is assuming that in both examples, we aren't using WPF Commands)

Some considerations to keep in mind here are:

  • In the Teach Yourself WPF way, the Presenter needs a reference to the View, but the View also needs a reference to the Presenter. For loose coupling to work, both of these should be interfaces, which may require a second interface. Using events, you normally create an interface for the view - there is no need for an interface for the presenter, since the view does not need to reference it.
  • On the other hand, testing is made a little easier with Teach Yourself WPF approach. To simulate the click event, you just have to call the method on the presenter. But to simulate the click event in the eventing approach, you have to mock the view and raise the mock event.

Presenter is DataContext?

One pattern the Teach Yourself WPF book uses, and Caliburn too, is that the Presenter is normally the DataContext for the view. This is different to the approach I've normally used, where data context is a separate object - either a domain model object, or a view model object that the presenter manipulates.

One benefit of this is that in the Teach Yourself WPF approach, properties on the presenter are available for direct binding. A presenter can use some code like this:

public void Initialize() 
{
    Contact = LoadContactFromDatabase();
}

public Contact Contact { get; set; }

However, in my approach, the presenter normally sets properties on a Model object, which it assigns to the view:

public void Initialize() 
{
    View.Model = new ContactDetailsModel();
    View.Model.Contact = LoadContactFromDatabase();
}

The Model property on the view is just a wrapper around the data context, but the key point is the DataContext is not the presenter - it is a different object. The presenter can manipulate the model, and the view can manipulate the model, but the view doesn't touch the presenter (except perhaps indirectly via raising events).

A similar thing happens with Commands. In Teach Yourself WPF/Caliburn, a command would be exposed to the view by a property on the presenter. In my approach, the presenter gives the command to the model, and the view gets it from the model.

public void Initialize() 
{
    View.Model = new ContactDetailsModel();
    View.Model.Contact = LoadContactFromDatabase();
    View.Model.SaveCommand = new DelegateCommand(SaveExecuted);
}

Here are some points to consider:

  • The Teach Yourself WPF approach is simpler because it requires less code - you don't have to create a separate class for the model.
  • However, it does put both state and behavior in the same object. In the approach I have used, state and behavior are separated.

Is it MVVM?

Though Teach Yourself WPF/Caliburn use the term presenters, some of the points above make me feel almost as if they are closer to the MVVM pattern than the MVP pattern. In MVVM:

  • The ViewModel is usually the DataContext (check)
  • The View binds to properties on the ViewModel (check)
  • The View holds a direct reference to the ViewModel (check)
  • The ViewModel typically contains both the state of the view and it's behavior (check)

The one thing Caliburn and Teach Yourself WPF do that wouldn't fit the MVVM profile is that the Presenter holds a reference to the View - something ViewModels should never do. However, in many ways it feels that the Presenters in Teach Yourself WPF/Caliburn are closer to the MVVM pattern than they are to the traditional MVP pattern.

Model-View-Presenter-Parameter

Another object that I have created for each view is a Parameter object. When you open a view, it's common to need to pass arguments to the presenter - such as a customer ID, filter information, and so on. Instead of making these properties on the presenter, the pattern I follow is to put them in a separate object. This is a technique I learned at from a client, and though it seemed redundant at first, it has grown on me.

For example, in the sample application, when you click a contact, the ContactDetailsPresenter is shown for that contact. The parameter object looks like this:

public class ContactDetailsParameter : Parameter
{
    public ContactDetailsParameter() : this(0)
    {
    }

    public ContactDetailsParameter(int contactId)
    {
        ContactId = contactId;
    }

    public int ContactId { get; set; }
    public bool IsNew { get { return ContactId == 0; } }
}

The presenter can make use of these arguments when initializing the view:

public class ContactDetailsPresenter : Presenter<IContactDetailsView, ContactDetailsModel, ContactDetailsParameter>
{
    protected override void Initialize()
    {
        View.Model = new ContactDetailsModel();
        View.Model.SaveCommand = new DelegateCommand(SaveExecuted);
        View.Model.Contact = Parameter.IsNew 
            ? new Contact() 
            : _contactRepository.GetContact(Parameter.ContactId);
    }
}

The key benefit that parameters have is that you can use them to identify information about a view, without actually creating the presenter or view. This is common when you want to re-activate an existing view.

In the sample application, you can click a contact and they will open in a new tab. If you click another contact, another tab opens. But if you click the same contact a second time, you don't want a new tab to appear - instead, you might want the currently open tab to get focus.

To open a view, I normally raise a Composite WPF event:

private void ShowContactExecuted(Contact contact)
{
    EventAggregator.GetEvent<ShowViewEvent>().Publish(new ShowViewRequest(typeof(ContactDetailsPresenter), "ContentPane", new ContactDetailsParameter(contact.Id)));
}

The arguments are:

  1. The type of presenter
  2. The region name to insert the view into
  3. The parameter, with all the arguments the presenter needs

There is a singleton event listener which receives that event and works something like this (simplified):

if (region.HasMatchingView(parameter))
{
    region.ActivateView(parameter);
}
else 
{
    var presenter = Container.Resolve(presenterType);
    region.Add(presenter.View, parameter);
}

The logic for deciding whether to re-use the view in the parameter is implemented like this:

public ContactDetailsParameter(int contactId)
{
    ContactId = contactId;

    // Use the same view when ContactID's match, but for new views, never reuse them.
    ReuseViewBy(() => IsNew ? (object)Guid.NewGuid() : ContactId);
}

While this approach has many upsides, it does increase the number of files needed for a full MVP view:

Solution Explorer showing views

The upside however is that you do get a flexible, consistent approach to views that can handle pretty much any scenario - and having used this pattern in a fairly complicated LOB application, I'm pretty confident that it can handle anything. A good New Item template for Visual Studio can greatly speed up the getting started time too.

Some things to consider:

  • A parameter is a good way to encapsulate the arguments for a presenter
  • But it does introduce an extra class, though that in itself has some benefits

Composite WPF Regions vs. Presenter Hierarchies

I have used Composite WPF in five client projects now and I'll continue to do so. It's a fantastic piece of work that does help you to solve many common scenarios without being overly controlling. The major feature Composite WPF brings for me is regions, which allow you to compose and nest views, and allow the to communicate via the event aggregator.

Composition is an aspect of Teach Yourself WPF/Caliburn that I really don't feel comfortable with, especially from a Composite WPF background. Check out the Caliburn documentation on presenters. Depending on your UI, you can choose from:

  • Presenter
  • Navigator
  • PresenterManager
  • MultiPresenter
  • MultiPresenterManager?

Each of these are kinds of presenters and serve to fulfill a specific composition pattern. They are necessary because, for example, if you have a view with two child views, the parent view's presenter needs a reference to the child presenters, and vice-versa sometimes, so they can communicate. It reminds me of the CAB SmartPart/Workspace approach.

The Composite WPF region approach does away with all of this. Here's the start up code for the sample application:

_container.Resolve<IEventAggregator>().GetEvent<ShowViewEvent>().Publish(new ShowViewRequest(typeof(ShellPresenter), "Shell", new ShellParameter()));
_container.Resolve<IEventAggregator>().GetEvent<ShowViewEvent>().Publish(new ShowViewRequest(typeof(MenuPresenter), "Menu", new MenuParameter()));
_container.Resolve<IEventAggregator>().GetEvent<ShowViewEvent>().Publish(new ShowViewRequest(typeof(ContactListPresenter), "ContentPane", new ContactListParameter()));

This code opens the Shell, and injects the Menu and ContactList into it. However, the Shell view has no notion of either of them - the ShellPresenter is just a presenter, not some kind of MultiPresenterManager. If it needed to communicate with them, it could do so via a loosely coupled pub-sub model called the Event Aggregator.

Keep in mind that the Composite WPF region system would work just as easily with a Teach Yourself WPF presenter approach (presenter as DataContext, etc.). The composition approaches aren't necessarily exclusive either - hierarchical presenters as used with Caliburn can work alongside regions.

Conclusion

In this post I focused on two alternative approaches to implementing the Model View Presenter pattern - a traditional, strict approach with strong separation between state and behavior, and the Teach Yourself WPF/Caliburn approach which is a nice intermediary between MVP and MVVM. I think both patterns have their strengths and I've tried to provide some ideas on what those might be. I also contrasted the related composition approaches which aren't necessarily exclusive. I'd love your comments on additional strenghts/weaknesses you've experienced with either pattern.

And if you are building WPF line of business applications and haven't picked up a copy of Teach Yourself WPF, I'd definitely recommend it. This post is also not a thorough representation of the features that Caliburn nor Composite WPF provide - Caliburns Actions are definitely worth a look.