Click here to Skip to main content
15,868,419 members
Articles / Desktop Programming / WPF

MVVM Pattern in WPF: A Simple Tutorial for Absolute Beginners

Rate me:
Please Sign up or sign in to vote.
4.71/5 (70 votes)
11 Nov 2010CDDL3 min read 463.2K   17.6K   112   69
MVVM pattern in WPF: A simple tutorial for absolute beginners.

Introduction

As part of learning the MVVM pattern, I tried to search many sites and blogs and found most of them explained the pattern in a complicated way. After some research, I cracked the very basic steps in MVVM pattern, and here I am trying to write an MVVM tutorial for absolute beginners.

I don’t think much more time or words need to be spent for explaining the various parts of MVVM and the relationship between MVVM and WPF. If you travel to the depths of WPF, you will realize that MVVM is the best suitable pattern for WPF (you might not understand the difference between these two).

As a formal procedure, I am giving a simple diagram and definition for MVVM:

Image 1

I start this tutorial with two examples: WpfSimple.csproj and WpfMvvmTest.csproj.

For the sake of simplicity, in the first project (WpfSimple.csproj), we are avoiding the Model object (an example with Model will come later).

In the example WpfSimple, the View contains just a Button and no code-behind, but the button click event is loosely bound with the ViewModel. The bindings between the View and ViewModel are simple to construct because a ViewModel object is set as the DataContext of a View. If property values in the ViewModel change, those new values automatically propagate to the View via data binding. When the user clicks a button in the View, a command on the ViewModel executes to perform the requested action.

The View

Image 2

The following code snippets are from the WpfSimple application (available with the tutorial):

XML
<Window x:Class="WpfSimple.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:WpfSimple"
        Title="MainWindow" Height="150" Width="370">
    <Window.DataContext>
        <local:MainWindowViewModel/>
    </Window.DataContext>
        <Grid>
        <Button Content="Click" 
                Height="23" 
                HorizontalAlignment="Left" 
                Margin="77,45,0,0" 
                Name="btnClick" 
                VerticalAlignment="Top" 
                Width="203"
                Command="{Binding ButtonCommand}" 
                CommandParameter="Hai" />
    </Grid>
</Window>

The ViewModel class used here is MainWindowViewModel, the object set as the DataContext of the View.

The ViewModel

The ViewModel class used over here is MainWindowViewModel.

C#
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Input;
using System.Windows;

namespace WpfSimple
{
    class MainWindowViewModel
    {
        private ICommand m_ButtonCommand;
        public ICommand ButtonCommand
        {
            get
            {
                return m_ButtonCommand;
            }
            set
            {
                m_ButtonCommand = value;
            }
        }

        public MainWindowViewModel()
        {
            ButtonCommand=new RelayCommand(new Action<object>(ShowMessage));
        }

        public void ShowMessage(object obj)
        {
            MessageBox.Show(obj.ToString());
        }
    }
}

You can see an empty code-behind file here. If you click on the button, it will prompt a message box, despite the lack of event handling methods in the Views. When the user clicks on buttons, the application reacts and satisfies the user's requests. This works because of bindings that were established on the Command property of Button displayed in the UI. The command object act as an adapter that makes it easy to consume a ViewModel's functionality from a View declared in XAML.

RelayCommand

RelayCommand is the custom class which is implemented in the ICommand interface. You can use any name instead of RelayCommand.
Its usage is as follows:

C#
private ICommand m_ButtonCommand;
public ICommand ButtonCommand
{
get
{
return m_ButtonCommand;
}
set
{
m_ButtonCommand = value;
}
}

ButtonCommand=new RelayCommand(new Action<object>(ShowMessage));

public void ShowMessage(object obj)
{
// do Something
}

In the View using the ButtonCommand as follows:

XML
<Button Content="Click" 
Command="{Binding ButtonCommand}" 
CommandParameter="Hai" /> 
C#
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Input;

namespace WpfSimple
{
    class RelayCommand : ICommand
    {
        private Action<object> _action;

        public RelayCommand(Action<object> action)
        {
            _action = action;
        }

        #region ICommand Members

        public bool CanExecute(object parameter)
        {
            return true;
        }

        public event EventHandler CanExecuteChanged;

        public void Execute(object parameter)
        {
            if (parameter != null)
            {
                _action(parameter);
            }
            else
            {
                _action("Hello World");
            }
        }

        #endregion
    }
}

I think it’s a simple and standard approach. Another common approach is also available by using a nested inner class.

