Going from Windows Forms to WPF and Silverlight, we've seen some incredible advances in the Presentation Layer in recent years. Of course, technology advances never mean longer holidays for us developers. Instead, users just expect more. Where they might have been happy to suffer through forms-over-data user interfaces, they now expect our applications to be much more proactive in managing business processes and surfacing useful information. In short, the needs of our UI's are rapidly becoming more complicated!
When the problems we're trying to solve increase in complexity, our code has a tendency to begin to increase in complexity. But it doesn't have to be that way. Programming principles like the SOLID principles, and pattern catalogs like the Gang of Four design patterns, give us some help for managing code complexity. One of my favourite books, Domain Driven Design by Eric Evans, describes solutions for managing both complex problems as well as the code. In fact, the book's subtitle is "Tackling Complexity in the Heart of Software".
Figure: As our problem domain becomes more complicated, patterns can help to act as a downward force on code complexity.
While we've had patterns for the other layers of our code for a long time, the use of patterns on the Microsoft UI stack has become an emerging trend. A category of design patterns has evolved called Separated Presentation patterns, which describe ways to structure the code that lives in the presentation layer. The goal of this article is to give you a brief overview of the major Separated Presentation patterns, as they apply to WPF.
The three patterns I'm going to cover are Model-View-ViewModel, ViewModel-first MVVM, and Model-View-Presenter.
Plain, old-fashioned code behind
Even if you don't use a separated presentation pattern, there are two kinds of objects you'll have: Views and Models. Even in the days of .NET 1.1 Windows Forms, we were using Forms (the View) and DataSets (the Model). The separated presentation patterns, which we'll look at shortly, build on top of these.
Figure: at a minimum, we want to put things on the screen (a view), and we need data to put on the screen (the model)
The View is a generic name for the Window, UserControl, Control, Page, or other major UI element that encapsulates our piece of functionality. Since we intend to eventually put some kind of UI into the monitor for a human to see and interact with, any pattern needs some kind of View.
The Model is the data objects that our UI uses - for example, an Account with Transactions, which belongs to a Customer. It may be some kind of canonical understanding of the problem domain we're dealing with, used by all of our views in differently, or they might be used just by this view. They might just be Data Transfer Objects or messages returned from a Web Service/message bus, or a DataSet, NHibernate or Entity Framework classes that we fetched from a database.
The View and Model might communicate as follows:
- Code-behind in the View can set properties or call methods on the Model
- Controls on the View can be bound to properties on the Model
- The Model can raise events, and the View can subscribe to them
Throughout this article I'll use dotted arrows to indicate an event being raised, and solid arrows to represent a method call or data binding.
This approach works fine, for a while, but since the only place to put code is code-behind, it can start to become hard to manage. The code to hide a button is next to the code to evaluate whether an account is overdue, which is next to the code that triggers an animation. Separated Presentation patterns allow us to clean up this code by adding a Third Object.
Introducing the Third Object
Each Separated Presentation pattern uses the View and Model. The third part of each pattern has a different name, but they solve the same purpose – any code that isn't highly UI specific, but is mostly concerned with behaviour or state management, goes there.
Figure: The Third Object is introduced by the Separated Presentation patterns. The difference between each pattern is how the Third Object communicates with the other two.
What might we expect to see in that third object?
- Code for creating and managing background threads
- Code for invoking a web service
- Code for filtering an in-memory collection
- Code for UI-calculated values that don't belong on the model
- Code for triggering navigation to another view
So why do we have different patterns? Because there are different ways that the third object, the View and the Model can communicate. The third object always has the same responsibilities, but the way it achieves them vary.
With WPF or Silverlight, MVVM is the simplest pattern to implement. The third object is the ViewModel, which the View is bound to, usually using the WPF DataContext property. The ViewModel also exposes Model objects, which the View also binds to.
Figure: the View is bound to the ViewModel, which raises events to notify the View about changes. The ViewModel can also manipulate and subscribe to events on the Model.
For example, imagine a screen that allowed us to search through a list of patients.
- The ViewModel would expose a SearchText string property, which would be bound to a TextBox on the View. WPF's data binding capabilities will set the SearchText property when we type in the TextBox.
- The ViewModel might expose a method, or a WPF ICommand, called Search.
- When a button is clicked, the Search command or method on the ViewModel would be invoked.
- When invoked, the Search command or method would call a web service.
- The list of patients would be added to an ObservableCollection
, which is a property exposed by the ViewModel
- The Patients list is also bound to a ListBox or DataGrid, which would show the new list of patients
Notice that most of our communication is done through properties, using observable collections or property changed events to notify the view that it has changed. This means the View becomes reactive to our ViewModel.
From a unit testing perspective, this is a joy, because our tests become very state-based – we can set a property, and assert the contents of another property. We don't need to test the view at all, because we can assume that data binding just works.
In regular MVVM, our ViewModel is "owned" by the View – the View is bound to and depends upon the ViewModel, but the ViewModel has no direct reference to the View. A downside to this is that if the ViewModel wants to trigger the View to do something, it has to do so through raising an event, and hoping that the View interprets it correctly.
ViewModel-first MVVM is a variation of MVVM whereby the ViewModel also holds a reference back to the view.
Figure: note that the View-ViewModel communication now uses method calls on both sides. The ViewModel knows about the View, and can call methods directly on it.
This means that the ViewModel can still use properties and events to notify the View of changes, but since it holds a reference, it can also call methods, such as Close, on the View.
Since this communication means both objects are dependent on each other, we often introduce an interface for the View. The View would implement the interface, giving it the smallest contract needed by the ViewModel. The ViewModel would be coded against the interface, rather than the implementation.
From a testing point of view, this variation is slightly harder, since once we introduce the interface we need to use a mocking or stubbing approach to provide a test implementation of each interface. We then turn from state-based testing to interaction-based testing.
With Model-View-Presenter, we flip MVVM on its head. Instead of the View referencing the Third Object, the Third Object references the View:
Figure: in MVP, the Presenter tells the View what to do. The View can raise events to let the presenter know something happened.
As with ViewModel-first MVVM, we use an interface for the View to provide a tighter contract between the Presenter and View.
Taking our patient search scenario again, our interaction would become:
- When we click Search, the view would raise a SearchRequested event, with the text from the TextBox included in the event arguments
- The Presenter would be subscribed to this event from the view's interface
- The Presenter would call the web service
- When the results come back, the Presenter would call a method on the view, such as ShowResults(patients), passing the results to the view.
This pattern works well for ASP.NET and Windows Forms, but since data binding in WPF or Silverlight is so powerful, it's a shame to miss out on that – MVVM does a much better job of leveraging the power of WPF. From a testing point of view, since the presenter talks to the view, we again need to use interaction-based testing and mock objects to fully test our presenter.
While the point of these patterns is to reduce the complexity of our views by separating the two major categories of code, a nice benefit we get is increased testability. With just code-behind, our only option is automated UI testing.
With the separated presentation patterns, since most of the behavioural code is in one place, we can test it without involving the view. It doesn't mean we don't do automated UI testing – I like to still have automated UI smoke tests – but it allows all of the nitty-gritty permutations to be captured as simple unit tests, keeping our automated UI test suite smaller. The ROI from testing presenters or ViewModels is so great; we may even decide automated UI testing isn't worth the hassle.
Separated Presentation patterns exist to move behavioural code out of the code-behind, so it isn't mixed up with UI technology specific concerns. The patterns may sound daunting at first, but most people are surprised by how few lines of code are needed to introduce them.
No matter which pattern you use, if any at all, you'll need the View and Model. Then it's a question of interaction patterns. I often ask clients to implement an example of both, so they can see for themselves the benefits of each.
Hello, I'm Paul Stovell
I'm a Brisbane-based software developer, and founder of Octopus Deploy, a DevOps automation software company. This is my personal blog where I write about my journey with Octopus and software development.
I write new blog posts about once a month. Subscribe and I'll send you an email when I publish something new.Subscribe