Logo
HOW TO
Windows XP
Windows Vista
Windows 7
Windows Azure
Windows Server
Windows Phone
 
 
Windows Phone

Windows Phone 7 : Implementing MVVM on Windows Phone by Using MVVMLight

7/19/2012 6:21:55 PM

1. Problem

You want your application implementation to have a consistent separation of concerns between modules. You want the view to have only the task of displaying information, and to remove from the view everything that requires some logic or data access. You need to create a view with only a set of controls (such as TextBlock, TextBox, Button,...) binding them with the DataContext (ViewModel) of the view. Then the view it's not responsible of data-retrieving, but it's a task reserved to the ViewModel that will aggregate data if necessary, and put it in properties, bindable by the view.

2. Solution

You must implement the Model-View-ViewModel (MVVM) pattern, and because you don't want to reinvent the wheel, you'll also use the MVVMLight framework, which is available on CodePlex (http://mvvmlight.codeplex.com). MVVMLight provides some important classes and "gears" necessary to implement MVVM on Silverlight for Windows Phone, such as EventToCommand and MessageBroker (a.k.a. Messenger).

3. How It Works

MVVM (based on a specialization of Martin Fowler's Presentation Model) is a design pattern used to realize the separation of concerns. Elements of the MVVM pattern are as follows:

  • View is responsible for showing information, as in other MVx patterns (where x could be C = controller, P = presenter, and so forth) to display controls (for example: buttons and text boxes).

  • ViewModel is responsible for preparing data taken from the model for the visualization.

  • Model represents the model of your application (that is, your data source).

In order to implement MVVM, you need to understand some basic concepts such as commands and binding. Then you'll learn about the basics of MVVM and after that, you will see unit testing. and their importance within your application life cycle.

Let's start with binding. A binding is simply an association between two properties of two different classes. You can bind the Value of a Slider with the Text property of a TextBox, or in the case of the MVVM implementation, you can bind the value of a property in our ViewModel with the property of a control on the view. Typically, you will use properties with the same or similar types. There are different options for binding:

  • Path

  • Mode

  • UpdateTrigger

  • BindsDirectlyToSource

  • Converter

Path represents the path to the binding source property. For example, if your binding source is a product and you are binding TextBox.Text to show the category name, your path will be Path=Category.Description.

In Silverlight for Windows Phone, as in Silverlight for the Web, the Mode attribute can assume three values: OneWay, OneTime, and TwoWay. By default, the binding Mode is set to OneWay. This means that when you update the source of the binding, the target is updated. As you might guess from its name, TwoWay mode means that updating the target value will reflect on the source value, and vice versa. Finally, when you use OneTime, the target will be updated only the first time, when you create the binding.

UpdateTrigger in Silverlight for Windows Phone, as in its cousin for the Web, accepts only two values: Default and Explicit. With Explicit, you must indicate to the binding engine to update the source. Default mode updates the value when a lost focus (of the control) occurs. 

BindsDirectlyToSource, when set to false, causes Path to be evaluated relative to the data item; otherwise, Path is evaluated relative to the data source.

Last but not least is the concept of a converter. With a converter, you can bind properties of different types, defining the way in which the view must evaluate the value. For example, sometimes you will need to bind a Boolean property to the visibility of a control. As you know, visibility is an enumeration, so when you try to apply a Boolean, nothing happens. The solution? A converter! You will see this in the following "The Code" section.

Now you need to understand what a command is. The concept of a command comes from the command pattern, which allows you to isolate the part of code that executes an operation from the part that requests the execution. Inside the code of our ViewModel, a command is a class that implements the ICommand interface, which represents the contract to implement commanding. As you can see in the class diagram shown in Figure 1, ICommand exposes the methods CanExecute and Execute, and the event CanExecuteChanged. As you can imagine, CanExecute is the method that enables the control bound with the command to be enabled, and Execute is the method that describes what you want to do with that command. Unfortunately, in this version of Silverlight for Windows Phone, even if the ICommand interface is available, the command cannot be bound with controls without using EventToCommand, because the controls don't expose the Command property. However, we are confident that this capability will be introduced in future versions. It's important to remember that when you bind a command to a control through the Command={Binding MyCommand} syntax, where MyCommand is your command exposed by your ViewModel, this command refers to the default event of the control (for example, the Click event of a button).

Figure 1. Class diagram for ICommand

4. The Code

Before you begin to create a new project, download the libraries indicated in the preceding "How it Works" section. Of course, if you want to get the most from MVVMLight, you should also install the templates for Visual Studio 2010 (Express or not, depending on your version) that the package contains.

To begin, create a new Windows Phone application, and add a reference to the MVVMLight DLLs and to the Coding4Fun libraries, as shown in Figure 2.

Figure 2. Adding the MVVM light and Coding4Fun DLLs to your project

Your application is now ready for a quick implementation of the pattern, and to use a workaround, notorious among people who work with Silverlight. This workaround helps you to update the source of the binding, every times a change accours inside a textbox, then will be raised PropertyChanged event every time you write something in a text box, because as you know the default UpdateSourceTrigger, set the value of the source property only when the control has lost the focus.

To do it you need to import the Coding4Fun.Phone.Controls.Binding namespace in this way

xmlns:local="clr-namespace:Coding4Fun.Phone.Controls.Binding;
              assembly=Coding4Fun.Phone.Controls"

Then the only thing that you need to do now it's to set inside the TextBox the attached property TextBoxBinding .UpdateSourceOnChange to True in this way:

<TextBox Text="{Binding Text}"local:TextBoxBinding.UpdateSourceOnChange="True" />

					  

After the explanation of the workaround we are ready to continue the implementation of the pattern. The first step is to add the ViewModelLocator of MVVMLight to your application. Then you must create the folder ViewModels and add an item of type ViewModelLocator, calling it ViewModelsLocator.

Immediately after this, you must edit your App.xaml by adding the namespace of ViewModelsLocator to it, and creating a resource of type ViewModelsLocator, at shown here:

<Application
    x:Class="Wp7Recipe_10_2_MVVM.App"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:phone="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone"
    xmlns:shell="clr-namespace:Microsoft.Phone.Shell;assembly=Microsoft.Phone"
    xmlns:vm="clr-namespace:Wp7Recipe_10_2_MVVM.ViewModels">

    <!--Application Resources-->
    <Application.Resources>
        <vm:ViewModelsLocator xmlns:vm="clr-namespace:Wp7Recipe_10_1_MVVM.ViewModels"
                                   x:Key="Locator" />
    </Application.Resources>
    ...
</Application>

					  

Now that you have a way of accessing your ViewModels, you can start creating them. You'll begin with the ViewModel for the main page.

A naming convention for Views and ViewModels is important. If you have a view named CategoriesView, you could name the relative ViewModel CategoriesViewModel. Although you do not have to always have a 1:1 correspondence of Views and ViewModels, perhaps there is some case where you will have only one ViewModel for CRUD (Create, Read, Update, and Delete) operations for all your typological entities.


As before, add a new item to your ViewModels folder, of type ViewModel, and call it MainPageViewModel. As you can see in the constructor, this will ready a piece of code that has been commented out:

////if (IsInDesignMode)
////{
////    // Code runs in Blend --> create design time data.
////}
////else
////{
////    // Code runs "for real": Connect to service, etc...
////}

This is the first important feature to consider, because by using this code, you will have the ViewModel available at design time, and this is significant when you want to test your binding inside DataTemplate. In addition, you must specify in ViewModelsLocator a property of type MainPageViewModel, and bind it to the data context of the view (MainPage, in this case).

#region MainPageViewModel
private static MainPageViewModel _mainPageViewModel;

  /// <summary>
  /// Gets the MainPageViewModel property.
  /// </summary>
  public static MainPageViewModel MainPageViewModelStatic
  {
    get
    {
      if (_mainPageViewModel == null)
      {
        CreateMainPageViewModel();
      }
      return _mainPageViewModel;
    }
  }

  /// <summary>
  /// Gets the MainPageViewModel property.
  /// </summary>
  [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance",
  "CA1822:MarkMembersAsStatic",
  Justification = "This non-static member is needed for data binding purposes.")]
  public MainPageViewModel MainPageViewModel
  {
    get
    {
      return MainPageViewModelStatic;
    }
  }

					  

/// <summary>
  /// Provides a deterministic way to delete the MainPageViewModel property.
  /// </summary>
  public static void ClearMainPageViewModel()
  {
    _mainPageViewModel.Cleanup();
    _mainPageViewModel = null;
  }

  /// <summary>
  /// Provides a deterministic way to create the MainPageViewModel property.
  /// </summary>
  public static void CreateMainPageViewModel()
  {
    if (_mainPageViewModel == null)
    {
      _mainPageViewModel = new MainPageViewModel();
    }
  }

  /// <summary>
  /// Cleans up all the resources.
  /// </summary>
  public static void Cleanup()
  {
    ClearMainPageViewModel();
  }
  #endregion

					  

To quickly write these parts of the code, you must install snippets that are available inside the MVVMLight package that you download from CodePlex. The mvvmlocatorproperty snippet creates a property of ViewModel inside your ViewModelLocator. Another really helpful snippet is mvvminpc, which rapidly creates a bindable property in your ViewModel that raises PropertyChangedEvent.


Now you have the most important step to do: binding the data context of the view to your ViewModel. In this way, all properties exposed by the ViewModel will be available for binding controls inside the view. You do this with a simple binding, as you can see in the following code:

<phone:PhoneApplicationPage
    x:Class="Wp7Recipe_10_2_MVVM.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:phone="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone"
    xmlns:shell="clr-namespace:Microsoft.Phone.Shell;assembly=Microsoft.Phone"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"

					  

mc:Ignorable="d" d:DesignWidth="480" d:DesignHeight="768"
    FontFamily="{StaticResource PhoneFontFamilyNormal}"
    FontSize="{StaticResource PhoneFontSizeNormal}"
    Foreground="{StaticResource PhoneForegroundBrush}"
    SupportedOrientations="Portrait" Orientation="Portrait"
    shell:SystemTray.IsVisible="True"
    DataContext="{Binding Source={StaticResource Locator}, Path=MainPageViewModel}">
...

					  

In this way, you have the view (MainPage) data context bound to the property MainPageViewModel of the Locator resource that you have defined in App.xaml.

These are all the preliminary steps to start with MVVM, but it's clear that you must add properties at your ViewModel to bind them to the controls on the user interface, and you should start with simply the strings ApplicationName and PageName.

...
        /// <summary>
        /// The name of our application
        /// </summary>
        public string ApplicationName { get { return "Learning MVVM"; } }

        /// <summary>
        /// The name of the page
        /// </summary>
        public string PageName { get { return "Main Page"; } }
        ...

Binding is important in implementing MVVM, and so you bind the properties to the TextBlocks of the view, called ApplicationTitle and PageTitle:

<StackPanel x:Name="TitlePanel" Grid.Row="0" Margin="12,17,0,28">
  <TextBlock x:Name="ApplicationTitle" Text="{Binding Path=ApplicationName}"
             Style="{StaticResource PhoneTextNormalStyle}"/>
  <TextBlock x:Name="PageTitle" Text="{Binding Path=PageName}" Margin="9,-7,0,0"
             Style="{StaticResource PhoneTextTitle1Style}"/>
</StackPanel>

					  

At this point, if you use Microsoft Blend, you can see another point of strange of MVVM Light that supports the "Blendability" then you will see the ApplicationTitle TextBlock Text property valued with text of property ApplicationName of the ViewModel (Figure 3) and PageTitle set with PageName.

All of this introduces you to the concept of MVVM, but you want to use it in a real case—for example, with an application that helps the user to keep track of expenses. Then in MainPage, you would have a form for inserting data, with fields such as Date, Amount, and Motivation, and a button to bind to a command. Unfortunately, buttons in Windows Phone 7 don't support "clean" commands (for example, Command={Binding SaveCommand}) in this release.

Figure 3. MainPageViewModel supports blendability

At this point, your view XAML will be something like this:

...
<Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
  <Grid.ColumnDefinitions>
    <ColumnDefinition Width="0.421*"/>
     <ColumnDefinition Width="0.579*"/>
  </Grid.ColumnDefinitions>
  <Grid.RowDefinitions>
    <RowDefinition Height="80"/>
    <RowDefinition Height="80"/>
    <RowDefinition Height="80"/>
    <RowDefinition Height="80"/>
    <RowDefinition/>
  </Grid.RowDefinitions>

<TextBlock Margin="0" TextWrapping="Wrap" Text="Date" TextAlignment="Center"
            VerticalAlignment="Center"/>
  <TextBlock Margin="0" Grid.Row="1" TextWrapping="Wrap" Text="Amount"
            TextAlignment="Center" VerticalAlignment="Center"/>
  <TextBlock Margin="0" Grid.Row="2" TextWrapping="Wrap" Text="Motivation"
            TextAlignment="Center" VerticalAlignment="Center"/>
  <TextBox Margin="0" Grid.Column="1" Grid.Row="1" TextWrapping="Wrap"
           TextAlignment="Center" VerticalAlignment="Center"
           Text="{Binding Path=Amount, Mode=TwoWay}"
           local:TextBoxBinding.UpdateSourceOnChange="True" />
  <TextBox Margin="0" Grid.Column="1" Grid.Row="2" TextWrapping="Wrap"
           TextAlignment="Center" VerticalAlignment="Center"
           Text="{Binding Path=Motivation, Mode=TwoWay}"
           local:TextBoxBinding.UpdateSourceOnChange="True"/>

  <toolkit:DatePicker Grid.Column="1" VerticalAlignment="Bottom" Height="77"
                      HorizontalContentAlignment="Center"
                      Value="{Binding Path=Date, Mode=TwoWay}"
                      local:TextBoxBinding.UpdateSourceOnChange="True" />

  <Button Content="Save It" Grid.Column="0" HorizontalAlignment="Center" Grid.Row="3"
          Width="152" Grid.ColumnSpan="2" >
    <i:Interaction.Triggers>
      <i:EventTrigger EventName="Click">
        <cmd:EventToCommand Command="{Binding SaveCommand}"/>
      </i:EventTrigger>
   </i:Interaction.Triggers>
</Button>
</Grid>
...

					  

As you can see, in this XAML you have two "new" namespaces, cmd and i, which come from the MVVMLight libraries. In this way, you have bound the event Click with a command (inside ViewModel) named SaveCommand demanding to this command the interest to execute the logic of this operation, so when Button.Click raises, SaveCommand.Execute will be called. Of course, to use these namespaces, you must import them in your XAML, in this way:

...
xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
xmlns:cmd="clr-namespace:GalaSoft.MvvmLight.Command;assembly=GalaSoft.MvvmLight.Extras.WP7"
...

					  
As you can see from the XAML, you bound your controls to the properties of ViewModel named Date, Amount, and Motivation. In the same way as before, you can use a snippet (mvvminpc) to create these properties inside your ViewModel. The advantage of using this snippet is that you don't need to add any call to the RaisePropertyChanged method that raises the PropertyChanged event for subscribed handlers (in this case by binding engine).

NOTE

The RaisePropertyChanged method call raises the PropertyChanged event in ViewModelBase (a class inside of the MVVMLight toolkit) that derives from the interface INotifyPropertyChanged. The binding engine will handle the event, looking for updated properties, and informing the source of the binding that something has changed.

As you can imagine, the ViewModel has become a little longer because you have to bind more properties and a command:

...
    public class MainPageViewModel : ViewModelBase
    {
        /// <summary>
        /// The name of our application
        /// </summary>
        public string ApplicationName { get { return "Learning MVVM"; } }

        /// <summary>
        /// The name of the page
        /// </summary>
        public string PageName { get { return "Main Page"; } }

        public GalaSoft.MvvmLight.Command.RelayCommand SaveCommand { get; set; }

        #region DateProperty

        /// <summary>
        /// The <see cref="Date" /> property's name.
        /// </summary>
        public const string DatePropertyName = "Date";

        private DateTime _date = DateTime.Now.AddDays(-4);

        public DateTime Date
        {
            get
            {
                return _date;
            }

            set
            {
                if (_date == value)
                {
                    return;
                }

					  

var oldValue = _date;
                _date = value;

                // Update bindings, no broadcast
                RaisePropertyChanged(DatePropertyName);
            }
        }
        #endregion

        #region Amount Property
        /// <summary>
        /// The <see cref="Amount" /> property's name.
        /// </summary>
        public const string AmountPropertyName = "Amount";

        private decimal _amount = 0;

        public decimal  Amount
        {
            get
            {
                return _amount;
            }

            set
            {
                if (_amount == value)
                {
                    return;
                }

                var oldValue = _amount;
                _amount = value;

                // Update bindings, no broadcast
                RaisePropertyChanged(AmountPropertyName);

            }
        }
        #endregion

        #region Motivation Property

        /// <summary>
        /// The <see cref="Motivation" /> property's name.
        /// </summary>
        public const string MotivationPropertyName = "Motivation";

        private string _motivation = string.Empty;

        public string Motivation
        {

					  

get
            {
                return _motivation;
            }

            set
            {
                if (_motivation == value)
                {
                    return;
                }

                var oldValue = _motivation;
                _motivation = value;

                // Update bindings, no broadcast
                RaisePropertyChanged(MotivationPropertyName);

            }
        }

        #endregion

        /// <summary>
        /// Initializes a new instance of the MainPageViewModel class.
        /// </summary>
        public MainPageViewModel()
        {
            SaveCommand = new GalaSoft.MvvmLight.Command.RelayCommand(SaveCommandExecute);
        }

        private void SaveCommandExecute()
        {
            //put your logic to save here
        }
    }
}

					  

