This article is compatible with the latest version of Silverlight.
Introduction
In this short series of articles I’ll cover what MVVM is and how to use it in practice, how to solve issues when applying this pattern and how to take advantage from it.
What is MVVM and why you need it
Do you know the feeling, when you start to develop an application, and in the first couple of days you feel good because you made a really good progress? Do you know the feeling, when after a couple of days, you feel that your design is not that best and when you have to modify a little code, you are afraid of the effect it might have on other parts of you code? The first sign of a bad design is when applying a hack is easier than to implement it the proper way. At the end of the week you already have spaghetti code. You have logic that is not testable, it is very tightly coupled with the UI. You cannot make just “minor” changes without taking risks. If someone wants to understand your code, it takes days or weeks to figure out how it works.
This is where MVVM comes into play. MVVM stands for the Model – View – ViewModel pattern. It’s originated from MVC and is specific to WPF and Silverlight. The three most important things that MVVM can offer you:
- Testable code
- UI is loosely coupled with related logic
- Maintainable code
Basic concepts in MVVM
The Layers:
1. The View
The View is your User Interface. It’s basically xaml. The view does not interact directly with the model. It does not contain any kind of non-UI related logic. Be aware, don’t be fooled by demo codes and presentations. Sometimes you also have C# code in your code-behind, not just xaml. Sometimes you just cannot get around it. Don’t feel bad about it. There is nothing wrong writing UI related code in the view’s code-behind, after all it’s probably view specific. However sometimes you can write or find workarounds for these cases, in form of Behaviors and Triggers.
2. The Model
The Model can contain entities, DTOs, data source and proxy objects, repositories. It largely depends on what kind of application you are writing. For example if you have a WCF service on the server (above the service layer you may have many other layers, like Business Logic Layer and Data Access Layer, etc.) you will need client side objects to interact with the services. You will need DataContracts (you can transfer data with these objects) and proxy objects to invoke operations. These can be part of your model. I like to refer to the model as the entry point to the rest of the application. The Model and the View DOES NOT know each other, they do not communicate directly.
3. The ViewModel
The ViewModel’s responsibility is to get the necessary data from the model and expose it for the view. Through the view, the user can enter data and trigger actions (commands), so the ViewModel has to interpret this input and the commands, and invoke the appropriate operations on the model and also pass the necessary data.
Interaction between the Layers:
The ViewModel communicates with the Model through an interface. (It’s highly recommended to use interfaces instead of actual implementation). The Model should not know the ViewModel. We try to keep the dependencies low and avoid tightly coupled scenarios.
The communication between the View and the ViewModel is where it gets more interesting. Thanks to the brilliant binding mechanism in Silverlight and WPF the view can get and pass data to and from the ViewModel without actually referencing it.
For example if you have a list of products in the ViewModel and the ViewModel itself is the DataContext of your View, a ListBox defined in your view can DataBind to those product entities. So the Binding mechanism provides this kind of flexibility in this specific pattern.
Let’s see it in action!
MVVM in practice
Well, if you decide to use the MVVM pattern, you’ll see that there are many things that needs to be implemented over and over again. So after a while you may want to develop you own MVVM assisting framework / library to help you along with forthcoming projects. Luckily enough, there are very good solutions out there. The two most famous ones are the MVVM Light Toolkit by Laurent Bugnion, and the Composite Application Library (aka Prism) by the Patterns and Practices Team of Microsoft. However both products are very popular and support MVVM, their main concepts are pretty different. While the MVVM Light Toolkit focuses solely on the MVVM architecture, the Composite Application Library’s main focus is on supporting composite scenarios. Prism supports building Modular applications and also happens to support the MVP architecture. (MVP is like MVVM, but in MVP, the Presenter (ViewModel in our terms) may interact with the View through a well-defined interface directly)) So in our example we will use the MVVM Light Toolkit. (also available, for WPF 3.5, 4.0, Silverlight 3.0, 4.0 and WP7) You can download it from here.
Our demo application will load Books in a ListBox. The selected product’s details will be displayed in a details panel. Also we’ll have remove buttons, so when we click on one, the selected product will be deleted.
So I’m just going to create a simple Silverlight 4.0 project. (Please note, that if you install the MVVM Light Toolkit, you’ll also get Visual Studio Project templates. I’m just going to use the dlls here only)
As you can see I’ve created separate folders for my ViewModels and my Views. I have a Master View this is the BooksView UserControl, and a details view, the BookDetailsView UserControl. Also I’ve created a separate Model Project, let’s see that first.
1. The Model Project
Book.cs – This is an entity class, that represents a Book object. It’s very simple does not contain any special code:
namespace MVVMProductsDemo.Model
{
public class Book
{
public int BookID { get; set; }
public string Title { get; set; }
public string Author { get; set; }
public decimal Price { get; set; }
}
}
IBookDataSource.cs – This is an interface that specifies how the ViewModel will interact with the Model.
namespace MVVMProductsDemo.Model
{
public interface IBookDataSource
{
void LoadBooks();
void RemoveBook(int bookID);
event EventHandler<BooksLoadedEventArgs> LoadBooksCompleted;
event EventHandler<OperationEventArgs> RemoveBookCompleted;
}
}
As you can see, the interface implies that access to the DataSource will be asynchronous. Well, in Silverlight, mostly this is the case. I have two operations. One to load books, and another one to remove a book. I get notified of the results of the operations by events. Below there is an example of the BooksLoadedEventArgs
namespace MVVMProductsDemo.Model
{
public class BooksLoadedEventArgs : OperationEventArgs
{
public IEnumerable<Book> Books { get; set; }
public BooksLoadedEventArgs(IEnumerable<Book> books)
{
this.Books = books;
}
}
}
Now all I have to do is to actually implement the interface and provide a DataSource implementation. Well for this article a mock (fake) data source is perfect. Not to mention that I plan to use that for testing as well!
namespace MVVMProductsDemo.Model
{
public class MockBookDataSource : IBookDataSource
{
List<Book> bookList = new List<Book>();
public MockBookDataSource()
{
bookList.Add(
new Book
{
BookID = 1,
Author = "Tom Clancy",
Title = "Rainbow Six",
Price = 29.99m
});
bookList.Add(
new Book
{
BookID = 2,
Author = "Tom Clancy",
Title = "Executive Orders",
Price = 19.99m
});
bookList.Add(
new Book
{
BookID = 3,
Author = "John Grisham",
Title = "The Partner",
Price = 22.99m
});
}
#region IBookDataSource Members
public void LoadBooks()
{
if (LoadBooksCompleted != null)
{
LoadBooksCompleted(this, new BooksLoadedEventArgs(bookList));
}
}
public void RemoveBook(int bookID)
{
Book bookToDelete = bookList.Single(p => p.BookID == bookID);
bookList.Remove(bookToDelete);
if (RemoveBookCompleted != null)
RemoveBookCompleted(this, new OperationEventArgs());
}
public event EventHandler<BooksLoadedEventArgs> LoadBooksCompleted;
public event EventHandler<OperationEventArgs> RemoveBookCompleted;
#endregion
}
}
As you can see I load some fake books and provide an implementation for LoadBooks and RemoveBook.
2. The ViewModels
ViewModelBase and Property Change Notifications
Let’s get back to the ViewModels. So I have to provide a special model class for my view to display products, load products and handle item selection. This is the BooksViewModel class. The BooksViewModel inherits from ViewModelBase defined in the MVVM Light Toolkit. For data binding to work properly, we need property change notification. ViewModelBase implements the INotifyPropertyChanged interface and provides a protected RaisePropertyChanged method, that we can use to propagate property change notification towards the UI.
Books Collection
The BooksViewModel class exposes a Books property of type ObservableCollection<BookDetailsViewModel>. This property serves as the ItemSource of the ListBox. But why not ObservableCollection<Book>? The answer is simple, I have three reasons:
- I want to bind to details panel’s (BookDetailsView) DataContext to the SelectedItem of the ListBox. But if I have Books in the ListBox instead of BookDetailsViewModel instances I cannot do this simply since the DataContext should be a BookDetailsViewModel, not a Book instance.
- In the ListBox I might want to define a DataTemplate. In the template I might want to add a button that corresponds to an action defined in the BookDetailsViewModel.
- I really want to keep the dependency of the book object very low in my ViewModels. Who knows, maybe I will have to replace it.
private ObservableCollection<BookDetailsViewModel> books;
public ObservableCollection<BookDetailsViewModel> Books
get { return books; }
set
{
books = value;
RaisePropertyChanged("Books");
}
}
Selected Book
Also I have a SelectedBook property of type BookDetailsViewModel. I’m planning to bind the ListBox’s SelectedItem to this property and also the BookDetailsView’s DataContext should be read from this property. Why not use direct UI to UI binding? I have many reasons:
- Most of the time different parts of my application is likely to react if a selected item changes. Not just the UI, other components as well.
- I' want to modify the selected book from code as well. The UI needs to reflect those changes!
- The designer might screw this one up :)
- etc…
So basically I need control over the SelectedItem, in this case the SelectedBook. This is why I’m going to do a two-way binding for the ListBox’s SelectedItem and a one-way binding for the BookDetailsView’s DataContext.
private BookDetailsViewModel selectedBook;
public BookDetailsViewModel SelectedBook
{
get { return selectedBook; }
set
{
selectedBook = value;
RaisePropertyChanged("SelectedBook");
}
}
Loading Books
I can access the DataSource through IBookDataSource, an instance that I get through the BooksViewModel constructor. The DataSource is event-based so I just have to subscribe the the loaded event.
IBookDataSource bookDataSource;
public BooksViewModel(IBookDataSource bookDataSource)
{
this.bookDataSource = bookDataSource;
if (bookDataSource != null)
{
bookDataSource.LoadBooksCompleted += new EventHandler<BooksLoadedEventArgs>(bookDataSource_LoadBooksCompleted);
}
}
I also create a LoadBooks() method that loads the books from the DataSource;
public void LoadBooks()
{
bookDataSource.LoadBooks();
}
void bookDataSource_LoadBooksCompleted(object sender, BooksLoadedEventArgs e)
{
if (e.Error != null)
{
//Do dg.
return;
}
List<BookDetailsViewModel> loadedBooks = new List<BookDetailsViewModel>();
foreach (var book in e.Books)
{
loadedBooks.Add(new BookDetailsViewModel(book));
}
Books = new ObservableCollection<BookDetailsViewModel>(loadedBooks);
}
As you can see after we check a possible error, we refresh the Books Collection. Transformation in necessary into ViewModels since the EventArgs returns IEnumerable<Book> list.
One final step for this View Model. When the user clicks the load button, the ViewModel should get the books. Now we are not binding a simple data property to a simple dependency property like a TextBox.TextProperty. Now we are binding an event to an operation. Of course this is just not possible, we need an extra object, what is called a command. Different MVVM libraries use different names for these commands, like DelegateCommand, ActionCommands or RelayCommands. All of them implement the ICommand interface and this is what matters here. We have RelayCommands in the toolkit. You can bind commands as properties. So all I have to do is to expose a LoadBooksCommand
private RelayCommand loadBooksCommand;
public RelayCommand LoadBooksCommand
{
get
{
return loadBooksCommand;
}
}
//This line goes to the contructor
loadBooksCommand = new RelayCommand(LoadBooks);
What did we here? We specified that if a LoadBooksCommand gets invoked than we should call the LoadBooks() method! The buttons have a Command property which is a dependency property. We can directly bind that property to this LoadBooksCommand. Also note, that the second parameter the the RelayCommand’s contstructor is another delegate. Here you can enter a function that checks whether the command in question can actually be executed. If not, then the associated control’s IsEnabled property is set to false. (And this is why I don’t like it. Once more we tie the hands of the designer)
BookDetailsViewModel
The last ViewModel in our case is just a representation of the Book class for now. But that’ll change later!
namespace MVVMProductsDemo.ViewModels
{
public class BookDetailsViewModel : ViewModelBase
{
Book book;
public BookDetailsViewModel(Book book)
{
this.book = book;
}
public string Title
{
get
{
return book.Title;
}
set
{
book.Title = value;
RaisePropertyChanged("Title");
}
}
public string Author
{
get
{
return book.Author;
}
set
{
book.Author = value;
RaisePropertyChanged("Author");
}
}
public decimal Price
{
get
{
return book.Price;
}
set
{
book.Price = value;
RaisePropertyChanged("Price");
}
}
}
}
There we go. We’re done. Remember that I wrote always things like, the user does this or that, that we have a ListBox, and details etc… I never said anything about a concrete UI or layout. Let’s create it now!
3. The Views
Let’s start with the details view. How do we display one item.
So this is what we want to create and bind. Here is the code:
<UserControl x:Class="MVVMProductsDemo.Views.BookDetailsView"
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">
<Grid.RowDefinitions>
<RowDefinition Height="0.117*"/>
<RowDefinition Height="0.107*"/>
<RowDefinition Height="0.117*"/>
<RowDefinition Height="0.66*"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="0.345*"/>
<ColumnDefinition Width="0.655*"/>
</Grid.ColumnDefinitions>
<TextBlock Margin="10,0,0,0" TextWrapping="Wrap" d:LayoutOverrides="Width, Height" HorizontalAlignment="Left" VerticalAlignment="Center" Text="Title:"/>
<TextBlock Margin="10,0,0,0" TextWrapping="Wrap" Grid.Row="1" d:LayoutOverrides="Width, Height" HorizontalAlignment="Left" VerticalAlignment="Center" Text="Author:"/>
<TextBlock Margin="10,0,0,0" TextWrapping="Wrap" Grid.Row="2" d:LayoutOverrides="Width, Height" HorizontalAlignment="Left" VerticalAlignment="Center" Text="Price:"/>
<TextBox Grid.Column="1" TextWrapping="Wrap" d:LayoutOverrides="Height" VerticalAlignment="Center" Margin="15,0" Text="{Binding Title, Mode=TwoWay}"/>
<TextBox Grid.Column="1" TextWrapping="Wrap" Grid.Row="1" d:LayoutOverrides="Height" VerticalAlignment="Center" Margin="15,0" Text="{Binding Author, Mode=TwoWay}"/>
<TextBox Grid.Column="1" TextWrapping="Wrap" Grid.Row="2" d:LayoutOverrides="Height" VerticalAlignment="Center" Margin="15,0" Text="{Binding Price, Mode=TwoWay}"/>
</Grid>
</UserControl>
As you can see we did simple two-way bindings like {Binding Title, Mode=TwoWay}.
The BooksView UserControl is where it gets interesting:
And here is the xaml:
<ListBox x:Name="lboxBooks" HorizontalAlignment="Left" Margin="8,8,0,29" Width="129" ItemsSource="{Binding Books}"
SelectedItem="{Binding SelectedBook, Mode=TwoWay}" DisplayMemberPath="Title"/>
<Button Command="{Binding LoadBooksCommand}" x:Name="btnLoad" Content="Load Books" HorizontalAlignment="Left"
Margin="8,0,0,3" VerticalAlignment="Bottom" Width="129"/>
<local:BookDetailsView x:Name="bookDetails" Margin="154,8,8,29" DataContext="{Binding SelectedBook}"/>
So we have a ListBox with and ItemsSource bound to the Books property, and the SelectedItem bound two-way to the SelectedBook property. Right now we are not using any DataTemplates yet, this is why the DisplayMemberPath is set to Title (not Book.Title, its BookDetailsViewMode.Title!!!)
The Button has the Command property bound to the LoadBooksCommand so when the user clicks it, it gets invoked. And finally a complete view (a user control) is placed here, this is the BookDetailsView. It’s DataContext is bound to the SelectedBook. So whenever that property changes, the Details will reflect the changes.
4. Wiring Up
Okay so we have the Views and ViewModels but we lack the wire up at the beginning? I told you I don’t want to reference the ViewModel from View. (If you do, that’s not a big problem, many MVVM implementation does that.) But I prefer to do this one more level above, in the application class:
private void Application_Startup(object sender, StartupEventArgs e)
{
BooksView view = new BooksView();
IBookDataSource dataSource = new MockBookDataSource();
BooksViewModel viewModel = new BooksViewModel(dataSource);
view.DataContext = viewModel;
this.RootVisual = view;
}
As you can see, this is the place where I specify the actual implementation details for the IBookDatasource. This is the place where I could change it to a live data source. And here is the wire up. I specify the DataContext of the BookView as a BooksViewModel instance. That’s it.
If we run the application now, it just works!
Summary
Okay, what do we have so far? We have a Maintainable, Testable and pretty loosely coupled solution. Also we can easily create different views, for the same data and functionality!
Where do we go from here? We need advanced functionalities, like Removing or Updating an item, we need to battle the problems we face, when implementing these additions, like notifying other views / ViewModels if something changes. We need to create unit tests to make sure, changes in the code does not ruin the application. We need to change to a live data source from the mock one, and of course, we need validation support both on the server and the client!
Well, if you’re interested, read Part 2!
Download the source code