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”

Share

About Jesse Liberty

Jesse Liberty is a Master Consultant for Falafel Software, and has three decades of experience writing and delivering software projects. He is the author of 2 dozen books and multiple Pluralsight courses, and has been a Technical Evangelist for Telerik and for Microsoft, a Distinguished Software Engineer for AT&T, a VP for Information Services for Citibank and a Software Architect for PBS.
This entry was posted in Mini-Tutorial, Patterns & Skills and tagged . Bookmark the permalink.

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

  1. Nick Dowling says:

    Small grammar correction – ‘loses site of the fact’ should be ‘loses *sight* of the fact’ and ‘any time of information in virtually any format’ should be ‘any *type* of information in virtually any format’ (I think)

  2. Server Automator says:

    “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” IS BS! You WILL always write more code using MVVM rather than using code behind. YOu WILL be working harder (because you write more code). It is NOT easier to read. Debatable if more code is ever simpler to maintain. Testing is simpler because the VM tier is unit testable which has always been a challenge for WinForms & WPF with code behind.

    For a button using code behind: OnClick(TheMethodName). Done. Right click it, go to code, and BAM you are in the event that gets raised. Going to do that with MVVM? NO WAY! Impossible. It’s a handful of extra code, you can not go directly to the event.

    Want a dialog window? Using(formobject form=new formobject()){form.showdialog();} and it disposes itself automatically. MVVM equivalent? There is NONE unless you write a form controller and all of the code necessary to manage a dialog window (any sizing, title bar, moving events etc etc etc…good luck under 150 lines of code).

    MVVM is great, and I use it with WPF all the time. But it’s not simpler. It’s not faster to write. And, it is not easier to read.

  3. Tevin says:

    Thanks very much, Help a lot ; )

  4. Dim custID As Guid = (CType(V2_CustomerDataGrid.SelectedItem, _
    v2_Customer)).Cust_UUID

    Dim loadOp = context.Load(context.GetCustDetailByIDQuery(custID))

    ‘context.Load(context.GetCustDetailByIDQuery(custID))
    ‘CustomerDataForm1.ItemsSource = loadOp.Entities
    ‘aaaa”””””””””””””
    Dim taskList As ObservableCollection(Of v2_Customer) = New ObservableCollection(Of v2_Customer)
    ” Dim custID As Guid = (CType(V2_CustomerDataGrid.SelectedItem, _
    ” v2_Customer)).Cust_UUID
    ” Generate some task data and add it to the task list.
    For index = 0 To taskList.Count – 1
    taskList.Add(New v2_Customer() With _
    {.Cust_UUID = custID, .Billing_Zip = .Billing_Zip
    })
    Next

    Dim taskListView As New PagedCollectionView(taskList)
    Me.CustomerDataForm1.ItemsSource = taskListView

  5. jeanine says:

    Great post with simple approach to raising data events. Just what I needed. For VS2010 and Silverlight 4, the Expression.samples.interactivity needs to be changed to Microsoft.Expression.Interactivity.Core and include that reference as well. The CallDataMethod uh method has been changed to CallmethodAction. so changes — si:CallMethodAction MethodName=”TrimestersClicked”

    The MSDN for this is here –> http://msdn.microsoft.com/en-us/library/microsoft.expression.interactivity.core.callmethodaction_members(v=expression.40).aspx

  6. Pingback: Model-View-ViewModel « MolePlex

  7. Pingback: Visual Basic Code Examples | Pozitive.NeT

  8. Paul says:

    It cut off my code. take 2:

    <i:Interaction.Triggers>
    <i:EventTrigger EventName=”SelectionChanged”>
    <si:CallDataMethod Method=”HandleSelectionChanged” />
    </i:EventTrigger>
    </i:Interaction.Triggers>

  9. Paul says:

    I downloaded Jason Quin’s code and made the following change to PeopleView.xaml and got it to work:


  10. Rod says:

    @Jason Quinn: Ok, I downloaded your version and it now builds (using VS010 pro) but the selection changed event does nothing?

    In this original example it calls the method HandleSelectionChanged on the VM, but on yours it calls “ViewModelMethod” which does nothing. I tried changing this to the method “HandleSelectionChanged” but it still does not fire.

    Has anyone got a working version of this in 2010? thanks

  11. Rod says:

    I could not get the source to build (using SL4 and VS2010) –

    Error 1 The tag ‘CallDataMethod’ does not exist in XML namespace ‘clr-namespace:Expression.Samples.Interactivity;assembly=Expression.Samples.Interactivity’
    and

    Error 2 A value of type ‘CallDataMethod’ cannot be added to a collection or dictionary of type ‘TriggerActionCollection’.

    But when I browse with the Object Browser it is there?

  12. Pingback: When should you use the MVVM Pattern | Scott Koland's blog

  13. Richard Ebel says:

    Richard Ebel :It works with VS EX 2010 and Silverlight 4 and the following changed PeopleView:


  14. Richard Ebel says:

    It works with VS EX 2010 and Silverlight 4 and the following changed PeopleView:


  15. Jason Quinn says:

    I’ve fixed the code sample so it works now with visual studio 2010 and Expression Blend 4. It can be found at http://cid-136dba83ff6950a3.office.live.com/browse.aspx/Public/Code%20Samples

    • jeanine says:

      Can you give me more here? You have 4 projects there and none are really labled. Or just give the information. Thanks!

  16. Pingback: How to Implement MVVM, INotifyChanged and ICommand in a Silverlight Application | Arrange Act Assert

  17. Pingback: Why Developers Should, Must, Do Care About The New Expression Blend | Jesse Liberty

  18. @Per H
    Thanks for taking the time to post this after working it out. Your comment was cut off, if you get a chance to fill in the details, that will be terrific.

  19. mark says:

    Download code doesn’t build. It’s a well written article code. Thanks for the effort. I’ll see if I can get it to compile.

  20. Bryon says:

    @Per H , where are these libraries found? I’m using VS 2010 and Expression Blend 4.

  21. DelC says:

    @Jesse Liberty

    That would be Kool ;-) I’ll try and see if I can get something done in the next week. If I don’t then I’ll fess up to being lazy and leave it for you to take it on. Cheers.

  22. DelC says:

    Hi Jesse,

    Good post, particularly on how simply you can use the Blend Samples to extend the commanding.

    I know you weren’t trying to write the definitive guide to MVVM but it would be good to have a little more on the View Model/Model relationship as mocking up the data within the VM avoids that somewhat.

    We’ve been on the MVVM kool aid since last year and it’s definitely the way to go, although sometimes I think the MVVM police might be about to storm in.

    Some of the team got to see you on your UK visit – thanks again for that.

    • I agree, and if you would like to write something and have me chime in and send it back and once done publish here with joint authorship, that would be a gas. If not, it is definitely on my follow-up list. :-)

  23. Exactically!

    @Jeff Ballard

    Jeff Ballard :

    Am I correct in stating that the part with the codeplex expression samples and interactivity is basically a way to implement commanding for non button and hyperlink base controls in MVVM?

  24. @Ken Byrd
    The problem is that I didn’t explain what I wanted to do, and so you are totally right wrt what i wrote, but not what i meant

    What I was trying to show was an example where a change in the state causes you to want to invoke a method (not just update the view).

  25. Jesse,

    Are you breaking any MVVM rule when calling System.Windows.MessageBox.Show in the ViewModel?

    Regards,

  26. Ken Byrd says:

    Why would this method be preferable to simply implementing a SelectedPerson property on the ViewModel?

    {ListBox ItemsSource=”{Binding People}”
    SelectedItem=”{Binding SelectedPerson, Mode=TwoWay}” />

    Within your ViewModel, you could simply watch for changes to the SelectedPerson property which wouldn’t require 3rd party codeplex code!

  27. Per H says:

    Hi. This is a great example, and I agree that one has to apply a little KISS to MVVM in order to get your brain around it! However, using VS2010RTM and Blend4 beta, I think the following needs to be changed?

    1) I updated the references and the XAML:

    xmlns:si=”http://schemas.microsoft.com/expression/2010/interactivity”
    xmlns:sa=”http://schemas.microsoft.com/expression/2010/interactions”

    2) and changed the CallDataMethod to CallMethodAction like this:

  28. JasonBSteele says:

    And I thought references to drinking Kool Aid were more to do with Kool Aid Acid Test… which is more to do with seeing things that aren’t there! ;)

  29. zire says:

    Heh ;) Hi Jesse I like it very much your intro in this post . Trend’s killing our precious time.

  30. Jeff Ballard says:

    Jesse,

    Am I correct in stating that the part with the codeplex expression samples and interactivity is basically a way to implement commanding for non button and hyperlink base controls in MVVM? I was trying to do something similar using Prism and was having trouble getting it to work although I did have only a few minutes to troueshoot before I had to leave for a family event. :)

    Great article!

Leave a Reply

Your email address will not be published.

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>