Intersoft ClientUI Documentation
MVVM Pattern Overview

This topic provides an overview of the MVVM pattern, its terminology, concepts and benefits, as well as describing how ClientUI supports the MVVM pattern throughout its entire framework and controls. It also includes the examples of using MVVM in several key scenarios as well as links to advanced walkthroughs.

This topic contains the following sections:

MVVM: The Basics

Model-View-ViewModel (further referred as MVVM) is an architectural pattern used in software engineering that originated from Microsoft as a specialization of the Presentation Model design pattern. The MVVM pattern is targeted at modern UI development platforms that originally invented in Windows Foundation Presentation (WPF) and later adopted in Silverlight.

In its fundamental design concept, MVVM makes use of specific functions in Silverlight such as bindings, data context and visual tree model to better facilitate the separation of view layer development from the rest of the pattern by removing virtually all “code behind” from the view layer. Instead of requiring UX designers to write the view code, they can use the native XAML markup language and create bindings to the ViewModel, which is written and maintained by application developers. This separation of roles allows UX designers to focus on the user experience needs rather than programming or business logic, allowing for the layers of an application to be developed in multiple work streams.

The following illustration describes the main components of a Silverlight/WPF application that uses MVVM pattern. The View, ViewModel and Model layer are highlighted in the dotted line rectangle to show you the roles and scope of the MVVM pattern in a typical page.

 

The following sections explain the concept of each layer described in the above illustration.

The View

The View layer is the user interface of your application which mainly consisted of layout and user interface controls written in XAML markup. The View layer often includes resources that required in the user interface, such as data templates and styles.

The Model

The Model layer refers to the entities, business objects, data source, proxy objects and other repositories available in your application. The Model should be designed in a way that it does not know the existence of the View or the ViewModel. This means that the model should not access, reference or interact directly with the ViewModel or the View.

The ViewModel

The ViewModel layer describes the user interaction logic of the view by exposing the properties such as commands, observable commands and selection objects that can be bound from the view. In most cases, the ViewModel encapsulates the Model and expose it as properties that can be referenced from the View. The ViewModel also contains methods that perform validation and certain business logic which is invoked by the command initiated from the View. Note that the ViewModel should be designed in a way that it does not know the existence of the View. This means that the ViewModel should not access, reference or interact directly with the View.

The benefits, examples and walkthroughs of using MVVM pattern with the ClientUI framework and controls are explained later in this topic.

Why Use MVVM?

In short, the Model-View-ViewModel pattern is a simple and effective set of guidelines for designing and implementing a Silverlight/WPF application. It allows you to create a strong separation between data, behavior, and presentation, making it easier to control the software development process primarily on the user interface engineering.

The MVVM pattern introduces a number of advantages over the classic ViewModel pattern, such as:

MVVM Support in ClientUI

In the previous sections, you have learned the basic concepts and benefits of using MVVM pattern in your application development. This section describes how ClientUI supports the MVVM pattern in an end-to-end approach throughout its frameworks and user interface controls.

Commanding

One of the fundamental pillars that makes MVVM a powerful pattern is the commanding model originally introduced in WPF. A command can be declaratively defined in the XAML while its logic is separated to another class, which can be the code behind of the view or a ViewModel class. This concept aligns with the MVVM fundamental whereby the user interaction logic of a view should be loosely coupled from the View layer.

ClientUI provides a comprehensive commanding framework to facilitate the application development using MVVM pattern that works in both Silverlight and WPF project. The commanding framework is engineered with the commanding infrastructure, behaviors and specifications of WPF. As the result, you can now take advantage of commanding in a Silverlight project that will also work in WPF. This unification process is often referred as unified development model. For more information on how ClientUI promotes unified development model in Silverlight and WPF, see Unified Development Model.

The commanding framework in ClientUI introduces a number of commands that specifically designed for MVVM pattern such as DelegateCommand and HybridDelegateCommand. The DelegateCommand allows you to define the commands in the ViewModel and write the event handler for the delegates in the ViewModel as well, thus eliminates the need to reference the event handler directly in the View or code behind of the View. To learn more about the commanding concept, see Commanding Overview.

The following example shows how a DelegateCommand is configured in an MVVM pattern implementation.

XAML
Copy Code
<Intersoft:UXToolBar>
    <Intersoft:UXToolBarButton Content="Save"
                               Command="{Binding SaveCommand}"/>