5. Usage

This application can run in the emulator or the physical device. This recipe is a good start point for a real financial tracking application, all you need to do is to add your preferred logic for saving data  and start the application. Make sure that you have decoupled the view from the related ViewModel, so that the only interest of the view is to know how to show data (ViewModel is view-ignorant). In this way, if you change your logic for saving information, nothing must be done on your view. Furthermore, if you want to test your ViewModel, you can do it with unit tests without problems—as opposed to the events approach, that requires that an event be raised

Other -----------------
- Windows Phone 7 : In the Cloud - Creating a Feed Reader
- Windows Phone 7 : In the Cloud - Interacting with WCF
- Windows Phone 7 : Isolated Storage - Saving a Photo in Isolated Storage (part 2)
- Windows Phone 7 : Isolated Storage - Saving a Photo in Isolated Storage (part 1)
- Windows Phone 7 : Isolated Storage - Modifying Settings of Your Application
- Windows Phone 7 : Isolated Storage - Saving Serialized Data
- Windows Phone 7 : Saving a File in Isolated Storage and Loading It
- Windows Phone 7 : Media Management - Adding Integration with the Music-Videos Hub
- Windows Phone 7 : Sounding Out with Game Audio - Playing Music
- Windows Phone 7 : Playing Sound Effects
 
 
REVIEW
- First look: Apple Watch

