In part one I described how to control Storyboard animations using triggers. I created a demo application to provide an example use case. In this post I will discuss converting the demo application to adopt the Model-View-ViewModel (MVVM) pattern as well as describing why this is a worthwhile exercise.
MVVM
What is MVVM? It is a pattern that was tailor-made for developing UI applications in WPF. It is an evolution of the Model-View-Controller (MVC) pattern, providing a clear separation of concerns between designing a UI and developing the application behind a UI. MVVM was introduced in 2005 by John Gossman and is very similar to the Presentation Model (PM) pattern that Martin Fowler introduced in 2004. John describes the ViewModel as an abstraction of the View that can also translate the data in the Model to a format that the View can consume. The ViewModel is completely decoupled from the View and knows nothing about the various Views that can bind to it. For this reason there is a one-to-many relationship between the ViewModel and the View. Josh Smith wrote a great Blog post detailing the evolution of MVVM. His post appears to be the authority on MVVM, judging by the number of other Blogs and stack overflow responses that reference it .
PM
The PM pattern also introduces this idea of an abstraction of the View. Unlike MVVM, which was designed specifically for WPF and Silverlight, PM is a platform independent UI pattern, .
MVC
In MVC the Model is the application, the View displays the application UI and the Controller decides how to update the Model and the UI. The View is bound to the Model so that any changes in the Model can be replicated in the View. The Controller updates the Model based on input external to the View and the Model. This input could come from interaction with the View but it is the Controller that handles it rather than the View. There is a one-to-many relationship between the Controller and the View in MVC. Contrary to MVC, the View is decoupled from the Model and the View handles the input rather than ViewModel in MVVM.
MVP
The Model-View-Presenter (MVP) pattern, which is a subset of MVC, is another similar UI pattetn. There are two varieties of MVP: Passive View and Supervising Controller. I will describe the Passive View, but you can find out more about Supervising Controller and the differences between the two on MSDN. In MVP-Passive View the Presenter acts as the glue between the Model and the View. It will subscribe to any events that are raised by both the Model and the View. It communicates with the View through an interface, ensuring that the View is decoupled from the Presenter, lending itself well to ease of testing in isolation through mocking. All state is managed in the Presenter and the View contains little to no logic. The main difference between MVVM and MVP is that the Presenter holds a reference to the View that it is associated with but the ViewModel is completed decoupled from the View. In MVVM the View binds to the relevant properties in the ViewModel, which in turn exposes the data in the Model.
Why Use MVVM?
When UI applications become very complex, using well defined and understood patterns to organise the code makes developers’ lives easier when collaborating on, re-factoring and extending such applications. These patterns become the common language that developers use to convey intent. Sometimes the patterns themselves can be complex out of necessity because the platform itself doesn’t make it convenient to do what you need.
The MVVM pattern utilizes three major features of WPF to decouple the View from the ViewModel; namely, data binding, data templates and the resource system. Decoupling the View from the ViewModel is achieved through the data binding infrastructure, exposing the data in the Model to the View through the bound properties in the ViewModel. Data templates support mapping Views to ViewModels in XAML, and the resource system automatically applies these data templates at runtime.
Since the ViewModel is an abstraction of the View, many Views can bind to a single ViewModel displaying the data differently each time. Therefore the cost of updating an application’s UI is reduced to just the View, enabling rapid prototyping during the development of a UI. Another benefit of this abstraction is the fact that testing the logic in the ViewModel is easier as it can be unit tested without the need to instantiate a View, leading to early validation identifying any need for adjustment sooner. Finally updating the logic in the ViewModel doesn’t require changes in the View as long as the properties that the View binds to remain the same, allowing for fast iteration on the ViewModel
Converting the PanelScroller Application to use MVVM
The application that I developed in the previous post relied on logic in the code-behind the View. The View was bound to dependency properties (DP) that were defined in the code-behind. Testing this logic is a bit involved as it requires instantiating the View. Therefore I moved all of the code from the code-behind into a ViewModel. As a result I couldn’t use DPs for the properties that the View was bound to. Instead the View must bind to standard properties in the ViewModel. The ViewModel must also implement the INotifyPropertyChanged interface so that it can broadcast a PropertyChanged event each time a bound property in the ViewModel is updated. I created a base class called PropertyChangedNotifier that each of the ViewModels could inherit from in order to take advantage of this machinery:
1 2 3 4 5 6 7 8 9 10 11 12 |
public sealed class PanelsControl : PropertyChangedNotifier { private Nullable<float> mScrollLeftWidth = 0.0f; public Nullable<float> ScrollLeftWidth { get { return mScrollLeftWidth; } set { mScrollLeftWidth = value; OnPropertyChanged("ScrollLeftWidth"); } } ... } |
Deciding which View to apply to a ViewModel can be done in a ResourceDictionary ensuring that instantiating a ViewModel in code automatically results in the specified View being created and automatically applied to it. This is a ViewModel-First approach to hooking up the ViewModel and the View:
1 2 3 4 5 6 7 8 9 |
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:vm="clr-namespace:ScrollingAnimations.ViewModel" xmlns:v="clr-namespace:ScrollingAnimations.View"> <DataTemplate DataType="{x:Type vm:PanelScrollerControl}"> <v:PanelScrollerControl /> </DataTemplate> ... </ResourceDictionary> |
The logic from the code-behind didn’t change much and most of the properties remained the same. However the event handlers that were in the code-behind couldn’t be used in the ViewModel. Rather than relying on the relevant routed events being raised I had to trigger ViewModel ICommand properties from the XAML. I use a custom DelegateCommand type which allows you to specify a delegate, that has access to the ViewModel data, to run in the Execute method for each command. The Publish-Subscribe pattern enables communication between ViewModels through a shared pseudo Message Bus.
Even though I don’t use the code-behind event handlers anymore, I still need a way to hook onto the relevant mouse button clicked event so that these new commands are executed once an event is raised. This isn’t possible with the standard EventTrigger in WPF. However it is possible using the Expression Blend framework.
Behaviours – Expression Blend
Expression Blend can be downloaded from the Microsoft Website here. I used Blend for Visual Studio (VS) 2013 when developing the attached demo application. You will need to add a reference to the Microsoft.Expression.Interactions.dll and System.Windows.Interactivity.dll assemblies in your VS project. There are different flavours of the assemblies that target different versions of the .Net Framework (I reference the .Net 4.5 Framework variants). Accessing the Blend types in XAML requires referencing the namespaces from the relevant assemblies:
1 2 3 4 5 |
<UserControl ... xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity" xmlns:ic="clr-namespace:Microsoft.Expression.Interactivity.Core;assembly=Microsoft.Expression.Interactions" xmlns:im="clr-namespace:Microsoft.Expression.Interactivity.Media;assembly=Microsoft.Expression.Interactions"/> |
Invoking a command once an event is raised can be achieved using Blend Behaviours via the EventTrigger from the System.Windows.Interactivity namespace:
1 2 3 4 5 |
<i:Interaction.Triggers> <i:EventTrigger EventName="PreviewMouseDown"> <i:InvokeCommandAction Command="{Binding ScrollRightCommand}"/> </i:EventTrigger> </i:Interaction.Triggers> |
1 2 3 4 5 6 7 |
private readonly ICommand mScrollRightCommand; public ICommand ScrollRightCommand { get { return mScrollRightCommand; } } ... mScrollRightCommand = new DelegateCommand(() => {...}); |
Another great feature of Blend is the ability to use triggers for triggering Storyboard animations that target a named element. This can only be achieved using templates as described in the previous post, however using a Blend DataTrigger and ControlStoryboardAction provides the same functionality without the need for a template:
1 2 3 4 5 |
<i:Interaction.Triggers> <ic:DataTrigger Binding="{Binding ScrollLeft}" Value="True"> <im:ControlStoryboardAction Storyboard="{StaticResource OnScrollLeft}" /> </ic:DataTrigger> </i:Interaction.Triggers> |
ViewModel Testing
Adding unit tests for the ViewModels is now a straight forward task since the ViewModel can be instantiated without the need to instantiate a View.
Summary
Using the MVVM pattern has some major advantages over adding logic to View code-behind files including ease of testing, extensibility and maintainability. I have demonstrated this through the development of the application associated with this post.
Source Code
The source code can be found on Bitbucket.