Freigeben über


Implementing the Model View ViewModel pattern

In this third part of my series of blog posts on the ViewModel pattern, I’m going to demonstrate how to implement a simple example of the ViewModel pattern. Not to worry, we’ll dive into more exiting examples later.

<Back to intro>

<Back to: The difference between MVVM and other Separated presentation patterns>

<Next: Adapting a simple vs complex model>

The ViewModel

So what is a ViewModel? And how does it differ from a View or the Model. Consider the following example. Suppose you would build Paint using WPF or Silverlight, using the MVVM pattern:

image

The View obviously determines what is displayed on the Screen. And the Model, well that will likely be the Image. But the application works with more data than ‘just’ the Document. This extra data is mostly the State of the UI. What brush is currently selected? What is the ‘zoom’ percentage? While it is possible to store this information in the View itself, this is usually a bad practice. For example, the zoom slider bar could be the place where you would store the zoom percentage. However, when more elements on the screen need to use the Zoom percentage, this value quickly becomes very hard to manage.

Model for the View

The ViewModel is a model for the view. Therefore, it hold’s all the state of the view as well as the logic that operates on the state. The ViewModel exposes this data and logic in such a way that the View can very easily access it. However, the ViewModel should not expose or interact directly with UI elements. 

Another responsibility of the ViewModel is to adapt the Model to the View. Typically the Model is a domain model (we’ll dive into that more in a later blog post). If you have full control over the model, then typically you can build it so that the model itself is very easily databindable. However, if you don’t have control over the model, for example, you are getting it from an external system, then the ViewModel might need to adapt the model to the view. For example, by adding aggregations, combining information from different models or simply by implementing INotifyPropertyChanged to enable the ViewModel notifying the View that properties have changed.

The MVVM pattern

So the following picture shows the MVVM pattern as how you would typically implement it in WPF or Silverlight:

image

The View is typically implemented as a UserControl or DataTemplate (in WPF). It uses Databinding to read and update information on the ViewModel. For example, the ViewModel can expose a public property Name that can be bound to a textbox.

If the view needs to communicate to the ViewModel, there are several options:

Commands

To route the Click event from a button to a ViewModel, the easiest way is to implement a Command. The Command pattern describes an object that can invoke functionality at a later point in time. In this case, when the user clicks a button. We’ll dive into commands later in this post.

This defines the Commands in XAML:

    1: // Define the Commands
    2: public ICommand ShowCalorieKingInfoCommand { get; private set; }
    3: public ICommand ShowIngredientInfoCommand { get; private set; }
    4:  
    5: // Initialize the commands in the constructor with a delegate command.
    6: public MealDetailsViewModel(IDialogService dialogService)
    7: {
    8:     this.ShowCalorieKingInfoCommand = new DelegateCommand<object>(ShowMealCalorieKing);
    9:     this.ShowIngredientInfoCommand = new DelegateCommand<Ingredient>(ShowIngredientInfo, CanShowIngredientInfo);
   10: } 
   11:  
   12: // The methods called when the commands are invoked
   13: private void ShowMealCalorieKing(object notUsed) {..}
   14: private bool CanShowIngredientInfo(Ingredient selectedIngredient) {..}
   15: private void ShowIngredientInfo(Ingredient selectedIngredient) {..}

This snippet shows an example how to bind a command to a button in XAML

 <Button Margin="10, 10, 10, 10" Content="Show information on CalorieKing.Com" 
        Commands:Click.Command="{Binding ShowCalorieKingInfoCommand}"/>

Pretty much only buttons allow you to use Commands out of the box. Now it’s possible (and not too hard) to create your own commands using attached behaviors).

2 way bindable properties

You can also use TwoWay databinding to notify the ViewModel that something has changed. For example, the following codesnippet shows how the DataGrid Control has a SelectedItem property. If you TwoWay databind this property to a property on the ViewModel.