</Intersoft:UXToolBar>
C#
Copy Code
// Constructor
public EditClientViewModel()
    : base()
{
    // Creates the Save DelegateCommand
    this.SaveCommand = new DelegateCommand(ExecuteSave, CanExecuteSave);
}

// Commands
public DelegateCommand SaveCommand
{
    get;
    set;
}

// Event Handlers
private bool CanExecuteSave(object parameter)
{
    return true;
}

private void ExecuteSave(object parameter)
{
    // do something when user clicks on the Save button
}

More complete examples and walkthroughs for implementing MVVM pattern are available later in this topic.

Frameworks

ClientUI provides a variety of frameworks that supports MVVM pattern such as the ItemTemplateSelector and ItemContainerStyleSelector implemented in the framework level that powers the entire user interface controls in the ClientUI. The selector enables you to define the data templates and styles in the XAML while keeping the selector logic separated. The data templates and styles make MVVM pattern more usable by taking advantage the powerful XAML binding engine which automatically locate and apply those templates and styles at run time.

The following example shows how to configure a UXListBox to show customized item template that defined in XAML to be based on the alternating row logic that written in selector logic class.

XAML
Copy Code
<Intersoft:UXListBox ItemsSource="{Binding Path=Feedbacks}">
     <Intersoft:UXListBox.ItemTemplateSelector>
         <local:CustomItemTemplateSelector>
             <local:CustomItemTemplate Color="Silver" Template="{StaticResource Feedback_Silver}"/>
             <local:CustomItemTemplate Color="Natural" Template="{StaticResource Feedback_Natural}"/>
         </local:CustomItemTemplateSelector>
     </Intersoft:UXListBox.ItemTemplateSelector>
</Intersoft:UXListBox>

...

<Intersoft:UXPage.Resources>

        <DataTemplate x:Key="Feedback_Silver">
            <StackPanel HorizontalAlignment="Center">
                 <Intersoft:StylishLabel Background="{StaticResource SilverGradient}" 
                                         BorderBrush="{StaticResource SilverBorder}">
                      <TextBlock Text="{Binding Quote}"/>
                 </Intersoft:StylishLabel>
            </StackPanel>
        </DataTemplate>

        <DataTemplate x:Key="Feedback_Natural">
            <StackPanel HorizontalAlignment="Center">
                 <Intersoft:StylishLabel Background="{StaticResource NaturalGradient}" 
                                         BorderBrush="{StaticResource NaturalBorder}">
                      <TextBlock Text="{Binding Quote}"/>
                 </Intersoft:StylishLabel>
            </StackPanel>
        </DataTemplate>

</Intersoft:UXPage.Resources>

To learn more about the ClientUI architecture and concepts, see ClientUI Architecture Overview. To learn more about items control, see Items Control Overview.

UI Controls

While the commanding and frameworks are essential MVVM's building blocks, the lacking of UI controls that support MVVM semantics can create difficulty and unnecessary complexity in implementing MVVM pattern in your application. For instance, a DialogBox control that does not support MVVM semantics will require developers to write plumbing code, a number of interfaces and proxy implementation to workaround the limitation.

ClientUI shipped with over 120 rich presentation controls that completely support MVVM pattern. All controls in ClientUI are thoughtfully engineered in a way that expose bindable properties for features that are likely consumed in the view model, particularly selection related properties, property bindings, items source, busy state management, and more.

For instances, consider an advanced control such as CoverFlow that shows a list of contacts. As users flipped to the next item, the contact details should be reflected accordingly. Using UXFlow that fully supports MVVM, you can handle the item presentation and selection entirely in XAML. The following example shows how to setup the UXFlow to achieve the scenario described in the example.

XAML
Copy Code
<Intersoft:UXFlow ItemsSource="{Binding Contacts}" 
                  SelectedItem="{Binding SelectedItem, Mode=TwoWay}"
                  ImageMemberPath="Contact.Photo" 
                  TitleMemberPath="Contact.Name" 
                  SubTitleMemberPath="Contact.Title"/>
<Border>
    <Intersoft:DockPanel DataContext="{Binding SelectedItem}">
        <Intersoft:FieldLabel Header="Address: ">
            <TextBlock Text="{Binding Contact.Address}"/>
        </Intersoft:FieldLabel>
        <Intersoft:FieldLabel Header="Mobile: ">
            <TextBlock Text="{Binding Contact.MobileNumber}"/>
        </Intersoft:FieldLabel>
    </Intersoft:DockPanel>
