MVVM – It’s Not Kool-Aid*

MiniTutorialLogo2

[ Revised with C# and VB.NET code]

Okay, first, understand that I’m in the position of running through the streets yelling at folks “c’mere! ya’ gotta see this!” and what I’m pointing to is the incredible new invention of… a laptop computer. Something that is undeniably amazing and cool, but everyone else on my block has already got one.

Second, and much worse, I’m about to show you how I used a “pattern” that you either have already embraced, or that you’ve been avoiding like the plague because the folks who are running around shouting “MVVM! MVVM!” sound just like the folks who were running around shouting “MVC! MVC!” and “OOP! OOP!” and “COM! COM!”…  you get the idea.

Many of us are still recovering from the last five fads that caused us to go out and buy dozens of books and break our head on the latest/greatest trend, only to have it be “oh so last year” by the time we fully grokked it.

Drinking The Kool Aid*

Kool Aid Man

But this is different. Honest.

Here are three heretical assertions about MVVM:

  1. It is not all that different from what you are already doing
  2. It is not hard to understand or to do
  3. You will write better code, and you’ll write less code.

Typically, when a pattern or practice comes along, there is a steep learning curve, and the cognoscenti will tell you that it takes a very long time to truly master the approach. Feh. And not so, at least not this time. Let’s go over the assertions above, and I’ll explain, briefly, what you need to know to profit from MVVM.

Disclaimer: I’m not saying this is all you’ll ever need to know about MVVM, I’m saying if you know this, you can benefit from MVVM on any non-trivial application, while you’re learning more.While I’m disclaiming, please note that I’m writing very specifically about a Silverlight 4 project and that I’ve been coding with MVVM for about a half hour.

It’s Not All That Different and It’s Not That Hard

We’ve been talking about n-tier development, decoupling, scoping, visibility and related topics since at least 1990. I’m pretty sure that when they were cracking the Enigma machine in World War II, they discussed decoupling the code-breaker module from the UI (did they have UI then?)

MVVM, at its heart has three core concepts, only the third of which is new, but that difference is all the difference in the world when you’re writing a Silverlight application that you want to be able to maintain and create unit tests for (Yes, I owe a blog post on Test-Driven design, but one glass of grape juice at a time).

MVVMSketch

Core Concept #1:  Separate your User Interface concerns (View) from your Business objects and behaviors (View Model) and from your data/persistence layer (Model).

Core Concept #2 Don’t Look Up.

We tend to conceptualize the View (User Interface objects) at the top, the ViewModel (objects that provide the UI with its data and behaviors) in the middle and the model (often the persistence layer) at the bottom.  The View can know about the ViewModel, the ViewModel about the Model, and the Model, like the cheese, stands alone.

Core Concept #3 – And this is the killer: Binding.

In MVVM the mechanism for the ViewModel to provide data to the View, is for the View to set the ViewModel as its DataContext.  That is so cool; it takes a while to realize the implications.  Further, we don’t just bind data, as I’ll show below.

Why Would You Want To Do That, & What Does It Cost?

The huge advantages of using binding and making the VM the datacontext for the View is that you write less code and, equally important, your behaviors and state are all separated from the UI and thus you have enormously increased the testability of your application. (It is a bear to try to test a UI object because the pesky UI gets in the way. ViewModels have no UI, they have just the things you want to test: “does it behave as expected? and is the data correct at any given instant?”

So, the cost is negative; that is, by adopting MVVM you don’t work harder, you work less, and in exchange for doing less, your code is easier to write, to read, to maintain and to test. Not bad.

Names

Simplifying is one thing, over-simplifying another, and there is an elegance in the chosen names.

The Model is that which the application is modeling. Calling this the database layer or the persistence layer loses site of the fact that the  model might be virtually any time of information in virtually any format.

Calling the top layer the View, rather than the User Interface is important both to emphasize that it is just one of many possible views of the model, and to keep clear that the User Interface comprises both the appearance and the behaviors and the View is concerned only with the appearance.

The ViewModel is the bridge between the Model and the View; and the ViewModel thus owns responsibility for binding the relevant data to the view and for handling user actions appropriately, whether the response is in the widget, elsewhere in the View or in other parts of the application.

A Practical Example

To see this at work, we’ll start by creating a new Silverlight Application. (Complete source code is available here)

Immediately add a new UserControl and name it PeopleView. Here is the Xaml for the UserControl:

01 <UserControl
02 x:Class="MVVMWithBehaviors.PeopleView"
03 xmlns=
04 "http://schemas.microsoft.com/winfx/2006/xaml/presentation"
05 xmlns:x=
06 "http://schemas.microsoft.com/winfx/2006/xaml"
07 xmlns:d=
08 "http://schemas.microsoft.com/expression/blend/2008"
09 xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
10 mc:Ignorable="d"
11 d:DesignHeight="300"
12 d:DesignWidth="400">
13 <Grid
14 x:Name="LayoutRoot"
15 Background="White">
16 <ListBox
17 Name="Names"
18 Background="Beige"
19 Width="200"
20 Height="250"
21 HorizontalAlignment="Center"
22 VerticalAlignment="Center"></ListBox>
23 </Grid>
24 </UserControl>
For completeness, here is the Xaml for MainPage.xaml
01 <UserControl
02 x:Class="MVVMWithBehaviors.MainPage"
03 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
04 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
05 xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
06 xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
07 mc:Ignorable="d"
08 xmlns:me="clr-namespace:MVVMWithBehaviors"
09 d:DesignHeight="300"
10 d:DesignWidth="400">
11 <Grid
12 x:Name="LayoutRoot"
13 Background="White">
14 <me:PeopleView
15 HorizontalAlignment="Stretch"
16 VerticalAlignment="Stretch" />
17 </Grid>
18 </UserControl>
Note: If my solution were named HVP I’d have the following projects to support this model:HVPHVP.Web

HVP.Model

HVP.ViewModel

HVP.View

HVP.Tests

The advantage, for me, is that when I wish to touch a method or property in another class, the compiler will let me know if I’m crossing a project boundary (I’ll need a using statement) and that is a great time to double check that I have my visibility correct (e.g., a class in the View project can include ViewModel but not vice versa)

Next we’ll create PeopleViewModel, as a class.  The job of this class is to expose the properties (and as you’ll see, the methods) that we want the View to bind to.  Specifically, we want the ListBox within the view to bind its ItemsSource property to a collection of People, and we want the Person object to have a property which will display the name of that person. The complete source code is shown here:

C#

01 using System.Collections.ObjectModel;
02 using System.ComponentModel;
03
04 namespace MVVMWithBehaviors
05 {
06 public class PeopleViewModel : INotifyPropertyChanged
07 {
08 public class Person
09 {
10 public string Name { get; private set; }
11 public Person( string name )
12 {
13 this.Name = name;
14 }
15 }
16
17 private ObservableCollection<PERSON> people;
18 public ObservableCollection</PERSON><PERSON> People
19 {
20 get { return people; }
21 set { people = value; }
22 }
23
24 public PeopleViewModel()
25 {
26 MockGetDataFromTheModel();
27 }
28
29 private void MockGetDataFromTheModel()
30 {
31
32 string[ ] firsts = new[ ] { "Tom", "Dick", "Harry", "Joe", "John", "Ringo" };
33 string[ ] lasts = new[ ] { "Liberty", "Papa", "Hanselman", "Heuer", "Brown", "Gu" };
34
35 var r = new System.Random();
36 people = new ObservableCollection </PERSON><PERSON>();
37 for (int i = 0; i  < 10; i++)
38 {
39 people.Add(
40 new Person(
41 firsts[r.Next( firsts.Length )]
42 + " "
43 + lasts[r.Next( lasts.Length )] ) );
44 }
45 }
46
47 public event PropertyChangedEventHandler PropertyChanged;
48 private void OnPropertyChanged( string propertyName )
49 {
50 if (PropertyChanged != null)
51 {
52 PropertyChanged( this, new PropertyChangedEventArgs( propertyName ) );
53 }
54 }
55 }
56 }

VB.Net

01 Imports System.Collections.ObjectModel
02 Imports System.ComponentModel
03
04 Namespace MVVMWithBehaviors
05 Public Class PeopleViewModel
06 Implements INotifyPropertyChanged
07 Public Class Person
08 Private privateName As String
09 Public Property Name() As String
10 Get
11 Return privateName
12 End Get
13 Private Set(ByVal value As String)
14 privateName = value
15 End Set
16 End Property
17 Public Sub New(ByVal name As String)
18 Me.Name = name
19 End Sub
20 End Class
21
22 Private people_Renamed As ObservableCollection(Of Person)
23 Public Property People() As ObservableCollection(Of Person)
24 Get
25 Return people_Renamed
26 End Get
27 Set(ByVal value As ObservableCollection(Of Person))
28 people_Renamed = value
29 End Set
30 End Property
31
32 Public Sub New()
33 MockGetDataFromTheModel()
34 End Sub
35
36 Private Sub MockGetDataFromTheModel()
37
38 Dim firsts() As String = { "Tom", "Dick", "Harry", "Joe", "John", "Ringo" }
39 Dim lasts() As String = { "Liberty", "Papa", "Hanselman", "Heuer", "Brown", "Gu" }
40
41 Dim r = New System.Random()
42 people_Renamed = New ObservableCollection (Of Person)()
43 For i As Integer = 0 To 9
44 people_Renamed.Add(New Person(firsts(r.Next(firsts.Length)) & " " & lasts(r.Next(lasts.Length))))
45 Next i
46 End Sub
47
48 Public Event PropertyChanged As PropertyChangedEventHandler Implements INotifyPropertyChanged.PropertyChanged
49 Private Sub OnPropertyChanged(ByVal propertyName As String)
50 RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(propertyName))
51 End Sub
52 End Class

We start by defining a Person (lines 6-15) and then we give the PeopleViewModel class a property People which is an ObservableCollection of Person objects.  The constructor (lines 24-27) calls a method that mocks up getting data from the Model; in this case I just generate ten names on lines 30-48   The final code implements INotifyPropertyChanged, which is not required yet, as the only property we have is an Observable collection.

Binding

We are now ready to bind the ListBox inside the View class to the ViewModel.  Add the following two lines to the ListBox declaration in PeopleView.xaml
ItemsSource="{Binding People}"DisplayMemberPath="Name"

and then add this to the constructor in the code behind:

C#

1 DataContext = new PeopleViewModel();

VB.Net

1 DataContext = New PeopleViewModel()
Running the application will now display the data from the ViewModel in the View

Behavior

The design of the Silverlight HVP says that when an object changes its state (e.g., the user changes the current item in a list box) the View object will notify the state object of the change. The State object then raises the StateChanged event to which all the View objects have subscribed, and each resets its state.
It would be nice to avoid having code in the View to manage the State Change notification; we’d like to bind this as well as the data, thus increasing the overall testability of the project.  Unfortunately, as of now, there is little support for this built into Silverlight (though there is some, using ICommand, with ButtonBase and HyperLink derived classes)
The solution is to be friends with John Papa, who pointed me to the Expression Blend Samples project on CodePlex, which, when installed, provides a library that includes, among other things, the wondrous CallDataMethod, enabling you to trigger a method in your ViewModel based on an event in your view(!)
Since I had to spend quite a bit of time figuring out how to make this work, I thought I’d walk through it here. It is very easy, once you know how, but the project doesn’t yet provide the necessary instructions. Begin by going to the Expression Blend Samples page and downloading the latest release (which currently is from July 12, 2009 and marked Alpha.  This will put a .msi file on your disk, and running that will install the samples; unfortunately it will do so shockingly quietly, and you’ll have no idea what to do next.
Here’s what you do:  in your project you’ll need to add two references. The first is to System.Windows.Interactivity which on my machine is in:
c:\Program Files (x86)\Microsoft SDKs\Expression\Blend Preview for
.NET 4\Interactivity\Libraries\Silverlight\
System.Windows.Interactivity.dll
the second is Expression.Samples.Interactivey, which on my machine was in:
C:\Program Files (x86)\Microsoft Expression\Blend 3
Samples\Silverlight\Design\Expression.Samples.Interactivity.dll
You’ll now modify the view class to include the namespaces:
xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=
System.Windows.Interactivity"xmlns:si="clr-namespace:Expression.Samples.Interactivity;assembly=
Expression.Samples.Interactivity"

and finally, you modify the control (the listbox) to add the trigger and behavior

01 <ListBox
02 Name="Names"
03 Background="Beige"
04 Width="200"
05 Height="250"
06 HorizontalAlignment="Center"
07 VerticalAlignment="Center"
08 ItemsSource="{Binding People}"
09 DisplayMemberPath="Name">
10 <i:Interaction.Triggers>
11 <i:EventTrigger
12 EventName="SelectionChanged">
13 <si:CallDataMethod
14 Method="HandleSelectionChanged" />
15 </i:EventTrigger>
16 </i:Interaction.Triggers>
17 </ListBox>

The EventTrigger has a property for the EventName; indicating that this trigger will fire when the event SelectionChanged is raised for the ListBox.  The CallDataMethod has a property Method for the name of the method to invoke. Since the ViewModel class is the data context, you don’t need to indicated which class supplies the method, any more than you need to indicate which class has the property People to which the ListBox’s ItemsSource is binding.

Just add that method to the ViewModel class…

C#

1 public void HandleSelectionChanged(){   System.Windows.MessageBox.Show( "Update the state!" );}

VB.Net

1 Public Sub HandleSelectionChanged()
2 System.Windows.MessageBox.Show("Update the state!")
3 End Sub

… and start your program. When you change the selection, the method you’ve bound to will be called.

In the Silverlight HVP this will allow us to bind the event notification to the SelectionChanged event, further decouples the control from its data and logic. The technical term for this is “good.”

Overall Impact of Refactoring for MVVM

While this  is not an exhaustive understanding of MVVM by any means, with these fundamentals, it became obvious how to break up my code, and I found that there was less of it, and it was more intention-revealing.

In fact, the code-behind for my View classes have no code at all except setting the dataContext; and the ViewModel code is short and extremely readable.

All in all, I find MVVM well worth the small cognitive startup costs; yielding a very natural separation of concerns, and perhaps equally important, exposing far more of the program to unit tests, and thus driving down the overall time to release.

———————————–

* In 1978 cult leader Jim Jones induced 900 followers to commit “revolutionary suicide” by knowingly drinking cyanide-laced grape Flavor Aid. To the chagrin of General Foods, the cultural memory of the event is that they drank Kool Aid and the expression. To drink the Kool Aid has evolved to mean “to embrace without reservations, the ideas of a strong leader”

About Jesse Liberty

Jesse Liberty has three decades of experience writing and delivering software projects and is the author of 2 dozen books and a couple dozen online courses. His latest book, Building APIs with .NET will be released early in 2025. Liberty is a Senior SW Engineer for CNH and he was a Senior Technical Evangelist for Microsoft, a Distinguished Software Engineer for AT&T, a VP for Information Services for Citibank and a Software Architect for PBS. He is a Microsoft MVP.
This entry was posted in Mini-Tutorial, Patterns & Skills and tagged . Bookmark the permalink.

30 Responses to MVVM – It’s Not Kool-Aid*

Comments are closed.