Here’s the property in C#:

    1: public Ingredient SelectedIngredient
    2: {
    3:     get { return _selectedIngredient; }
    4:     set
    5:     {
    6:         if (_selectedIngredient == value)
    7:             return;
    8:  
    9:         _selectedIngredient = value;
   10:         OnPropertyChanged("SelectedIngredient");
   11:     }
   12: }

You can see that it also supports the INotifyPropertyChanged event. This means that the View will be notified if this value is changed in the ViewModel. And this is what the XAML would look like:

 <data:DataGrid ItemsSource="{Binding Meal.Ingredients}" 
               SelectedItem="{Binding SelectedIngredient, Mode=TwoWay}" >

 

When I first started using the ViewModel pattern, I overlooked this capability. For example, I was looking for a way to attach functionality to the SelectedItemChanged event of the DataGrid. But two way databinding works a lot more directly. Just don’t forget to add the “Mode=TwoWay” tag ;)

Bind ViewModel methods to events from controls

The Caliburn framework allows you to bind public methods on the ViewModel directly to your controls in XAML. While this is a really cool technology, I also believe it makes your XAML more complex, because effectively, you are now programming (calling methods) in XAML. So I would personally try to either use commands, use two way databinding or implement an attached behavior (more on this subject later). 

Code behind

It is always possible to use Code Behind to communicate between the View and the ViewModel. Whether this is a good idea is a topic of hot debate. I personally am of the opinion that there are better ways to solve this problem than to use code behind. In a later blog post, I’ll go into details why i think this is a bad idea and how you can avoid this.  But there are people who think differently.

ICommand interface

A common way to expose functionality from your ViewModel that your Views can interact with is by exposing Command objects. A command object implements the ICommand interface.

The ICommand interface has two methods:

  • Execute, which will fire the logic encapsulated by the command
  • CanExecute, which determines if the command can actually execute.

The CanExecute is interesting. It’s a common scenario that you might wish to disable certain functionality, based on the state of your UI. This can easily be accomplished with a command. Because, when you bind a button to a command and CanExecute returns false, then the button will automatically become disabled.

Commands also have another advantage: They decouple the invocation from the actual logic. This allows for interesting scenario’s:

  • Binding multiple ui elements to a command

    Consider for example a SaveCommand. Often you don’t just have a Save Button, but also a Save menu item, and perhaps a save keyboard shortcut that will fire the same functionality. All these user gestures can be bound to the same command.

  • Chaining commands together
    You might also wish to bind several commands to a single UI element. For example, a certain type of ViewModel can expose a Save Command. However, you can have several instances of that ViewModel (with the corresponding view) open at any given time. A Save All button can be bound to all SaveCommands on all those ViewModels.

Silverlight doesn’t have Commanding support built in. Fortunately, it does support the ICommand interface, so there are several libraries available that provide Silverlight implementations of the ICommand interface. For example Prism (aka the Composite Application Guidance for WPF and Silverlight) and the MVVM Toolkit both provide a DelegateCommand implementation.

In my demo application, I’m also demonstrating the use of the DelegateCommand.

The INotifyPropertyChanged interface

One thing that you’ll find yourself implementing on your ViewModels the time is the INotifyPropertyChanged interface. This interface allows your ViewModel to notify the View of changes to properties.

This code snippet shows how to implement this pattern:

    1: // Public event (for the interface)
    2: public event PropertyChangedEventHandler PropertyChanged;
    3:  
    4: // Protected method for invoking the event from code
    5: protected virtual void OnPropertyChanged(string property)
    6: {
    7:     var handler = PropertyChanged;
    8:     if (handler != null)
    9:     {
   10:         handler(this, new PropertyChangedEventArgs(property));
   11:     }
   12: }
   13:  
   14: // Example property that triggers the property. 
   15: public Ingredient SelectedIngredient
   16:  {
   17:      get { return _selectedIngredient; }
   18:      set
   19:      {
   20:          // IMPORTANT: only trigger the event if someting actually changes!
   21:          if (_selectedIngredient == value)
   22:              return;
   23:  
   24:          _selectedIngredient = value;
   25:          OnPropertyChanged("SelectedIngredient");
   26:      }
   27:  }

