by jesseliberty via Jesse Liberty - Silverlight Geek on 1/29/2010 2:15:46 AM
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.
But this is different. Honest.
Here are three heretical assertions about MVVM:
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.
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).
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.
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.
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.
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:
<UserControl x:Class="MVVMWithBehaviors.PeopleView" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d" d:DesignHeight="300" d:DesignWidth="400"> <Grid x:Name="LayoutRoot" Background="White"> <ListBox Name="Names" Background="Beige" Width="200" Height="250" HorizontalAlignment="Center" VerticalAlignment="Center"></ListBox> </Grid></UserControl>
<UserControl x:Class="MVVMWithBehaviors.MainPage" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d" xmlns:me="clr-namespace:MVVMWithBehaviors" d:DesignHeight="300" d:DesignWidth="400"> <Grid x:Name="LayoutRoot" Background="White"> <me:PeopleView HorizontalAlignment="Stretch" VerticalAlignment="Stretch" /> </Grid></UserControl>
1: using System.Collections.ObjectModel;
2: using System.ComponentModel;
3:
4: namespace MVVMWithBehaviors
5: {
6: public class PeopleViewModel : INotifyPropertyChanged
7: {
8: public class Person
9: {
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> People
19: {
20: get { return people; }
21: set { people = value; }
22: }
23:
24: public PeopleViewModel()
25: {
26: MockGetDataFromTheModel();
27: }
28:
29:
30: private void MockGetDataFromTheModel()
31: {
32:
33: string[] firsts = new[]
34: { "Tom", "Dick", "Harry", "Joe", "John", "Ringo" };
35: string[] lasts = new[]
36: { "Liberty", "Papa", "Hanselman", "Heuer", "Brown", "Gu" };
37:
38: var r = new System.Random();
39: people = new ObservableCollection<Person>();
40: for ( int i = 0; i < 10; i++ )
41: {
42: people.Add(
43: new Person(
44: firsts[ r.Next( firsts.Length ) ]
45: + " "
46: + lasts[ r.Next( lasts.Length ) ] ) );
47: }
48: }
49:
50: public event PropertyChangedEventHandler PropertyChanged;
51: private void OnPropertyChanged( string propertyName )
52: {
53: if ( PropertyChanged != null )
54: {
55: PropertyChanged( this, new PropertyChangedEventArgs( propertyName ) );
56: }
57: }
58: }
59: }
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.
ItemsSource="{Binding People}"DisplayMemberPath="Name"
DataContext = new PeopleViewModel();
c:\Program Files (x86)\Microsoft SDKs\Expression\Blend Preview for .NET 4\Interactivity\Libraries\Silverlight\System.Windows.Interactivity.dll
C:\Program Files (x86)\Microsoft Expression\Blend 3 Samples\Silverlight\Design\Expression.Samples.Interactivity.dll
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
<ListBox Name="Names" Background="Beige" Width="200" Height="250" HorizontalAlignment="Center" VerticalAlignment="Center" ItemsSource="{Binding People}" DisplayMemberPath="Name"> <i:Interaction.Triggers> <i:EventTrigger EventName="SelectionChanged"> <si:CallDataMethod Method="HandleSelectionChanged" /> </i:EventTrigger> </i:Interaction.Triggers></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…
public void HandleSelectionChanged(){ System.Windows.MessageBox.Show( "Update the state!" );}
… 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 decouplit’s the control from its data and logic. The technical term for this is “good.”
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”
Original Post: MVVM – It’s Not Kool-Aid*
The content of the postings is owned by the respective author. Silverlight Feeds is not responsible for the contents of the postings. This site is automatically generated and cannot be reviewed for abusive content. If you find abusive content on Silverlight Feeds, please contact us. Designated trademarks and brands are the property of their respective owners. All rights reserved.