</Border>

For more walkthroughs on using ClientUI controls with MVVM pattern, see MVVM Pattern Walkthroughs

Base Classes and Templates

To help you quickly get started with MVVM pattern application development, ClientUI provides a number of MVVM-ready project templates that integrate to Visual Studio 2010.

The basic MVVM project templates include base classes such as ModelBase.cs and ViewModelBase.cs as well as several providers to work with dialog boxes in ClientUI. Some advanced MVVM business templates include more comprehensive base classes such as ValidationViewModelBase.cs and EntityValidationViewModelBase.cs to provide built-in error validation that integrates with data annotations in the domain service.

The following illustration shows the installed ClientUI project templates.

For more information about the available project templates in ClientUI, see Introduction to ClientUI Project Templates.

MVVM Examples

This section provides the code examples of using MVVM pattern in several common scenarios.

Before you can write code that leverage MVVM pattern, it is best practice to create several base classes implementing the interfaces and members that are commonly used in the MVVM classes.

ModelBase

The following example shows the base class for a simple Model class.

C#
Copy Code
using System.Collections.Generic;
using System.ComponentModel;

namespace Intersoft.ClientUI.Framework.ViewModels
{
    public class ModelBase : INotifyPropertyChanged, IDataErrorInfo
    {
        #region Properties

        private Dictionary&t;string, string> _errors = new Dictionary<string, string>();

        public virtual bool HasErrors
        {
            get
            {
                return _errors.Count > 0;
            }
        }

        #endregion

        #region Public

        public void SetError(string propertyName, string errorMessage)
        {
            _errors[propertyName] = errorMessage;
            this.OnPropertyChanged(propertyName);
        }

        #endregion

        #region Protected

        protected void OnPropertyChanged(string propertyName)
        {
            if (this.PropertyChanged != null)
            {
                this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
            }
        }

        protected void ClearError(string propertyName)
        {
            this._errors.Remove(propertyName);
        }

        protected void ClearAllErrors()
        {
            List<string> properties = new List<string>();

            foreach (KeyValuePair<string, string> error in this._errors)
                properties.Add(error.Key);

            this._errors.Clear();

            foreach (string property in properties)
                this.OnPropertyChanged(property);
        }

        #endregion

        #region INotifyPropertyChanged Members

        public event PropertyChangedEventHandler PropertyChanged;

        #endregion

        #region IDataErrorInfo Members

        public string Error
        {
            get { return null; }
        }

        public string this[string columnName]
        {
            get
            {
                if (this._errors.ContainsKey(columnName))
                {
                    return this._errors[columnName];
                }
                return string.Empty;
            }
        }

        #endregion
    }
}

As shown in the above code, the ModelBase class generally implements two interfaces, the INotifyPropertyChanged and IDataErrorInfo. This implementation has been updated to use the new interfaces available in Silverlight 4 and WPF 4. The INotifyPropertyChanged is one of the fundamental interfaces in Silverlight/WPF that enables the binding engine to perform two-ways data binding, that is, changes in the model can be propagated to the target that bound to that particular model. The IDataErrorInfo provides built-in error validation that generally notifies an error to the binding engine.

ViewModelBase

The following example shows the base class for a simple ViewModel class. 

C#
Copy Code
using System.ComponentModel;

namespace Intersoft.ClientUI.Framework.ViewModels
{
    public abstract class ViewModelBase : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;

        protected void OnPropertyChanged(string propertyName)
        {
            PropertyChangedEventHandler handler = PropertyChanged;

            if (handler != null)
            {
                handler(this, new PropertyChangedEventArgs(propertyName));
            }
        }
    }
}

The ViewModelBase class is straightforward where it implements a single INotifyPropertyChanged interface. This simple base class for ViewModel is already sufficient to leverage the MVVM pattern which are explained in the following examples.

BookModel, BookViewModel and BooksViewModel

Once the base classes are created as described in the previous sections, you can start to create the MVVM classes for your scenario. This example uses book scenario where MVVM pattern will be used to load the books data source from xml and show the selected book to the view.

The following code shows the complete listing for the BookModel.

C#
Copy Code
using System;
using System.Xml.Linq;

namespace Intersoft.ClientUI.Samples.Assets.Models
{
    public class Book : ModelBase
    {
        #region Constructors

        public Book()
        {
        }