Next, I am going to discuss MVVM with an example that has a Model object. I will also talk a bit about the INotifyPropertyChanged Interface.

INotifyPropertyChanged

The INotifyPropertyChanged interface is used to notify clients, typically binding clients, when a property value has changed. The INotifyPropertyChanged interface contains an event called PropertyChanged. Whenever a property on a ViewModel / Model object has a new value, it can raise the PropertyChanged event to notify the WPF binding system of the new value. Upon receiving that notification, the binding system queries the property, and the bound property on some UI element receives the new value.

From the example WpfMvvmTest project, I am illustrating the following code:

C#
public class Product:INotifyPropertyChanged
{
    private int m_ID;
    private string m_Name;
    private double m_Price;

    #region INotifyPropertyChanged Members

    public event PropertyChangedEventHandler PropertyChanged;

    private void OnPropertyChanged(string propertyName)
    {
        if (PropertyChanged != null)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }
    }
    #endregion

    public int ID
    {
        get
        {
            return m_ID;
        }
        set
        {
            m_ID = value;
            OnPropertyChanged("ID");
        }
    }
    public string Name
    {
        get
        {
            return m_Name;
        }
        set
        {
            m_Name = value;
            OnPropertyChanged("Name");
        }
    }
    public double Price
    {
        get
        {
            return m_Price;
        }
        set
        {
            m_Price = value;
            OnPropertyChanged("Price");
        }
    }
}

Another approach for binding the ViewModel object as a DataContext of the View is shown here:

C#
public partial class App: Application
{
    protected override void OnStartup(StartupEventArgs e)
    {
        base.OnStartup(e);
        WpfMvvmTest.MainWindow window = new MainWindow();
        ProductViewModel VM = new ProductViewModel();
        window.DataContext = VM;
        window.Show();
    }
}

In the following code snippet, we use the inner class approach for implementing the ICommand interface; i.e., create a private nested class within the ViewModel class so that the command has access to private members of its containing ViewModel and does not pollute the namespace.

The disadvantage of this approach is that it is required to create a nested class that implements ICommand for each command exposed by a ViewModel, which will increase the size of the ViewModel class.

C#
class ProductViewModel 
{
    private IList<Product> m_Products;
    public ProductViewModel()
    {
        m_Products = new List<Product>
        {
            new Product {ID=1, Name ="Pro1", Price=10},
            new Product{ID=2, Name="BAse2", Price=12}
        };
    }
    public IList<Product> Products
    {
        get
        {
            return m_Products;
        }
        set
        {
            m_Products = value;
        }
    }
    private ICommand mUpdater;
    public ICommand UpdateCommand
    {
        get
        {
            if (mUpdater == null)
                mUpdater = new Updater();
            return mUpdater;
        }
        set
        {
            mUpdater = value;
        }
    }
    private class Updater : ICommand
    {
        #region ICommand Members

        public bool CanExecute(object parameter)
        {
            return true;
        }
        public event EventHandler CanExecuteChanged;
        public void Execute(object parameter)
        {

        }
        #endregion
    }
}

From the following View XAML, you can understand how to bind a collection of objects to a list control.

XML
<Window x:Class="WpfMvvmTest.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="350" Width="525">
    <Grid Height="314">
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="*"/>
            <RowDefinition Height="Auto"/>
        </Grid.RowDefinitions>
        <ListView Name="ListViewEmployeeDetails" Grid.Row="1" 
               Margin="4,109,12,23"  ItemsSource="{Binding Products}"  >
            <ListView.View>
                <GridView x:Name="grdTest">
                    <GridViewColumn Header="ID" 
                          DisplayMemberBinding="{Binding ID}"  Width="100"/>
                    <GridViewColumn Header="Name" 
                          DisplayMemberBinding="{Binding Name}"  Width="100" />
                    <GridViewColumn Header="Price" 
                          DisplayMemberBinding="{Binding Price}" Width="100" />
                </GridView>
            </ListView.View>
        </ListView>
        <TextBox Grid.Row="1" Height="23" HorizontalAlignment="Left" 
           Margin="80,7,0,0" Name="txtID" VerticalAlignment="Top" Width="178" 
           Text="{Binding ElementName=ListViewEmployeeDetails,Path=SelectedItem.ID}" />
        <TextBox Grid.Row="1" Height="23" HorizontalAlignment="Left" 
           Margin="80,35,0,0" Name="txtName" VerticalAlignment="Top" Width="178" 
           Text="{Binding ElementName=ListViewEmployeeDetails,Path=SelectedItem.Name}" />
        <TextBox Grid.Row="1" Height="23" HorizontalAlignment="Left" Margin="80,61,0,0" 
           Name="txtPrice" VerticalAlignment="Top" Width="178" 
           Text="{Binding ElementName=ListViewEmployeeDetails,Path=SelectedItem.Price}" />
        <Label Content="ID" Grid.Row="1" HorizontalAlignment="Left" 
               Margin="12,12,0,274" Name="label1" />
        <Label Content="Price" Grid.Row="1" Height="28" 
           HorizontalAlignment="Left" Margin="12,59,0,0" 
           Name="label2" VerticalAlignment="Top" />
        <Label Content="Name" Grid.Row="1" Height="28" 
           HorizontalAlignment="Left" Margin="12,35,0,0" 
           Name="label3" VerticalAlignment="Top" />
        <Button Content="Update" Grid.Row="1" Height="23" 
           HorizontalAlignment="Left" Margin="310,40,0,0" Name="btnUpdate" 
           VerticalAlignment="Top" Width="141"
           Command="{Binding Path=UpdateCommand}"
                />
    </Grid>