- 10 Amazing Tools You Should Be Using with Dropbox
 
VIDEO TUTORIAL
- How to create your first Swimlane Diagram or Cross-Functional Flowchart Diagram by using Microsoft Visio 2010 (Part 1)

- How to create your first Swimlane Diagram or Cross-Functional Flowchart Diagram by using Microsoft Visio 2010 (Part 2)

- How to create your first Swimlane Diagram or Cross-Functional Flowchart Diagram by using Microsoft Visio 2010 (Part 3)
 
Popular tags
Microsoft Access Microsoft Excel Microsoft OneNote Microsoft PowerPoint Microsoft Project Microsoft Visio Microsoft Word Active Directory Biztalk Exchange Server Microsoft LynC Server Microsoft Dynamic Sharepoint Sql Server Windows Server 2008 Windows Server 2012 Windows 7 Windows 8 Adobe Indesign Adobe Flash Professional Dreamweaver Adobe Illustrator Adobe After Effects Adobe Photoshop Adobe Fireworks Adobe Flash Catalyst Corel Painter X CorelDRAW X5 CorelDraw 10 QuarkXPress 8 windows Phone 7 windows Phone 8 BlackBerry Android Ipad Iphone iOS
Popular keywords
HOW TO Swimlane in Visio Visio sort key Pen and Touch Creating groups in Windows Server Raid in Windows Server Exchange 2010 maintenance Exchange server mail enabled groups Debugging Tools Collaborating
Top 10
- Microsoft Excel : How to Use the VLookUp Function
- Fix and Tweak Graphics and Video (part 3) : How to Fix : My Screen Is Sluggish - Adjust Hardware Acceleration
- Fix and Tweak Graphics and Video (part 2) : How to Fix : Text on My Screen Is Too Small
- Fix and Tweak Graphics and Video (part 1) : How to Fix : Adjust the Resolution
- Windows Phone 8 Apps : Camera (part 4) - Adjusting Video Settings, Using the Video Light
- Windows Phone 8 Apps : Camera (part 3) - Using the Front Camera, Activating Video Mode
- Windows Phone 8 Apps : Camera (part 2) - Controlling the Camera’s Flash, Changing the Camera’s Behavior with Lenses
- Windows Phone 8 Apps : Camera (part 1) - Adjusting Photo Settings
- MDT's Client Wizard : Package Properties
- MDT's Client Wizard : Driver Properties
 
Windows XP
Windows Vista
Windows 7
Windows Azure
Windows Server
Windows Phone
2015 Camaro