The MVC pattern is quite widespread, and lots has been written about it. Clearly, it is a popular addition to ASP.NET. MVC works especially well for request/response style web apps as well as in navigation-based client apps.
The ViewModel pattern, or more accurately said, the Model-View-ViewModel (M-V-VM) pattern on the other hand hasn't been discussed a whole lot as a formal pattern. However, it is a really nice pattern that is very well suited to client apps that generally feature interactive or dynamic user interfaces that are data-bound and implemented declaratively, for example, most Silverlight apps.
Introduction to ViewModel
This is a very brief description of the pattern, and how it is both similar and dissimilar from the code-behind pattern that is in vogue.
- Model/DataModel
- This is somewhat analogous to the model in MVC. It basically represents the data that the application operates over, as well as the data access mechanism. This might be reused for different parts of the application. This might be partially defined declaratively, and/or code-gen'd.
- View
- This represents the user interface, displays data, and enables user interaction. This is typically defined declaratively - XAML, HTML etc. No surprises here.
- ViewModel
- This can also be read as "The View's Model" which is a bit more representative of its function. This sits in between the View and the Model and surfaces data in a form that is more suited for viewing or editing. It also defines operations that can be invoked to perform some work, often times resulting in changes to the data that the View is bound to. While it doesn't depend on the View, it is logically associated with one in a one-to-one manner. This might be partially code-gen'd as well, but for the most part this is authored in code.
You've probably been doing something quite close all along. I am sure you've written some piece of UI that is associated with some code-behind that is responsible for loading in data, and implementing logic to handle user input and interaction in the form of event handlers. The figure on the right illustrates the mechanics of the code-behind pattern. One of the big problems with code-behind is that it mixes your logic with presentation and couples it deeply, which gets in the way of testability. Another problem is that it leads to co-mingling of designer-centric parts of the app with developer-focused parts of the app. Often times these implications may be ok for quick-and-dirty prototyping but would be nice to avoid for real apps.
The ViewModel pattern attempts to address these problems by encouraging separation of logic from the presentation. The data being presented is implemented as properties on the ViewModel, that the View consumes via data-binding. Secondly, most of the backing logic and operations are implemented as methods on the ViewModel that the view invokes via commanding. The key constraint is that the ViewModel is not dependent on View concepts such as choice of controls. The figure on the left illustrates this pattern. It is not a fundamentally different world, but rather something closer to a rearrangement and refactoring of the same pieces.
Some folks might argue that all of code-behind is to be removed. I tend to think that some may remain, specifically, to facilitate view-to-view interactions that cannot be expressed declaratively in XAML easily, hence the sliver of code-behind in the view.
This is simply one amongst many patterns that encourage discipline over what code goes where, how it is related to other code etc. The goal here is to think about how to get both long-term benefits from practicing discipline, as well as make it suitable for those smaller apps and quick-and-dirty experimental apps, by making this pattern emerge naturally from tooling guidance. I won't cover tooling today; perhaps another blog post on this down the road.
ViewModel In Action
Enough theory! Onto a running app... I've built a simple Amazon Search application using the ViewModel pattern, and the Silverlight.FX framework I've built to support it on top of Silverlight 2. You can play with it here, as well as download the entire solution containing code for the sample, associated sampling of unit tests, and the framework it builds upon.
Implementing ViewModel via Action Behaviors
Both John Gossman and Dan Crevier have blogged about this pattern in the context of WPF, where their approach builds on data-binding and commanding. Silverlight supports a subset of data-binding features, but doesn't currently provide a commanding mechanism. Instead of commanding, I'll build the glue between the view and the operations defined in the view model via Actions (as you'll see in a moment) that are then hooked up via my behaviors framework.
Lets take a brief look at the implementation. I won't cover all the details, as you have the code and can step through as deeply as you'd prefer. On the right is my application's project structure.
I have my data model defined in the Data namespace. The Product class is an entity that has properties like Title, ImageUrl, ASIN etc. The Catalog class provides data access logic built by consuming the Amazon ECS REST APIs and the WebClient object that supports cross-domain networking. This code is pretty straightforward and commonplace. The catalog class implements the ICatalog interface. This has been defined so that the data access functionality can be mocked in a straightforward way for unit testing (more on that at the end).
The ViewModel is where it starts to get interesting. Being a developer, I'll start with that. It is defined in SearchView.model.cs as follows:
public class SearchViewModel : Model {
private IEnumerable<Product> _products;
private string _keyword;
private bool _searching;
public bool CanSearch {
get {
return !IsSearching;
}
}
public bool IsSearching {
get { return _searching; }
private set {
_searching = value;
RaisePropertyChanged("IsSearching", "CanSearch");
}
}
public IEnumerable<Product> Products {
get { return _products; }
private set { _products = value; RaisePropertyChanged("Products"); }
}
public void Search(string keyword) {
_keyword = keyword;
Searching = true;
Catalog catalog = new Catalog();
catalog.SelectProducts(keyword, OnCatalogProductsSelected);
}
private void OnCatalogProductsSelected(string keyword, Ienumerable<Products> products) {
if (keyword != _keyword) { return; }
Products = catalog.Products;
Searching = false;
}
}
The first thing to notice is that this class is completely independent from the UI. As such it can be tested quite easily. It simply represents a list of products, and the ability to perform a search that populates that list of products. It does this by consuming the data model.
This class derives from a class called Model from my framework, which supplies a default implementation of INotifyPropertyChange. The Model class also does some other nice things like cache PropertyChangedEventArg instances, and make sure change events are raised on the appropriate thread.
The next thing to implement is the view. Here you might imagine its is something that is worked upon predominantly and independently by a designer in Blend. The designer is free to choose the controls, lay them out, add animations and other subtle visual polish. As the UI comes together it might then be declaratively bound to the properties and methods that the developer has made available in the view model.
<vm:View xmlns="..." xmlns:x="..."
xmlns:vm="clr-namespace:Silverlight.FX.ViewModel;assembly=Silverlight.FX"
xmlns:app="clr-namespace:AmazonSearch.Views">
<vm:View.Model>
<app:SearchViewModel />
</vm:View.Model>
<Grid>
<TextBox x:Name="searchTextBox" />
<Button Content="Search" IsEnabled="{Binding CanSearch}">
<vm:ButtonEvents.Click>
<vm:InvokeMethod MethodName="Search">
<vm:ElementParameter ElementName="searchTextBox" ElementProperty="Text" />
</vm:InvokeMethod>
</vm:ButtonEvents.Click>
</Button>
<ItemsControl ItemsSource="{Binding Products}">
<ItemsControl.ItemTemplate>
<DataTemplate><app:ProductView /></DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Grid>
</vm:View>
The view is implemented by deriving from View, which is in turn derived from UserControl. This is just for convenience. It adds the concrete notion of an associated Model that is assigned to the DataContext property so it is inherited by the contained controls.
The ItemsControl is bound to the list of products via vanilla data-binding already present in Silverlight.
The equivalent of commands are implemented using Actions. Actions are simply behaviors that can be attached to an element, via an attached property that provides event trigger semantics. In the sample, the ViewModel exposes a CanSearch property and a Search method that provide commanding-like semantics in a simple manner. The search button's IsEnabled property is bound to CanSearch, and its Click event is bound to the Search method.
I am not completely thrilled with the syntax, but this is perhaps the best that is doable, until the ability to define markup extensions becomes available in some future Silverlight version, and I might eventually be able to declare something like this instead:
<Button Content="Search" Click="{Action InvokeMethod Search(searchTextBox.Text)}"
IsEnabled="{Binding CanSearch}" />
Actions are actually quite expressive since they are extensible and can encapsulate stock behaviors (the equivalent of stock commands). In the framework I have actions that work against storyboards (eg. PlayStoryboard), set properties etc. Here is another example of an action to perform the navigation whenever a search result is clicked.
<Image Source="{Binding ImageUrl}">
<vm:MouseEvents.MouseLeftButtonDown>
<vm:Navigate NavigateUrl="{Binding ItemUrl}" Target="Amazon" />
</vm:MouseEvents.MouseLeftButtonDown>
</Image>
Unit Testing the View Model
One of the benefits of separation of concerns is easier testing of a greater portion of the application's code and functionality. Specifically, as the ViewModel is itself independent of the user interface, I should be able to easily unit test it.
Here I am going to leverage the Silverlight Unit Testing Framework to author tests for my ViewModel and then run them. This is also the place where I am going to leverage the fact that I defined an ICatalog interface earlier to represent my data access layer. For the unit tests, I am going to create a mock catalog implementation, that doesn't rely on making calls to the Amazon service.
Here is one of my mock catalog implementations that returns a dummy product.
public class TestCatalog : ICatalog {
public void SelectProducts(string keyword,
Action<string, IEnumerable<Product>> productsCallback) {
DispatcherTimer timer = new DispatcherTimer();
timer.Interval = TimeSpan.FromSeconds(2);
timer.Tick += delegate(object sender, EventArgs e) {
Product testProduct =
new Product {
ASIN = "1",
By = "TestAuthor",
ImageUrl = "http://www.example.com",
ItemUrl = "http://www.example.com",
Title = "Test Title"
};
productsCallback(keyword, new Product[] { testProduct });
};
}
}
And here is one of the tests defined in my test class. Note that I am using the ability to write async tests.
[TestClass]
public class SearchViewModelTest : SilverlightTest {
[TestMethod]
[Asynchronous]
[Description("Test a typical search result.")]
public void TestSearchResult() {
ICatalog mockCatalog = new TestCatalog();
bool productsChanged = false;
SearchViewModel viewModel = new SearchViewModel(mockCatalog);
((INotifyPropertyChanged)viewModel).PropertyChanged +=
delegate(object sender, PropertyChangedEventArgs e) {
if (e.PropertyName == "Products") {
productsChanged = true;
}
};
viewModel.Search("xyz");
EnqueueConditional(() => productsChanged);
EnqueueCallback(() => Assert.AreNotEqual(viewModel.Products, null,
"Expected non-null products list."));
EnqueueCallback(() => Assert.AreNotEqual(viewModel.Products.Count(), 0,
"Expected non-empty products list."));
EnqueueTestComplete();
}
}
Using the Unit Testing engine to execute my tests in the browser, i.e. running in the context of the Silverlight CLR, I see the following result, indicating goodness...
Summary
Hopefully this sample gave a good flavor of what the ViewModel pattern is all about, and how action behaviors can help create the bridge between the View and the ViewModel in Silverlight today. There are various other interesting discussion points and further exploration that relate to this, so stay tuned, but in the mean time I am curious what people think, and what questions arise as well, to help guide both the discussion, and the feature as its worked in into the framework.