I. Introduction:
In this article I will show a simple MVVM implementation to help understand this pattern and how to deal with it, first let's introduce the pattern. MVVM is an abbreviation of Model-View-View Model. It's a pattern that we can employ within WPF or Silverlight applications. The main purpose behind that is to separate the view, I mean the interface from the business logic by decoupling each other. The View Model which is normally a module, a class for example that ties the codeless view with the business logic and/or the data source.
The advantages of this Pattern are to render the business logic independent from the view for two main purposes, namely:
- For maintenance reasons, I mean if we want to entirely change the interface, we can do this easily, we just remove the current view and design the new view, all that we have to do is to bind the new view elements and controls' properties to the View Model module properties that's all.
- The second purpose is to render possible the test operations though unit testing, which is not possible within the classic implementation
II. The scenario
To well understand the logic, here is an example:
This is a simple application that adds, deletes a list of persons' objects. It contains three fields to set the persons' parameters which are namely the first name, the last name and the age. The application also contains three buttons; one used to add a new person, the second is used to delete a selected person from the list and the last one is to reset the collection and replace all the added persons with a default list.
So to build the solution we need four main steps:
II.1 First step:
The first step is to build the view, we can do this through Visual Studio IDE or though Expression blend, but one thing that one should keep in mind is that is not necessary to name the view controls, because we will not implement the view code behind. It will be clean like this:
using System.Windows;
namespace MVVM
{
/// <summary>
/// No
interaction logic with Window1.xaml.cs
/// </summary>
public partial
class Window1
: Window
{
public Window1()
{
InitializeComponent();
}
}
}
II.2 Second step:
We should provide, a Model for our application which means the data and logic provider. It could be a code that retrieves data from a data base, or some services like a WCF service or so. In our case, it will be a custom provider that we develop in a separate DLL project. It will be as under:
Here is now the implementation of the person class
namespace MVVM.Provider
{
public class
Person
{
public string FirstName { get;
set; }
public string LastName { get;
set; }
public int Age { get; set; }
public override string
ToString()
{
return
string.Format("{0}
{1} is {2} year(s) old");
}
}
}
The main contract that represents the data application data provider is:
namespace MVVM.Provider
{
public interface
IPersonsProvider
{
void AddPerson(Person
person);
void Delete(Person
person);
void Reset();
}
}
And finally, this is an implementation of the above contract:
/// <summary>
/// This class
represents an implementation of our provider, it simply profit
/// of the
observable collection type of Person
/// </summary>
public class
PersonsProvider: ObservableCollection<Person>,IPersonsProvider
{
/// <summary>
/// This list will be used to polulate the list when the
/// reset button is clicked
/// </summary>
private
List<Person>
originalCollection;
/// <summary>
/// This is the parameterless constructor that populate the
/// original collection
/// </summary>
public
PersonsProvider()
{
originalCollection = new List<Person>
{
new
Person{FirstName="Scorpio",LastName="iron",Age=32},
new Person{FirstName="Scorpio",LastName="iron",Age=32},
new
Person{FirstName="Scorpio",LastName="iron",Age=32},
new
Person{FirstName="Scorpio",LastName="iron",Age=32},
new
Person{FirstName="Scorpio",LastName="iron",Age=32},
new
Person{FirstName="Scorpio",LastName="iron",Age=32},
new
Person{FirstName="Scorpio",LastName="iron",Age=32},
new
Person{FirstName="Scorpio",LastName="iron",Age=32}
};
}
/// <summary>
/// void: The main purpose of this method is to add
/// a person to the list
/// </summary>
/// <param
name="person">Person: Person to
be added</param>
public void AddPerson(Person
person)
{
this.Add(person);
}
/// <summary>
/// void: The main purpose of this method is to delete
/// a person from the list
/// </summary>
/// <param
name="person">Person: Person to
be deleted</param>
public void Delete(Person
person)
{
this.RemoveAt(IndexOf(person));
}
/// <summary>
/// void: The main purpose of this method to reset the list
/// by replacing it with the original collection items
/// </summary>
public void Reset()
{
this.ClearItems();
foreach
(Person p in
originalCollection)
{
this.Add(p);
}
}
}
}
The above implementation could be replaced by any other logic like a WCF service; the essential to do is that the contract that IPersonsProvider interface exposes has to be respected.
II.3 Third step:
It is the most important one. Within this step we will build our View Model, the application engine if you want. First let's parse the principal fields to fill and the principal actions to do:
View Model members | Information |
First name : A string field | Could be a dependency property or it could be a NotifyPropertyChanged CLR classic property to be self kept up to date, once changes are done. |
Last name : A string field | Could be a dependency property or it could be a NotifyPropertyChanged CLR classic property to be self kept up to date, once changes are done. |
Age : An integer field | Could be a dependency property or it could be a NotifyPropertyChanged CLR classic property to be self kept up to date, once changes are done. |
Add person : A command | It could be a RoutedCommand or a class that implements ICommand interface to avoid implement the click event handler of the Add person button |
Delete person : A command | It could be a RoutedCommand or a class that implements ICommand interface to avoid implement the click event handler of the Delete person button |
Reset : A command | It could be a RoutedCommand or a class that implements ICommand interface to avoid implement the click event handler of the Reset button |
In our case, we chose to use dependency properties to represent the text fields and objects type of ICommand Interfaces to implement our custom commands.
First, let's begin developing our View Model, we start by adding three dependency properties those represent the First name, the last name and the age.
//Dependency proeprty
wrapper for AgeProperty
public int Age
{
get
{ return (int)GetValue(AgeProperty);
}
set
{ SetValue(AgeProperty, value); }
}
/* Using a
DependencyProperty as the backing store for Age.
* This enables animation, styling,
binding, etc...*/
public static readonly DependencyProperty AgeProperty =
DependencyProperty.Register("Age",
typeof(int), typeof(ViewModel),
new
UIPropertyMetadata(0));
//Dependency
proeprty wrapper for LastNameProperty
public string LastName
{
get
{ return (string)GetValue(LastNameProperty);
}
set { SetValue(LastNameProperty, value); }
}
/* Using a
DependencyProperty as the backing store for Age.
* This enables animation, styling,
binding, etc...*/
public static readonly DependencyProperty LastNameProperty =
DependencyProperty.Register("LastName",
typeof(string),
typeof(ViewModel),
new
UIPropertyMetadata());
//Dependency
proeprty wrapper for FirstName
public string FirstName
{
get
{ return (string)GetValue(FirstNameProperty);
}
set
{ SetValue(FirstNameProperty, value); }
}
/* Using a
DependencyProperty as the backing store for Age.
* This enables animation, styling,
binding, etc...*/
public static readonly DependencyProperty FirstNameProperty =
DependencyProperty.Register("FirstName",
typeof(string),
typeof(ViewModel),
new UIPropertyMetadata());
Afterward, we will add methods to deal with the Provider's methods add, remove and reset within the View Model class, I mean a sort of method wrappers. To do that we should inject a Provider instance through the View Model constructor like this:
IPersonsProvider _source;
public ViewModel(IPersonsProvider Source)
{
_source = Source;
}
And then we can make use of our provider methods within the View Model as follow:
/// <summary>
/// void: This method wraps up the provider AddPerson method
/// which is represented by the _source object
/// </summary>
public void AddPerson()
{
string
firstName = this.FirstName;
string
lastName = this.LastName;
int
age = this.Age;
_source.AddPerson(new Person {
FirstName = firstName,
LastName =
lastName, Age = age });
FirstName = "";
LastName = "";
Age = 0;
}
/// <summary>
/// void: This method is used to wrap up the provider Delete
method
/// </summary>
/// <param
name="listView"></param>
public void DeletePerson(System.Windows.Controls.ListView listView)
{
Person
person = listView.SelectedItem as Person;
_source.Delete(person);
}
/// <summary>
/// void: This method is used to wrap up the provider reset
method
/// </summary>
public void
ResetCollection()
{
_source.Reset();
}
A little word about the Delete method should be said about the parameter to make issues clear. In fact, the ListView parameter is used because the application publishes the person's collection through a ListView. And when a user selects an item type of person, this selected item will be deleted from the collection, so we will pass the ListView as a parameter because here we won't deal directly with the view code behind, instead we deal indirectly with the view through the View Model. The idea is that we pass the ListView of the current interface through the command parameter.
Let's develop the commands those will replace the event handlers which normally exist is the code behind implementation. There is one for adding persons, one for deleting persons and one for resetting the persons' list.
To deal with commands, we have two choices; we can profit of the all ready command implementations through Routed commands or directly implement the ICommand interface.
We can choose the second alternative for instance; it's an occasion to show how commands are implemented from the scratch.
The ICommand is an interface that Microsoft provides within the System.Windows.Input in order to implement customized command behaviors. Here is the ICommand interface.
interface ICommand
{
void
Execute(object parameter);
bool
CanExecute(object parameter);
event EventHandler CanExecuteChanged;
}
Now, let's implement this interface for each action needed in the application. Let's begin first by the Add command, recall that we do use injection technique to reference the view model within the target command.
namespace MVVM
{
/// <summary>
/// This is an
add command implementation
/// </summary>
public class
AddCommand : ICommand
{
private
ViewModel VM;
/// <summary>
/// Constructor that sets a ViewModel instance
/// </summary>
/// <param
name="VM"></param>
public
AddCommand(ViewModel VM)
{
this.VM
= VM;
}
#region ICommand Members
/// <summary>
/// void: This method disable the command if the FirstName and
the LastName
/// are not given yet by the user through the interface, the
FirstName and the
/// LastName properties are provided by the ViewModel instance
that is injected
/// thtrough the command constructor
/// </summary>
/// <param
name="parameter"></param>
/// <returns>boolean: It precises whether the command is enabled or
disabled</returns>
public bool CanExecute(object
parameter)
{
return
!string.IsNullOrEmpty(VM.FirstName)
&& !string.IsNullOrEmpty(VM.LastName);
}
/// <summary>
/// event: This event is important because it tels the command
that
/// a command state is changed when somme conditions are
fullfiled
/// </summary>
public event EventHandler
CanExecuteChanged
{
add
{ CommandManager.RequerySuggested += value; }
remove
{ CommandManager.RequerySuggested -= value; }
}
/// <summary>
/// void: This method will wrap the ViewModel method that adds
/// a person to the list
/// </summary>
/// <param
name="parameter"></param>
public void Execute(object
parameter)
{
VM.AddPerson();
}
#endregion
}
}
Now let's implement the Reset command exactly like the add command
using System;
using System.Windows.Input;
namespace MVVM
{
public class
ResetCommand :ICommand
{
ViewModel
VM;
public
ResetCommand(ViewModel VM)
{
this.VM
= VM;
}
#region ICommand Members
public bool CanExecute(object
parameter)
{
return
true;
}
public event EventHandler
CanExecuteChanged;
public void Execute(object
parameter)
{
VM.ResetCollection();
}
#endregion
}
}
And finally the Delete command
public class DeleteCommand
: ICommand
{
private
ViewModel VM;
public
DeleteCommand(ViewModel VM)
{
this.VM
= VM;
}
#region ICommand Members
/// <summary>
/// bool: This method will enable or disable the delete
command
/// according to the user whether he(she) selects the person
going
/// to be deleted or not
/// </summary>
/// <param
name="parameter"></param>
/// <returns></returns>
public bool
CanExecute(object parameter)
{
if
(parameter is System.Windows.Controls.ListView)
{
if
((parameter as System.Windows.Controls.ListView).SelectedItems.Count > 0)
{
return
true;
}
return
false;
}
return false;
}
public event EventHandler
CanExecuteChanged
{
add
{ CommandManager.RequerySuggested += value; }
remove
{ CommandManager.RequerySuggested -= value; }
}
/// <summary>
/// void: This method will delegate the DeletePerson method of
the ViewModel
/// instance
/// </summary>
/// <param
name="parameter">ListView: This
parameter will represent
/// the list view of the current interface</param>
public void Execute(object
parameter)
{
VM.DeletePerson((parameter as System.Windows.Controls.ListView));
}
#endregion
}
The next step is to add those commands as properties to the View Model class so that we can use them later,
//Those
are the view model commands
public ResetCommand resetCommand{get;set;}
public AddCommand addCommand { get;
set; }
public DeleteCommand deleteCommand { get; set; }
public
ViewModel(IPersonsProvider Source)
{
_source = Source;
this.resetCommand
= new ResetCommand(this);
this.addCommand
= new AddCommand(this);
this.deleteCommand
= new DeleteCommand(this);
}
Note that we haven't forget to instantiate them at the View Model constructor level and pass them the current ViewModel instance as parameter, this technique is called injection, note that we inject the ViewModel current instance in the command constructor.
Example:
this.resetCommand
= new ResetCommand(this); this = current
View Model instance
Now that our View Model implementation is completed:
using System;
using System.Windows;
using MVVM.Provider;
namespace MVVM
{
public class
ViewModel : DependencyObject
{
IPersonsProvider
_source;
public ResetCommand resetCommand{get;set;}
public AddCommand addCommand { get;
set; }
public DeleteCommand deleteCommand { get; set; }
public
ViewModel(IPersonsProvider Source)
{
_source = Source;
this.resetCommand
= new ResetCommand(this);
this.addCommand
= new AddCommand(this);
this.deleteCommand
= new DeleteCommand(this);
}
public IPersonsProvider ViewModelProvider
{
get
{ return _source; }
}
/// <summary>
/// void: This method wraps up the provider AddPerson method
/// which is represented by the _source object
/// </summary>
public void AddPerson()
{
string
firstName = this.FirstName;
string
lastName = this.LastName;
int
age = this.Age;
_source.AddPerson(new Person {
FirstName = firstName,
LastName =
lastName, Age = age });
FirstName = "";
LastName = "";
Age = 0;
}
/// <summary>
/// void: This method is used to wrap up the provider Delete
method
/// </summary>
/// <param
name="listView"></param>
public void DeletePerson(System.Windows.Controls.ListView listView)
{
Person
person = listView.SelectedItem as Person;
_source.Delete(person);
}
/// <summary>
/// void: This method is used to wrap up the provider reset
method
/// </summary>
public void ResetCollection()
{
_source.Reset();
}
//Dependency
proeprty wrapper for AgeProperty
public int Age
{
get
{ return (int)GetValue(AgeProperty);
}
set
{ SetValue(AgeProperty, value); }
}
/* Using a DependencyProperty
as the backing store for Age.
* This enables animation, styling,
binding, etc...*/
public static readonly DependencyProperty AgeProperty =
DependencyProperty.Register("Age",
typeof(int), typeof(ViewModel),
new
UIPropertyMetadata(0));
//Dependency
proeprty wrapper for LastNameProperty
public string LastName
{
get
{ return (string)GetValue(LastNameProperty);
}
set
{ SetValue(LastNameProperty, value); }
}
/* Using a
DependencyProperty as the backing store for Age.
* This enables animation, styling,
binding, etc...*/
public static readonly DependencyProperty LastNameProperty =
DependencyProperty.Register("LastName",
typeof(string),
typeof(ViewModel),
new
UIPropertyMetadata());
//Dependency
proeprty wrapper for FirstName
public string FirstName
{
get
{ return (string)GetValue(FirstNameProperty);
}
set
{ SetValue(FirstNameProperty, value); }
}
/* Using a
DependencyProperty as the backing store for Age.
* This enables animation, styling,
binding, etc...*/
public static readonly DependencyProperty FirstNameProperty =
DependencyProperty.Register("FirstName",
typeof(string),
typeof(ViewModel),
new
UIPropertyMetadata());
public bool CanAdd()
{
bool
result = !string.IsNullOrEmpty(this.FirstName)
&& !string.IsNullOrEmpty(this.LastName);
return
true;
}
}
}
Note that the ViewModel inherits from DependencyObject it is not the hazard, of Corse. In fact, the ViewModel inherits the DependencyObject so that we can use SetValue and GetValue to leverage the dependency properties wrappers.
Next, we proceed to the fourth step which consists of binding the interface control's properties to the View Model properties.
II.4 Fourth step:
First we open the App.xaml file and we move the selected XAML attribute as the below figure shows
The purpose behind this, is to let load the view programmatically through code, that is, we implement the Window1.xaml.cs file as follow:
namespace MVVM
{
/// <summary>
///
Interaction logic for App.xaml
/// </summary>
public partial
class App : Application
{
IPersonsProvider
Provider;
protected
override void
OnStartup(StartupEventArgs e)
{
base.OnStartup(e);
Provider = new
PersonsProvider();
ViewModel
VM = new ViewModel(Provider);
Window1
window1 = new Window1();
window1.DataContext = VM;
window1.Show();
}
}
}
The fact that we set the Data Context of the entire window to VM, we can bind any existed property within the interface with those belong to the VM instance. The schema below describes how the data is routed from the data source or the provider to the ViewModel, Then the View consumes that data through the ViewModel.
Here is a re modulated XAML interface, note that all controls except the ListView do have a name; all business is done through the binding. Also note that no element name or source element is defined explicitly within the binding expression, this is because the Data Context of the whole view is already set at the Application code behind.
<Window x:Class="MVVM.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:MVVM"
Title="MVVM DEMO" Height="497" Width="417">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="154*"
/>
<RowDefinition Height="305*"
/>
</Grid.RowDefinitions>
<Grid Margin="12,12,0,9" Name="grid1">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="89*"
/>
<RowDefinition Height="44*"
/>
</Grid.RowDefinitions>
<Button Grid.Row="1" Margin="6,0,5.75,7.362"
Name="btnAdd" Command="{Binding addCommand}">
Add person</Button>
<Button Margin="0,0,9,6" Name="btnDelete"
Grid.Column="2" Grid.Row="1"
Command="{Binding deleteCommand}"
CommandParameter="{Binding ElementName=listView}">
Delete
person</Button>
</Grid>
<GroupBox Header="Person" Margin="24,12.17,12,59" Name="groupBox1">
<Grid>
<TextBox Height="23" Name="txtFirstName"
Text="{Binding Path=FirstName,Mode=TwoWay,
UpdateSourceTrigger=PropertyChanged}"
HorizontalAlignment="Left" Margin="8,0,0,6"
VerticalAlignment="Bottom"
Width="64"
/>
<TextBox
HorizontalAlignment="Left"
Text="{Binding Path=LastName,Mode=TwoWay,
UpdateSourceTrigger=PropertyChanged}"
Margin="94,0,0,6" Name="txtLastName"
Width="64" Height="23"
VerticalAlignment="Bottom" />
<TextBox
HorizontalAlignment="Right"
Text="{Binding Path=Age, Mode=TwoWay,
UpdateSourceTrigger=PropertyChanged}"
Margin="0,0,95,6" Name="txtAge"
Width="64" Height="23"
VerticalAlignment="Bottom"
/>
<TextBlock
HorizontalAlignment="Left"
Margin="6,2,0,0" Name="textBlock1"
Width="52" Height="26.553"
VerticalAlignment="Top" Text="First
name" />
<TextBlock
HorizontalAlignment="Left"
Margin="84,2,0,0" Name="textBlock2"
Text="Last
name" Width="52"
Height="26.553"
VerticalAlignment="Top" />
<TextBlock
HorizontalAlignment="Right"
Margin="0,2,107,0" Name="textBlock3"
Text="Age" Width="52"
Height="26.553"
VerticalAlignment="Top" />
<Button Margin="0,6,6,6" Name="btnReset"
HorizontalAlignment="Right" Width="66"
Command="{Binding resetCommand}">Reset</Button>
</Grid>
</GroupBox>
<ListView Grid.Row="1"
ItemsSource="{Binding Path=ViewModelProvider}"
Name="listView">
<ListView.View>
<GridView x:Name="grdDisplay">
<GridViewColumn Header="First
name"
Width="100"
DisplayMemberBinding="{Binding Path=FirstName}"/>
<GridViewColumn Header="Last
name"
Width="100"