Click here to Skip to main content
15,867,488 members
Articles / Desktop Programming / WPF
Article

Model-View-ViewModel in WPF

Rate me:
Please Sign up or sign in to vote.
3.74/5 (15 votes)
21 Dec 2007CPOL5 min read 105.7K   1.8K   42   11
An article on creating WPF applications, following the Model-View-ViewModel pattern
Screenshot -

Introduction

The Model-View-ViewModel pattern, a variant of Model-View-Controller, provides a nice way to develop graphical applications that are testable. Several features in Windows Presentation Foundation, such as data binding and the command architecture, go a long way towards making it possible to follow this pattern. Unfortunately, once you start to follow the pattern in a real application, you start to run into various things that make it very difficult to maintain the strict separation of Model, View and ViewModel. This article implements the basis of a full-featured application, illustrating various techniques that may be used to maintain the strict separation of concerns demanded by the M-V-VM pattern.

Background

I tried to implement this application while following the Behavior-driven Development methodology. I've captured the attempt by including a complete version history in the form of a Bazaar repository. Bazaar is a distributed version control system and, as such, the entire repository was included in the downloadable source archive. You can see every step of development, including all of the various mistakes I made while coding.

Despite the attempt to follow BDD, don't expect production-quality code here. First, I'm new to this methodology and you can be certain that I didn't follow it as well as one should. Second, although the project includes a fairly complete example application, it is only an example. I've focused on the necessary things to illustrate how to implement an application in WPF following M-V-VM, not on what would be necessary to make a production-worthy application. This is a starting place only.

Also, note that the unit-testing code uses a lot of custom code. This was a large enough portion of the effort that I split it out into a separate article: Visual Studio Unit Testing Extensions.

Using the Code

The "core" to a lot of the code in this project can be found in the ViewModel class. This class is both a MarkupExtension class, as well as a class providing a few attached properties. In order to associate your ViewModel with your View, you use the ViewModel class like this:

XML
<Window
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:c="clr-namespace:CodeProject.Windows.Markup;assembly=CodeProjectCore"
    xmlns:vm="clr-namespace:TaskList.ViewModel;assembly=TaskList.ViewModel"
    c:ViewModel.Instance="{c:ViewModel {x:Type vm:MainWindowViewModel}}">

Assigning the ViewModel.Instance attached property does several things:

  • Assigns the property to an instance of an object. Note that this property is inherited by children.
  • Assigns the DataContext property to the same instance.
  • Assigns the ViewModel.Commands property to the empty string.

That last bullet point is worth explaining further. The ViewModel.Commands attached property is used to create command bindings on the View for command handlers in the ViewModel. This is actually one of the trickier things to accomplish in a simple and "clean" manner. I've blogged about this on my blog (check out the archives for several posts on this topic) and others, such as Brownie, have as well.

When ViewModel.Commands is attached to an element, it searches ViewModel.Instance for a property of type CommandBindingCollection with the CommandSinkAttribute and a matching KeyName. All CommandBinding instances in this collection are added to the element's Commands. This solution was inspired by the article Smart Routed Commands in WPF, although obviously the purpose and implementation are different here.

The ViewModel does not need to inherit from any base class or implement any interfaces. The ViewModel markup extension will try to instantiate an instance of the ViewModel first by looking for a constructor that takes a single parameter compatible with the element. You should avoid creating a tight coupling to the View here by using an interface that the element can implement. If the ViewModel doesn't have a constructor that fits the criteria here, it will instead be instantiated using the default constructor, if present. It's generally best to not create any coupling here at all, but there are some things you simply can't do in the ViewModel, such as navigate to another page. A View-specific interface allows you to put such code in the View while retaining as little coupling as possible.

A similar interface can be used for the Application. This gives the ViewModel access to application-wide settings and functionality, while not being closely tied to the actual Application. The sample application demonstrates using both the View interfaces, as well as an application interface, including how to use Mock Objects that implement these interfaces to facilitate testing.

The goal with the ViewModel is to put as much UI state as possible into it. The View then binds this state to the necessary elements. The classic example here is the selection state for a collection that will be presented in the View. The problem is that some states in the View are given in read-only properties that cannot be used in data-binding. The SelectedItems property on several controls is an example of this. In order to maintain such states in the ViewModel, a unique solution must be found to bind the state to the read-only properties on elements in the View. The Selection class provides an attached SelectedItems property to illustrate one way in which this can be accomplished. The attached property is responsible for watching the state both on the element as well as in the ViewModel, and keeping the two in sync.

Bonus: DataErrorInfo

This class isn't really related to M-V-VM, the main focus of this article. So, consider it a bonus. This is the starting point for a validation framework based on IDataErrorInfo. It uses various ValidatorAttribute classes to specify how properties are supposed to be validated. Only StringLengthValidatorAttribute is included in the code, but it should be straightforward to create other validators. The Task Model class illustrates the use and the EditTaskPage shows how to do validation with it in WPF.

History

12/16/2007 - Initial article published.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Web Developer
United States United States
Windows developer with 10+ years experience working in the banking industry.

Comments and Discussions

 
GeneralA Word of Warning Pin
Mirko Klemm1-Sep-10 10:01
Mirko Klemm1-Sep-10 10:01 
NewsAlternative approach with MEF Pin
jbe82244-Jul-09 0:53
jbe82244-Jul-09 0:53 
GeneralGood article Pin
BillW332-Jun-09 7:54
professionalBillW332-Jun-09 7:54 
GeneralA little bit too complicated :/ Pin
john19977-Apr-09 3:17
john19977-Apr-09 3:17 
GeneralDataErrorInfo - ArgumentOutOfRangeException Pin
John Myczek15-Dec-08 9:00
John Myczek15-Dec-08 9:00 
GeneralMy vote of 2 Pin
Jared Morton28-Nov-08 2:32
Jared Morton28-Nov-08 2:32 
Generalmany interesting ideas Pin
Quang Tran Minh17-Jun-08 21:51
Quang Tran Minh17-Jun-08 21:51 
QuestionDataModel instance Pin
FantaMango776-Jan-08 20:33
FantaMango776-Jan-08 20:33 
GeneralRe: DataModel instance Pin
William E. Kempf8-Jan-08 10:49
William E. Kempf8-Jan-08 10:49 
GeneralRe: DataModel instance Pin
FantaMango779-Jan-08 2:07
FantaMango779-Jan-08 2:07 
GeneralRe: DataModel instance Pin
William E. Kempf9-Jan-08 2:46
William E. Kempf9-Jan-08 2:46 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.