</Window>

I hope you were able to get a small insight into MVVM from this article.

License

This article, along with any associated source code and files, is licensed under The Common Development and Distribution License (CDDL)


Written By
Software Developer (Senior) BlackstoneCube
United Arab Emirates United Arab Emirates
6 years of extensive experience in Business Analysis, Implementation, Solution Providing, Designing, Developing, and testing of distributed applications.
Hands on experience in handling Multiple Projects simultaneously. Strong experience implementing enterprises distributed application using C#, VB.Net and Winform.
VSTO(Visual studio Tools for office) expert and successfully executed many project for Excel, Word and Outlook. Depth knowledge in .Net 3.5 with design and development experience in WCF and WPF application.
Currently Working in BlackstoneCube Business Solution
www.blackstonecube.com

Comments and Discussions

 
AnswerRe: Excellent Job Pin
ptMujeeb18-Jul-11 6:51
professionalptMujeeb18-Jul-11 6:51 
GeneralRequest for new examples Pin
AshwinMendon19-May-11 2:50
AshwinMendon19-May-11 2:50 
QuestionWhat does UpdateCommand do? Pin
ddawiddd6-Apr-11 13:29
ddawiddd6-Apr-11 13:29 
AnswerRe: What does UpdateCommand do? Pin
ptMujeeb6-Apr-11 18:03
professionalptMujeeb6-Apr-11 18:03 
Generalproblem create the command Pin
kshitij verma6-Apr-11 2:34
kshitij verma6-Apr-11 2:34 
GeneralRe: problem create the command Pin
ptMujeeb6-Apr-11 6:22
professionalptMujeeb6-Apr-11 6:22 
GeneralMy vote of 4 Pin
Farawin13-Dec-10 0:18
Farawin13-Dec-10 0:18 
GeneralRe: My vote of 4 Pin
ptMujeeb21-Dec-10 1:43
professionalptMujeeb21-Dec-10 1:43 
GeneralRe: My vote of 4 Pin
SantoshReddy Avaji6-Sep-12 2:44
SantoshReddy Avaji6-Sep-12 2:44 
GeneralTwo MainWindows (WpfMvvmTest) Pin
jlafay16-Nov-10 6:02
jlafay16-Nov-10 6:02 
General[My vote of 2] Perspective from a MVVM newbie. Pin
jlafay16-Nov-10 5:28
jlafay16-Nov-10 5:28 
GeneralTypo Pin
dzCepheus11-Nov-10 2:14
dzCepheus11-Nov-10 2:14 
GeneralRe: Typo Pin
ptMujeeb11-Nov-10 18:16
professionalptMujeeb11-Nov-10 18:16 
GeneralMy vote of 2 Pin
pikul10-Nov-10 21:21
pikul10-Nov-10 21:21 
GeneralRe: My vote of 2 Pin
ptMujeeb10-Nov-10 23:31
professionalptMujeeb10-Nov-10 23:31 
GeneralRe: My vote of 2 Pin
pikul11-Nov-10 0:55
pikul11-Nov-10 0:55 
GeneralMy vote of 3 Pin
SledgeHammer0110-Nov-10 13:58
SledgeHammer0110-Nov-10 13:58 
GeneralRe: My vote of 3 Pin
ptMujeeb10-Nov-10 18:02
professionalptMujeeb10-Nov-10 18:02 
GeneralRe: My vote of 3 Pin
SledgeHammer0111-Nov-10 5:09
SledgeHammer0111-Nov-10 5:09 

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.