Introduction
If you are going to work on any kind of WPF, Silverlight, or WP7 application, then you are almost certainly going to be using
MVVM. MVVM (Model-View-ViewModel) is a design pattern that encourages you to separate presentation from data.
Presentation is done in the View, data access and manipulation is done in the Model, and the two are bound together by a ViewModel.
This article assumes you understand the basic principals of MVVM - if you don't, then I suggest the superb article
Model-View-ViewModel (MVVM) Explained,
by Jeremy Likness.
If you are going to be using MVVM, there are some great libraries out there - however, if you like to do things from scratch, either as a learning exercise
or so that you can build your own custom code base, then you'll want to know how to build an MVVM framework. In this article, I will show you how to build a basic
MVVM framework from the ground up. I will also demonstrate how you can create a single common code-base and from this create libraries that support WPF, Silverlight, and WP7.
This library is called Apex. I have a stack of articles ready to be published that use Apex - so until I introduce it, I cannot publish the more interesting stuff!
Existing Frameworks
If you are looking for a fully-functional framework out of the box, then let me first recommend Cinch.
Cinch is written by Sacha Barber and is in my opinion the best MVVM framework out there.
The main page for v2 is here. Another good alternative is Prism.
Rolling Your Own
Ready to roll your own from scratch? Let's go. What do we need for a basic MVVM framework?
- A ViewModel Base Class: Something that implements
INotifyPropertyChanged
and allows us to quickly build ViewModels.
- A Command Base Class: Something that implements
ICommand
and allows us to quickly build commands that allow the View to invoke functionality on the View Model.
- Commonality: A framework that allows us to use consistent patterns in WPF, Silverlight, and WP7.
Let's get started - first, we'll take a look at a class that is used as a ViewModel and implements
INotifyPropertyChanged
.
The ViewModel
Here's a ViewModel that implements INotifyPropertyChanged
.
public class ViewModel1 : INotifyPropertyChanged
{
#region INotifyPropertyChanged Members
public event PropertyChangedEventHandler PropertyChanged;
#endregion
}
As we can see, the only thing of interest here is that we have provided the PropertyChanged
event - this is the event that we can fire to indicate that a specific property
has changed. When we fire this event, the WPF framework will know to update the visuals of anything that binds to the specified property.
Next, we should put together a function that calls the event - if it has been registered.
public virtual void NotifyPropertyChanged(string propertyName)
{
PropertyChangedEventHandler propertyChanged = PropertyChanged;
if (propertyChanged != null)
propertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
Now we have a function we can call to fire the property changed event. With this as a starting point, we can bulk the ViewModel out to actually do something - present a first and last name.
public string FirstName
{
get { return firstName; }
set
{
if (firstName != value)
{
firstName = value;
NotifyPropertyChanged("FirstName");
}
}
}
public string SecondName
{
get { return secondName; }
set
{
if (secondName != value)
{
secondName = value;
NotifyPropertyChanged("SecondName");
}
}
}
So there we have it - a ViewModel to represent a first and last name. What are the problems?
- We have to implement
INotifyPropertyChanged
for every ViewModel.
- Every property has to do the check to see if it has changed and call the
NotifyPropertyChanged
function.
How will we resolve this?
- Create a new class called '
ViewModel
' which will be the base for all future ViewModels. This is fairly standard for MVVM frameworks.
- Create a new class called '
NotifyingProperty
' which will represent a property of a ViewModel that calls NotifyPropertyChanged
.
This is a little bit different so we will go into some detail here.
If you are coding as you read, now is the time to start the actual project. I will describe this in terms of the Apex solution - if you are using this code as a basis
for your own project, here's where you can start to customise things.
Create a new solution named Apex. Add a WPF Class Library project called Apex. Add a folder in Apex called MVVM - this is where we'll put all the classes relating to MVVM.
Now we can take the class above and use it as a starting point for our base ViewModel class. The ViewModel class is shown below. Create a new file, ViewModel.cs.
using System.ComponentModel;
namespace Apex.MVVM
{
public class ViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public virtual void NotifyPropertyChanged(string propertyName)
{
PropertyChangedEventHandler propertyChanged = PropertyChanged;
if (propertyChanged != null)
propertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
Bingo. We have a ViewModel base class that we can use. Now what about the NotifyingProperties? As an inspiration for this, let's take a look at how a dependency property is defined.
public class SomeDependencyObject : DependencyObject
{
private static readonly DependencyProperty ageProperty =
DependencyProperty.Register("Age",
typeof(int), typeof(SomeDependencyObject));
public int Age
{
get { return (int)GetValue(ageProperty);}
set { SetValue(ageProperty, value);}
}
}
Although a bit wordy, what is nice about this is that it is familiar. In any kind of custom control work, we'll be using dependency properties,
so the more we use WPF and Silverlight, the more familiar we'll become with the above. Can we do something similar for properties of ViewModels?
Well, yes, we can - we can start off with a NotifyingProperty
class. What will it need? The name of the property, as a string, the property value itself (as an object),
and the type of the property. With this to go on, we can create the NotifyingProperty
class.
using System;
namespace Apex.MVVM
{
public class NotifyingProperty
{
public NotifyingProperty(string name, Type type, object value)
{
Name = name;
Type = type;
Value = value;
}
public string Name { get; set; }
public Type Type { get; set; }
public object Value { get; set; }
}
}
This seems like a good starting point - now how about the GetValue
and SetValue
functions of DependencyObject
? We can use exactly the same syntax
in the ViewModel class if we add two functions to our ViewModel base class. Add the following two functions to the ViewModel
class:
protected object GetValue(NotifyingProperty notifyingProperty)
{
return notifyingProperty.Value;
}
protected void SetValue(NotifyingProperty notifyingProperty,
object value, bool forceUpdate = false)
{
if (notifyingProperty.Value != value || forceUpdate)
{
notifyingProperty.Value = value;
NotifyPropertyChanged(notifyingProperty.Name);
}
}
GetValue
is simple - it just returns the value of the property. SetValue
is easy too - it checks to see if the value has changed and then if so, it sets it and fires
the NotifyPropertyChanged
function that we added earlier. We have also added a 'forceUpdate
' parameter that allows us to force an update even if the value hasn't changed.
We may need this in certain odd scenarios but in general, we won't, so we default the value to false
.
We've got it! The ViewModel is done and the NotifyingProperty
is done! Let's see it in action.
The MVVMSample
Create a samples solution folder in the Apex solution, and add a new WPF Application to it, called MVVMSample. We'll use this sample as a demo of the ViewModel.
Add a reference to the Apex library and create a new class in MVVMSample called MainViewModel
, just as below.
using Apex.MVVM;
namespace MVVMSample
{
public class MainViewModel : ViewModel
{
private NotifyingProperty firstNameProperty =
new NotifyingProperty("FirstName", typeof(string), string.Empty);
private NotifyingProperty secondNameProperty =
new NotifyingProperty("SecondName", typeof(string), string.Empty);
public string FirstName
{
get { return (string)GetValue(firstNameProperty); }
set { SetValue(firstNameProperty, value); }
}
public string SecondName
{
get { return (string)GetValue(secondNameProperty); }
set { SetValue(secondNameProperty, value); }
}
}
}
Things are looking good so far - we have a ViewModel that implements the base class and notifying properties that are syntactically similar to DependencyProperties.
Let's wire it up to the MainWindow. Here's the main window code, with the things that have been added in bold.
<Window x:Class="MVVMSample.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:MVVMSample"
Title="MainWindow" Height="200" Width="300">
-->
<Window.DataContext>
<local:MainViewModel x:Name="viewModel" />
</Window.DataContext>
<Grid>
<StackPanel>
<TextBlock Text="First Name" />
<TextBox Margin="4" Text="{Binding FirstName}" />
<TextBlock Text="Second Name" />
<TextBox Margin="4" Text="{Binding SecondName}" />
</StackPanel>
</Grid>
</Window>
The XAML is tight and tidy - things are definitely going in the right direction. The next step will be to handle commands.
The ViewModel Command
Implementing a Command function has been done in many articles on this site. The implementation I want should be able to do this:
- Allow a Command object to be added to a ViewModel so the View can bind to it.
- Allow a Command object to somehow be associated with a function of the ViewModel, so that we can allow a View to invoke functionality of a ViewModel.
- Allow a Command object to send events to some arbitrary object, so that we can let a View respond to commands.
This is an important one. Let's say we have a command that saves the current contact of an address book to the database. We want to make sure that when the command
is executed the View moves the focus to the 'first name' textbox, i.e., the focus logic (or any logic relating to the UI) happens in the View and the business
logic happens in the ViewModel.
Let's start off. Add a new class to the Apex MVVM folder called 'ViewModelCommand
', implementing ICommand
.
public class ViewModelCommand : ICommand
{
#region ICommand Members
public bool CanExecute(object parameter)
{
throw new NotImplementedException();
}
public event EventHandler CanExecuteChanged;
public void Execute(object parameter)
{
throw new NotImplementedException();
}
#endregion
}
Here we see the most basic things we need. Let's bulk this out. Remove the code in the ICommand
members region, we'll replace it now. Here's what we're adding, blow by blow.
First, we're going to need to keep track of whether the command can execute.
private bool canExecute = false;
Look at the initial implementation above - we need a CanExecuteChanged
event and the CanExecute
function - we can add these now.
public event EventHandler CanExecuteChanged;
bool ICommand.CanExecute(object parameter)
{
return canExecute;
}
Let's add a property that will allow us to set whether the command can execute and automatically fire the event.
public bool CanExecute
{
get { return canExecute; }
set
{
if (canExecute != value)
{
canExecute = value;
EventHandler canExecuteChanged = CanExecuteChanged;
if (canExecuteChanged != null)
canExecuteChanged(this, EventArgs.Empty);
}
}
}
When the command gets called, Execute
will be invoked. We'll add a function called 'DoExecute
' which will actually execute the command.
This means that the code can invoke the command manually. DoExecute
will first fire an 'Executing
' event, which will allow the command to be cancelled.
This will be very useful - for example, let's say that we have a save to database command but we want to pop a warning box up first. The View can subscribe
to the 'Executing
' event, show the message box, and set the cancelled flag if the user cancels the operation. This keeps the UI logic in the View.
We'll also add an 'Executed
' event which will be called when the command is done. Each of these events will have a delegate written by hand and
an 'EventArgs
' class written by hand - we'll need this to make it Silverlight and WP7 compatible.
Finally, the Command will allow an action to be specified - either a plain old function or a function that takes an object as a parameter - this is what will be called
when the Command is executed. I'm going to drop in the completed class - it'll become more clear in the example afterwards.
The full ViewModelCommand.cs file:
using System.Windows.Input;
using System;
namespace Apex.MVVM
{
public class ViewModelCommand : ICommand
{
public ViewModelCommand(Action action, bool canExecute)
{
this.action = action;
this.canExecute = canExecute;
}
public ViewModelCommand(Action<object> parameterizedAction, bool canExecute)
{
this.parameterizedAction = parameterizedAction;
this.canExecute = canExecute;
}
public virtual void DoExecute(object param)
{
CancelCommandEventHandler executing = Executing;
CommandEventHandler executed = Executed;
if (executing != null)
{
CancelCommandEventArgs args =
new CancelCommandEventArgs() { Parameter = param };
executing(this, args);
if (args.Cancel)
return;
}
if (action != null)
action();
else if (parameterizedAction != null)
parameterizedAction(param);
if (executed != null)
executed(this, new CommandEventArgs() { Parameter = param });
}
protected Action action = null;
protected Action<object> parameterizedAction = null;
private bool canExecute = false;
public bool CanExecute
{
get { return canExecute; }
set
{
if (canExecute != value)
{
canExecute = value;
EventHandler canExecuteChanged = CanExecuteChanged;
if (canExecuteChanged != null)
canExecuteChanged(this, EventArgs.Empty);
}
}
}
#region ICommand Members
bool ICommand.CanExecute(object parameter)
{
return canExecute;
}
void ICommand.Execute(object parameter)
{
this.DoExecute(parameter);
}
#endregion
public event EventHandler CanExecuteChanged;
public event CancelCommandEventHandler Executing;
public event CommandEventHandler Executed;
}
public delegate void CommandEventHandler(object sender, CommandEventArgs args);
public delegate void CancelCommandEventHandler(object sender,
CancelCommandEventArgs args);
public class CommandEventArgs : EventArgs
{
public object Parameter { get; set; }
}
public class CancelCommandEventArgs : CommandEventArgs
{
public bool Cancel { get; set; }
}
}
There's rather a lot here, but nothing we haven't described. Let's add a command to the MVVMSample that builds a full name from the first
and last name properties. The additions to MVVMSample's ViewModel are below:
using Apex.MVVM;
namespace MVVMSample
{
public class MainViewModel : ViewModel
{
public MainViewModel()
{
buildNameCommand = new ViewModelCommand(BuildName, true);
}
private void BuildName()
{
FullName = FirstName + " " + SecondName;
}
private NotifyingProperty firstNameProperty =
new NotifyingProperty("FirstName", typeof(string), string.Empty);
private NotifyingProperty secondNameProperty =
new NotifyingProperty("SecondName", typeof(string), string.Empty);
private NotifyingProperty fullNameProperty =
new NotifyingProperty("FullName", typeof(string), string.Empty);
public string FirstName
{
get { return (string)GetValue(firstNameProperty); }
set { SetValue(firstNameProperty, value); }
}
public string SecondName
{
get { return (string)GetValue(secondNameProperty); }
set { SetValue(secondNameProperty, value); }
}
public string FullName
{
get { return (string)GetValue(fullNameProperty); }
set { SetValue(fullNameProperty, value); }
}
private ViewModelCommand buildNameCommand;
public ViewModelCommand BuildNameCommand
{
get { return buildNameCommand; }
}
}
}
We've added a Command that can be bound to and we've tied it up to the 'BuildName
' function. Now we can update
the View to use it. Additions to MainWindow.xaml are shown below in bold.
<Window x:Class="MVVMSample.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:MVVMSample"
Title="MainWindow" Height="200" Width="300">
-->
<Window.DataContext>
<local:MainViewModel x:Name="viewModel" />
</Window.DataContext>
<Grid>
<StackPanel>
<TextBlock Text="First Name" />
<TextBox Margin="4" Text="{Binding FirstName}" />
<TextBlock Text="Second Name" />
<TextBox Margin="4" Text="{Binding SecondName}" />
<Button Margin="4" Width="80"
Content="Build Name" Command="{Binding BuildNameCommand}" />
<TextBox Margin="4" Text="{Binding FullName}" />
</StackPanel>
</Grid>
</Window>
Run the code. It works, we have exactly what we need. Let's show off the 'CanExecute
' capabilities. Modify the first and second name properties as below.
public string FirstName
{
get { return (string)GetValue(firstNameProperty); }
set
{
SetValue(firstNameProperty, value);
BuildNameCommand.CanExecute = string.IsNullOrEmpty(FirstName) ==
false && string.IsNullOrEmpty(SecondName) == false ;
}
}
public string SecondName
{
get { return (string)GetValue(secondNameProperty); }
set
{
SetValue(secondNameProperty, value);
BuildNameCommand.CanExecute = string.IsNullOrEmpty(FirstName) ==
false && string.IsNullOrEmpty(SecondName) == false ;
}
}
Also tweak the constructor so the command is disabled by default.
buildNameCommand = new ViewModelCommand(BuildName, false);
Finally, tweak the first and second name boxes so that they bind when the text changes rather than when the box is tabbed out of.
<TextBox Margin="4" Text="{Binding FirstName, UpdateSourceTrigger=PropertyChanged}" />
...
<TextBlock Text="Second Name" />
<TextBox Margin="4" Text="{Binding SecondName, UpdateSourceTrigger=PropertyChanged}" />
We have it. A solid ViewModel base and a solid command base for tying in functionality.
Commonality
What's great about this is how easy it is to tie in to Silverlight and WP7. Add a Silverlight Class Library called Apex.Silverlight. Add an MVVM folder. Right click
and choose Add Files. Select the files from the MVVM folder of Apex and choose 'Add as Link'.
Now create a WP7 Class Library called Apex.WP7. Add the MVVM file links as before. To keep the article of a sensible length, I won't include the sample
applications - guess what - they work the same and use the same Apex code base.
In many cases, not everything can be common - some controls will only work on Silverlight or WPF. However, the framework code, ViewModels, drag and drop, etc.,
should be common for each platform.
Next Steps
I have a huge amount of useful code that is based on Apex and adds to Apex - design time code, drag and drop code, custom controls, validation, etc.
I'll add each of these items over the next few weeks - there's some really great stuff on the way.