Reader Level:
ARTICLE

A simple concretization of MVVM pattern

Posted by Bechir Bejaoui Articles | WPF February 23, 2010
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.
  • 0
  • 0
  • 42674
Download Files:
 

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.

1.gif

The advantages of this Pattern are to render the business logic independent from the view for two main purposes, namely:
  1. 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. 
  2. 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:

2.gif

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:

3.gif

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 membersInformation
First name : A string fieldCould 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 fieldCould 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 fieldCould 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 commandIt 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 commandIt 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 commandIt 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

4.gif

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.

5.gif

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"
                                  DisplayMemberBinding="{Binding Path=LastName}"/>
                    <GridViewColumn Header="Age"
                                  Width="100"
                                  DisplayMemberBinding="{Binding Path=Age}"/>
                </GridView>
            </ListView.View>
        </ListView>
    </Grid>
</Window>

And the result will be as expected: