Introduction
If you work with Silverlight or WPF, you have probably met the phrase “MVVM”. Almost everyone who is anyone in the WPF / Silverlight scene has their own MVVM framework, and their own way of explaining and teaching MVVM. Scary terms like IoC, Dependency Injection, Commanding Frameworks, Event Aggregators, Unit Testing, etc just roll off the tongue of the MVVM experts. This is one of the reasons why MVVM is intimidating for a lot of people. Still, you can create perfectly valid MVVM applications without even knowing what those terms mean. This post will show you my approach to MVVM, which is hopefully simple enough to get you started.
So, what is MVVM? There are tons of blog posts on the topic on the net - for example, take a look at John Papa’s 5 Minute Overview of MVVM in Silverlight. I won’t go into details, but here is my very short summary:
- MVVM allows for real separation between logic and presentation (or developer and designer).
- Model is the data that is displayed in the application. Model is created by a developer.
- View is the User Interface, that is defined in XAML (mostly). View is the responsibility of a designer.
- ViewModel is the logic behind the UI, it handles data retrieval and manipulation, follows the state of the application (such as whether the user has logged on or not). The ViewModel is owned by the developer, and is present in the View as the DataContext.
From our perspective, the interesting part is how the View and the ViewModel are communicating. There are two components to this: databinding (synchronizing data between the View and the ViewModel) and commanding – which is essentially a mechanism that allows the View to notify the ViewModel when user interaction has happened.
Databinding in Silverlight and WPF is pretty powerful. In my mind, the Zen of WPF and Silverlight development is:
“Get the data to be displayed to the client, and trust databinding and XAML – the designer - to do the rest”.
Similarly, the Zen of WPF and Silverlight design is:
“No matter how you want to display the data for the user, XAML and databinding can probably handle it. Only ask a developer to help you after you have tried really hard.”
On the other hand, I always had a bad taste in the mouth when it comes to the commanding facilities of Silverlight (even Silverlight 4). Commanding has two key features:
- Notify the ViewModel if something (usually a user interaction) happens in the View
- Disable UI elements when a command is not available (for example, the save button, icons, etc should be disabled if the user does not have the right to modify an item).
Here is why I don’t like commanding:
- For the designer to be able select a command in Blend, the developer has to create a lot of code – and usually that code simply invokes a method. There is RelayCommand and DelegateCommand to help with this though.
- The biggest issue I have with commanding is that it really ties the hand of the designer. What if I don’t just want to disable the Save button, I want to change its text to “Login first to Save”? What if I am a crazy designer and want to have all the buttons fly in from outside the screen when the user has logged in and is authorized to press them?
- Also, the CanExecute approach can be tedious for the developer. For example, after logging in, you have to re-evaluate pretty much all commands, and call CanExecuteChanged for all of them. For complicated tasks, when there are tons of commands, and their executability (is there such a word) is driven by database settings, the built-in approach may be the right one. But for simpler software, this can be overkill.
The key goals of my MVVM approach
For the project itself:
- Total separation of the designer and developer workflows
- Shallow learning curve for both the designer and the developer
- Allowing both the designer and the developer to remain in their comfort zone, and only use the tools they are familiar with
- Approachable solution for projects with low or medium complexity
For the designer:
- As much Blend support as possible (this is not totally achieved yet due to limitations in Blend 3)
- Use of Visual States to reflect different states of the application
- High level of flexibility
- Almost everything can be done in Blend and XAML
- Shallow learning curve: there is only an Action and a Behavior to learn about
- Create modern UI, including animations, transitions, custom skins, etc.
For the developer:
- Focus only on coding
- Display logic (ViewModel) code is unit testable, including user actions
- Application states can be expressed naturally, via enum values
- Ability to include techniques from other MVVM approaches, such as the MVVM Light Toolkit
So, what do I do instead of commanding?
I use an Action and a Behavior. The Action is for calling methods in the DataContext (that is, the ViewModel). It works similar to the CallDataMethod Action in the Expression Blend Samples project.
The CallDataContextMethodAction
In the attached sample project, there is a TextBox and a Button to illustrate the basic functionality of the CallDataContextMethodAction. The TextBlock is bound to the Message property of the ViewModel, and the Button has the CallDataContextMethodAction applied to it, with the method name set to ShowMsgBox.
Here is the relevant part of the ViewModel (which is set to be the DataContext of the entire form):
1: public string Message { get; set; }
2:
3: public void ShowMsgBox()
4: {
5: if (Message == null)
6: MessageBox.Show("Enter something in the textbox");
7: else
8: MessageBox.Show(Message);
9: }
Here is our Action in Blend:
If the Button is pressed, a MessageBox is shown, with the text entered into the TextBox:
Note: You shouldn’t show a MessageBox from a ViewModel because the designer has no way of modifying what it looks like and how it behaves. However, this is just a test application, and the message boxes are only here to illustrate that the ViewModel actually received the call
So far, it is pretty trivial. What is new in the CallDataContextMethodAction is that it can handle collections pretty well, too. As Glenn Block said in his excellent article The spirit of MVVM (ViewModel), it’s not a code counting exercise, you usually do not need to have parameters for commands. But there are some circumstances when you cannot avoid parameters, Consider the “Greetings to” list in the above picture. We want to display a greeting message for the person the user has clicked on. If the names are displayed in a ListBox, you can bind the SelectedItem property of the ListBox to a ViewModel property, and call the GreetPerson method by capturing the SelectionChanged event (there is a pretty handy EventTrigger built into Blend to help you with this). But then clicking on the same name again won’t fire the SelectionChanged event one more time. Also, what if the designer uses an ItemsControl instead of a ListBox? What if we want to greet the person not by clicking, but by doing some other, non-selective gesture? In this case, we have to have the GreetPerson method either on the person’s ViewModel, or some way to let the main ViewModel know which person we need to greet.
Here is the relevant code in the ViewModel (note that there is no SelectedItem as the names are displayed in a simple ItemsControl):
1: public MyViewModel()
2: {
3: Names = new ObservableCollection<string> {"John", "Smith", "Toby", "Emily"};
4: SG1 = PanelState.On;
5: }
6:
7: public void GreetPerson(string name)
8: {
9: MessageBox.Show("Hello, " + name);
10: }
The Action is now attached to the ItemTemplate:
But the items displayed in the ItemsControl are strings, and therefore the DataContext of the TextBlock is also a string. And a string does not have a GreetPerson method, so how does the Action work in this case? When the Action cannot find a method in the DataContext of the AssociatedObject (the element the Action is attached to, in our case, the TextBlock displaying the name), it tries to find the same method in the parent of the AssociatedObject, and its parent and so on. If such a method is found in the parent, it passes the DataContext (ViewModel) of the AssociatedObject to the parent method. By traveling up the VisualTree, we get to the MainPage, where the DataContext is the MyViewModel class shown above. This class has a GreetPerson method, so that method gets called. The GreetPerson method also has a parameter of string – this will be the DataContext of the TextBlock, thus the name of the person to be greeted. Note, that with CallDataContextMethodAction, we could use a strongly typed parameter for the method, unlike in the case of Commands where the parameter has to be an object.
The big drawback of the CallDataContectMethodAction is that the name of the Method has to be typed in by the designer, thus it introduces the possibility of human error. Hopefully, we will be able to extend the design time experience later on, so that the developer can simply select the Method from a list instead of typing its name.
The VSMChangerBehavior
The other piece of the puzzle is the VSMChangerBehavior. It ties enum values in the ViewModel to Visual States in the View. As VisualStateGroups are orthogonal (meaning each group can be in any state regardless of what states the others are in), this is a very powerful means of visually reflecting the internal states the ViewModel is in. Here is an example:
Suppose we have two panels that we want to show depending on various ViewModel calculations. We have an enum type of PanelState:
public enum PanelState { Off, On }
And here are the two enums:
1: [VisualStateGroup]
2: public PanelState Panel1
3: {
4: get { return _panel1; }
5: set
6: {
7: _panel1 = value;
8: FirePropertyChanged("Panel1");
9: }
10: }
11:
12: [VisualStateGroup]
13: public PanelState Panel2
14: {
15: get { return _panel2; }
16: set
17: {
18: _panel2 = value;
19: FirePropertyChanged("Panel2");
20: }
21: }
For both panels (Panel1 and Panel2) we have their own properties of type PanelState, that fire the PropertyChanged event when set. However, we are using the [VisualStateGroup] attribute to mark these enums as ones that have (or should have) a VisualStateGroup counterpart. Here is what the visual states look in Blend:
As you can see, we have a VisualStateGroup for each PanelState property, The states themselves are called Panel1_On, Panel1_Off, Panel2_On, Panel2_Off. These state names are calculated by concatenating the enum property’s name (which has the VisualStateGroup attribute), an underscore and the name of the enum value:
<PropertyName>_<ValueName>.
After this has been set up, all we have to do is to add the VSMChanger behavior to the Control that has the states we would like to change from the ViewModel:
From this point on, the behavior automatically changes the visual state of the UserControl as the enum values change within the ViewModel. This even includes state changes where the change is animated, as in the case in this simple demo. However, when the state transition is not instantaneous, the developer of the ViewModel may want to be notified when the transition has finished. For this, VSMChangerBehavior includes a callback mechanism. Simply name your callback method <PropertyName>TransitionComplete, and the method gets called when the transition has finished. For example, to show a MessageBox whenever Panel2 has been completely shown or hidden, have this in the ViewModel:
1: public void Panel2TransitionComplete()
2: {
3: MessageBox.Show("Panel2 Transition complete");
4: }
There is one big problem with this behavior, though. Just like in the case of the CallDataContextmethodAction, the designer has to type in the names of the visual states groups and states. As we are wiring up things based on convention, If he/she makes a mistake, the state change will not happen. To help ease this pain, I have added a “VerboseInitialization” property to the behavior.
If it is on, the behavior will write detailed information to the output stream (unfortunately this stream can only be viewed from Visual Studio):
This should help debugging typos and missing states / groups until I figure out how to bend Blend to my will add add a proper designer experience for this behavior.
Summary
So, there you have it. A designer-friendly way to wire up the View and the ViewModel. User gestures can be turned into commands via the CallDataContextMethodAction, and internal states and state changes of the ViewModel can be easily reflected via the VSMChangerBehavior. The later can also be used to disable / enable / hide UI elements in a declarative way that gives much more freedom to the designer than the CanExecute approach. Please let me know what you think in the comments!
You can download the solution with code here. You are free to do whatever you want with it, except say it is yours :)