        public Book(XElement x)
            : this()
        {
            this._author = x.Element("Author").Value.Trim();
            this._title = x.Element("Title").Value.Trim();
            this._category = x.Element("Category").Value.Trim();
            this._ID = x.Element("ID").Value.Trim();
            this._price = double.Parse(x.Element("Price").Value.Trim());
            this._image = new Uri("/Intersoft.ClientUI.Samples.Assets;component/Images/Books/" + 
                              x.Element("Image").Value.Trim() , UriKind.RelativeOrAbsolute);
        }

        #endregion

        #region Fields

        private Uri _image = null;
        private string _author;
        private string _title;
        private string _category;
        private string _ID;
        private double _price;

        #endregion

        #region Properties

        public bool EnableValidation { get; set; }

        public string Author
        {
            get
            {
                return _author;
            }
            set
            {
                if (_author != value)
                {
                    _author = value;
                    ClearError("Author");
                    OnPropertyChanged("Author");
                }
            }
        }

        public string Title
        {
            get
            {
                return _title;
            }
            set
            {
                if (_title != value)
                {
                    _title = value;
                    ClearError("Title");

                    if (EnableValidation && string.IsNullOrEmpty(this.Title))
                        SetError("Title", "Title is required");

                    OnPropertyChanged("Title");
                }
            }
        }

        public string Category
        {
            get
            {
                return _category;
            }
            set
            {
                if (_category != value)
                {
                    _category = value;
                    OnPropertyChanged("Category");
                }
            }
        }

        public string ID
        {
            get
            {
                return _ID;
            }
            set
            {
                if (_ID != value)
                {
                    _ID = value;
                    OnPropertyChanged("ID");
                }
            }
        }

        public double Price
        {
            get
            {
                return _price;
            }
            set
            {
                if (_price != value)
                {
                    _price = value;
                    ClearError("Price");

                    if (EnableValidation && (double.IsNaN(this.Price) || this.Price == 0.0))
                        SetError("Price", "Price is required");

                    OnPropertyChanged("Price");
                }
            }
        }

        public Uri Image
        {
            get
            {
                return _image;
            }
            set
            {
                if (_image != value)
                {
                    _image = value;
                    OnPropertyChanged("Image");
                }
            }
        }

        #endregion
    }
}

The following code shows the complete listing for the BookViewModel. The BookViewModel describes the view for a book and also exposes several properties that can be useful for binding in the View layer.

C#
Copy Code
using Intersoft.ClientUI.Samples.Assets.Models;
using System;

namespace Intersoft.ClientUI.Samples.Assets.ViewModels
{
    public class BookViewModel : ViewModelBase
    {
        // Fields
        private Book _book;

        // Constructor
        public BookViewModel()
        {
        }

        public BookViewModel(Book book)
        {
            _book = book;
        }

        // Views
        public Book Book
        {
            get { return this._book; }
            set
            {
                if (this._book != value)
                {
                    this._book = value;
                    OnPropertyChanged("Book");
                }
            }
        }

        public string Title
        {
            get { return this.Book.Title; }
        }

        public string ShortTitle
        {
            get
            {
                if (this.Book.Title.Length < 80)
                    return this.Title;
                else
                    return this.Book.Title.Substring(0, 80) + "...";
            }
        }

        public Uri NavigateUri
        {
            get { return new Uri("/Books/" + this.Book.ID, UriKind.RelativeOrAbsolute); }
        }
    }
}

The following example shows a simple code listing for the BooksViewModel. Notice the the BooksViewModel is derived from ViewModelBase class. The simplest view model for showing a list of items often contain a property with ObservableCollection<T> type to represent the collection of the items.

C#
Copy Code
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using Intersoft.ClientUI.Samples.Assets.Models;

namespace Intersoft.ClientUI.Samples.Assets.ViewModels
{
    public class BooksViewModel : ViewModelBase
    {
        // Views        
        public ObservableCollection<BookViewModel> Books { get; set; }

        public BooksViewModel()
        {
        }
    }
}

Selected Book

One of the most common scenarios in MVVM pattern is to have a reference to the selected item in the view. This basic state management enables the user interaction logic to be written entirely in the ViewModel which is useful to perform validation and maintaining the state of other properties in the ViewModel. For an instance, you may want to set the Save command's CanExecute to false when there are no books selected in the View.

The following example shows how to add a selection property to the BookViewModel.

C#
Copy Code
// Fields
private BookViewModel _selectedItem = null;