One important thing: before invoking the event in the property, check if the value has actually changed. This helps preventing never ending loops when properties are chained together.

If you don’t like the repeating code you’ll get when implementing properties with INotifyPropertyChagned, you can also use ObservableObjects as properties, as I’m describing (among other things) in this blog post:

https://blogs.msdn.com/erwinvandervalk/archive/2009/04/29/how-to-build-an-outlook-style-application-with-prism-v2-part-2.aspx

 

What not to do in a ViewModel

There are a couple of things you shouldn’t do in your ViewModels:

  • Expose or interact with visual elements in your ViewModel

    Don’t try to expose Visual Elements, such as Buttons or other views from your ViewModel. This ties your UI to Visual Elements and makes it hard to test in isolation. Now you might run into the situation where your ViewModel needs to determine which view to display. In a later blogpost, i’ll describe how your ViewModel can be responsible for the logical ViewModel selection and use a DataTemplate to create the Visualization.

  • Try to control the View too directly from your ViewModel
    You should try to keep your ViewModel logical, rather than Visual. For example, if you want to color a control red in error situations, you could expose an “IsControlRed” property, or expose a brush in the red color. It’s usually better to expose the logical property, such as “HasError”. Then the UI designer can decide if the color of the control should be red or purple in an error situation.

Summary

This blog post describes the basics about implementing the MVVM pattern. In later blog posts, we’ll dive into more interesting scenario’s.

Comments

  • Anonymous
    August 19, 2009
    where can I find  a master-detail style app?3q

  • Anonymous
    August 27, 2009
    Should a ViewModel have a reference to the View (via interface)?  Let's say I have a AddFileCommand that's linked to a button, and when it is clicked, I want to prompt the user to select a file by showing the OpenFileDialog.  When I use MVP, I'll have the button click handler call a method on the presenter, and the presenter will trigger the view (via interface) to show the OpenFileDialog.  When the user selects a file, I then set a property on the presenter to indicate the full path of the file.  How will this be done in MVVM? Great posts BTW.

  • Anonymous
    August 27, 2009
    One of my previous examples, the outlook style applciation, shows a simple example of master detail. let me know if that's sufficient. Else I can whip up another example.

  • Anonymous
    August 27, 2009
    Hi Harold, No, your ViewModel should not have a reference to the View. This is a big difference as compared to MVP. Your ViewModel is a logical representation of all the functionality in your view, without ever touching UI elements. Now, with regards to your question on the OpenFileDialog. Think of it in a different way. You are calling "Something external" in a synchronous way for a file. Your ViewModel doesn't care if that "something external" is a user, or a database, or a webservice that takes a very long time. The file open dialog is a modal dailog. You can think of this as a synchronous action (because the code opening the modal dialog waits for the completion). Opening a separate non modal window can be thought of as an asynchronous operation. Now, if you are just asking "Something external" for a file, you should implement this "something external" as a service. In that respect, it's much more part of the Model (no different from a database). In my example, I'm showing how to do this with a IDialogService. I have a Confirm method on it, but you can imagine creating an "GetFile" method in the same way. Hope this helps, Erwin

  • Anonymous
    August 30, 2009
    The comment has been removed

  • Anonymous
    August 31, 2009
    Hi Eisenberg, Don't get me wrong. I think the Caliburn framework is a really cool piece of technology, though I haven't really used it myself. When I mentioned programming in the view, I meant more this type of code, which I read in the documentation:              <Button Content="Attached Container Command w/ 2 Parameters"                       cc:Command.Parent="{StaticResource Any}"                       cm:Message.Attach="ContainerCommand ShowTitledMessage(anyMessage.Text, anyTitle.Text)" /> I'm personally not a big fan of routing several pieces of information that's in the view to a single method. I would bind all this information to field on my viewmodel and then call a simpler command (or method in your case) on the viewmodel. I feel that approach makes your code more testable. However, I didn't know you also had that convention support in Caliburn! Cool stuff! _Erwin