// Selection, View States
public BookViewModel SelectedItem
{
    get { return _selectedItem; }
    set
    {
        if (_selectedItem != value)
        {
            _selectedItem = value;
            OnPropertyChanged("SelectedItem");
            InvalidateCommands();
        }
    }
}

Loading Books from XML Datasource

In the MVVM pattern, the data loading process is typically performed in the ViewModel layer rather than in the Model layer. Alternatively, the data loading process can also be performed in a Controller layer that statically maintains the data operation. In most cases, it is generally recommended to load data in the ViewModel layer such as described in this section.

The following example shows how to load the book list from an XML datasource embedded in the project. A new Book model is created for each book data and then added to the collection in the ViewModel class. 

C#
Copy Code
private void LoadBooks()
{
    // loads book data from xml file
    StreamResourceInfo resource = System.Windows.Application.GetResourceStream(
            new Uri("Intersoft.ClientUI.Samples.Assets;component/Data/BookDataSource.xml", UriKind.Relative));

    XDocument doc = XDocument.Load(resource.Stream);

    var books = from x in doc.Descendants("Book")
                   select new Book(x);

    this.Books = new ObservableCollection<BookViewModel>();

    foreach (Book book in books)
    {
        BookViewModel vm = new BookViewModel(book);
        this.Books.Add(vm);
    }

    resource.Stream.Close();
}

Buttons and Commanding

Finally, a basic component of an application is to have buttons that execute certain logics based on the selection and input fields.

The following example shows how to add a ShowDetails command to the BooksViewModel and write the event handlers to implement the delegate for the command.

C#
Copy Code
// Commands
public DelegateCommand ShowDetailsCommand
{
    get; set;
}

public BooksViewModel()
    : base()
{
    // Creates the ShowDetails delegate command
    ShowDetailsCommand = new DelegateCommand(ExecuteShowDetails, CanShowDetails);
}

private bool CanShowDetails(object parameter)
{
    // The ShowDetails button will be disabled 
    // if this function returns false

    return this.SelectedItem != null;
}

private void ExecuteShowDetails(object parameter)
{
    // write logics that show the book details
}

Instantiating the BooksViewModel

Once the BooksViewModel is ready, you create a new instance of the BooksViewModel and bind it to the UI elements within your View.

The following example shows how to instantiate the BooksViewModel and bind it to the UI element to display the book data.

XAML
Copy Code
<Intersoft:UXPage 
        ...
        xmlns:Intersoft="http://intersoft.clientui.com/schemas"
        xmlns:ViewModels="clr-namespace:Intersoft.ClientUI.Samples.Assets.ViewModels"
        x:Class="Intersoft.ClientUI.Samples.Views.Default" 
        Title="Default Page"
        d:DesignWidth="1024" d:DesignHeight="800">
        <Intersoft:UXPage.Resources>

            <ViewModels:BooksViewModel x:Key="BooksViewModel"/>
        
        </Intersoft:UXPage.Resources>

    <Intersoft:DockPanel x:Name="LayoutRoot" FillChildMode="Custom" DataContext="{StaticResource BooksViewModel}">

        <Intersoft:UXListBox Intersoft:DockPanel.IsFillElement="True"
                             ItemsSource="{Binding Path=Books}" 
                             SelectedItem="{Binding Path=SelectedItem, Mode=TwoWay}">
            <Intersoft:UXListBox.ItemTemplate>
                <DataTemplate>
                    <Intersoft:StylishLabel ContentType="ContentAndImage" 
                                            ImageSource="{Binding Path=Book.Image}">
                        <Intersoft:EllipsisText Text="{Binding Path=Book.Title}"/>
                    </Intersoft:StylishLabel>
                </DataTemplate>
            </Intersoft:UXListBox.ItemTemplate>
        </Intersoft:UXListBox>

        <Intersoft:UXCommandBar Intersoft:DockPanel.Dock="Bottom">
            <Intersoft:UXButton Content="Show Details" 
                                Command="{Binding Path=ShowDetailsCommand}"/>
        </Intersoft:UXCommandBar>

    </Intersoft:DockPanel>
</Intersoft:UXPage>

Several points to note in the above example are:

To learn more about advanced MVVM topics, see Advanced MVVM

Step-by-step Walkthroughs

To get started using MVVM pattern in your project, see the walkthroughs in the list below.

For more walkthroughs on implementing MVVM pattern, see MVVM Pattern Walkthroughs.

See Also

Other Resources