Logo
Windows XP
Windows Vista
Windows 7
Windows Azure
Windows Server
Windows Phone
EPL Standings
 
 
Windows Phone

The Model-View-ViewModel Architecture (part 1) - MVVM Overview

10/29/2011 4:37:45 PM
The Model-View-ViewModel (MVVM) architecture originated when the Microsoft Windows Presentation Foundation (WPF) team were building the first version of Expression Blend. WPF is Microsoft's desktop XAML development model, and Expression Blend is written in WPF. MVVM is similar to other separation of concerns architectures, like the tried-and-true Model-View-Controller (MVC) model; however, MVVM is optimized to take advantage of XAML's rich data binding, data templates, commands, and event routing capabilities. The next section covers the architecture in more detail.

1. MVVM Overview

The MVVM pattern is defined to help you grasp how it works with XAML. If you are familiar with MVC, MVVM will look somewhat familiar to you – but it is much more than just MVC. MVVM relies heavily on XAML data binding capabilities to allow the UI to data bind to both data and commands. Figure 1 depicts the MVVM architecture.

Figure 1. The MVVM architecture


The BasicMVVM and the WcfRemoteServicesSimpleRestJSON projects are configured as the startup project. Four folders are added to the project named Model, View, and ViewModel. The sections that follow cover the major components of MVVM in the BasicMVVM sample.

1.1. BasicMVVM - Model

The Model contains the building blocks of the application. It consists of the underlying data objects that are populated via a data access layer. Examples of Model classes are Customer, Store, Product, etc. When you create a class to represent an object in an application, it most likely belongs as part of the Model. The Model sits behind the ViewModel. The View will data bind to lists of or individual objects based on classes in the Model.

To get started, copy over the Vendor class from the WcfRemoteServicesSimpleRestJSON services project to the BasicMVVMModels folder. The class implements the INotifyPropertyChanged interface to support data binding at the class level. The INotifyPropertyChanged interface ensures that changes to the underlying object are propagated to the UI and vice versa. See Listing 1 for the code.

Example 1. Vendor Model Class Code File
using System;
using System.ComponentModel;
using System.Runtime.Serialization;

namespace BasicMVVM.Model
{
//Copied from services project
[DataContract()]
public class Vendor : INotifyPropertyChanged
{
private string AccountNumberField;
private byte CreditRatingField;
private string NameField;

[DataMemberAttribute()]
public string AccountNumber
{


get
{
return this.AccountNumberField;
}
set
{
this.AccountNumberField = value;
NotifyPropertyChanged("AccountNumber");
}
}

[DataMemberAttribute()]
public byte CreditRating
{
get
{
return this.CreditRatingField;
}
set
{
this.CreditRatingField = value;
NotifyPropertyChanged("CreditRating");
}
}

[DataMemberAttribute()]
public string Name
{
get
{
return this.NameField;
}
set
{
this.NameField = value;
NotifyPropertyChanged("Name");
}
}

public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(String propertyName)
{
if (null != PropertyChanged)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
}


1.2. BasicMVVM - ViewModel

Mentioned above is the fact that the View or UI data binds to the ViewModel, suggesting that a ViewModel consists of the data containers for the application, which is correct. Lists of objects defined in the Model are created and managed by the ViewModel. In addition, the ViewModel consists of the majority of application logic as well.

Next create the VendorViewModel class. The VendorViewModel class in the BasicMVVM project supports the following four major features:

  • Vendors specific business logic

  • UI Databinding via INotifyPropertyChanged

  • Design-time support

  • REST+JSON data loading

The Vendor-specific business logic is pretty straightforward. It consists of a read-only collection of Vendor objects from the Model and two event handlers to add and remove Vendor objects from the collection. For a professional application, additional methods and business logic would be present but the implementation is the same.

NOTE

While the VendorViewModel.Vendors collection is read-only – it has just a get property accessor – you can still add and remove Vendor objects in the collection. You just cannot assign a new collection to the property.

It is critical to implement INotifyPropertyChanged for data binding to work. Otherwise, changes are not propagated back to the UI, and vice versa. It is simple enough to do. Add an instance of the PropertyChangedEventHandler class named PropertyChanged and a method that takes a property name as a string and then fires the PropertyChanged event instance.

To detect design-time, the System.ComponentModel.DesignerProperties class has a static bool property named IsInDesignTool that indicates whether the code is running in a design-time tool. The VendorViewModel constructor checks if an instance of the class is running at design-time. If at design-time, the constructor calls LoadSampleData method. Otherwise, at run-time, it calls LoadData, which invokes a remote REST+JSON service.

The last major functionality for the VendorsViewModel class is making the remote service call. The interesting change for this scenario is that the service call and asynchronous callback live in the VendorsViewModel class. The callback cannot have code like the following:

vendorsListbox.Dispatcher.BeginInvoke(...);

The solution is to make the call using this line of code instead:

Deployment.Current.Dispatcher.BeginInvoke(..);

This code ensures that the correct Dispatcher instance is used to notify the UI that data changes occurred. The next challenge is that the callback function needs to update the Vendors collection property. Remember that the Vendors collection is a read-only collection, because we do not want external classes to be able to assign a new collection to it. We want the data to only come from the remote services. The code instead assigns the collection to the underlying _vendors collection private member variable.

The final issue is that the code still needs to notify the UI that data changes occurred, i.e. that the Vendors collection is loaded. Since the _vendors collection is updated directly, NotifyPropertyChanged("Vendors") is called in the anonymous delegate by BeginInvoke. Again, the code could make Vendors read/write and have a set accessor function like this but maintaining data integrity is preferred so the set function is commented out, as in the following:

set
{
_vendors = value;
NotifyPropertyChanged("Vendors");
}

Listing 2 has the full source code for review.

Example 2. VendorViewModel Class Code File
using System;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Linq;
using System.Net;
using System.Runtime.Serialization.Json;
using System.Windows;
using BasicMVVM.Model;

namespace BasicMVVM.ViewModel
{
public class VendorViewModel : INotifyPropertyChanged
{
public VendorViewModel()
{
if (InDesignTime)
{
LoadSampleData();
}
else
{
LoadData();
}
}

#region Design-time support
private bool InDesignTime
{
get
{
return DesignerProperties.IsInDesignTool;
}
}

private void LoadSampleData()
{
_vendors = new ObservableCollection<Vendor>()
{
new Vendor(){AccountNumber="111111", CreditRating=65,
Name="DesignTime - Fabrikam Bikes" },
new Vendor(){AccountNumber="222222", CreditRating=40,
Name="Contoso Sports" },
new Vendor(){AccountNumber="333333", CreditRating=30,
Name="Duwamish Surfing Gear" },
new Vendor(){AccountNumber="444444", CreditRating=65,
Name="Contoso Bikes" },
new Vendor(){AccountNumber="555555", CreditRating=40,
Name="Fabrikam Sports" },
new Vendor(){AccountNumber="666666", CreditRating=30,
Name="Duwamish Golf" },
new Vendor(){AccountNumber="777777", CreditRating=65,
Name="Fabrikam Sun Sports" },
new Vendor(){AccountNumber="888888", CreditRating=40,
Name="Contoso Lacross" },
new Vendor(){AccountNumber="999999", CreditRating=30,
Name="Duwamish Team Sports" },
};
}
#endregion

#region Vendors Data Load
HttpWebRequest httpWebRequest;
private void LoadData()
{
httpWebRequest = HttpWebRequest.CreateHttp("http://localhost:9191

/AdventureWorksRestJSON.svc/Vendors");
httpWebRequest.BeginGetResponse(new AsyncCallback(GetVendors), null);
}

//add a reference to System.Servicemodel.web to get DataContractJsonSerializer

void GetVendors(IAsyncResult result)
{
HttpWebResponse response = httpWebRequest.EndGetResponse(result) as HttpWebResponse;
DataContractJsonSerializer ser = new DataContractJsonSerializer

(typeof(ObservableCollection<Vendor>));
_vendors = ser.ReadObject(response.GetResponseStream()) as ObservableCollection<Vendor>;
//Vendors is read-only so cannot set directly
//Must call NotifyPropertyChanged notifications on UI thread
//to update the UI and have data binding work properly
Deployment.Current.Dispatcher.BeginInvoke(() =>
{
NotifyPropertyChanged("Vendors");
});
}
#endregion

#region Vendors Business Logic
private ObservableCollection<Vendor> _vendors;
public ObservableCollection<Vendor> Vendors
{
get
{
return _vendors;
}
//set
//{
// _vendors = value;
// NotifyPropertyChanged("Vendors");
//}
}

public Vendor GetVendorByAccountNumber(string accountNumber)
{
var vendor = from v in _vendors
where v.AccountNumber == accountNumber
select v;

return vendor.First<Vendor>();
}

public void AddVendor()
{
Vendors.Add(new Vendor()
{
AccountNumber = "111111",
CreditRating = 65,
Name = "Fabrikam Bikes - Added"
});
}

public void RemoveVendor(object vendor)
{
if (null != vendor)
Vendors.Remove((Vendor)vendor);


}
#endregion

#region INotifyPropertyChanged interface members
public event PropertyChangedEventHandler PropertyChanged;

public void NotifyPropertyChanged(String property)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(property));
}
}
#endregion
}
}

The next section covers how to make the Model and ViewModel objects available to the UI.

1.3. BasicMVVM - View

The View is the actual XAML of an application. It is the mainpage.xaml file in a Silverlight project, and is what the user interacts with directly, presenting the underlying data and application logic. The View data binds to the ViewModel, which is covered in the previous section. The goal when building the view is to not have any code in the code-behind for the .xaml file, if possible. This means that all logic is in the ViewModel, which is non-visual, making it much more unit-testable. The other advantage of the separation of concerns here is that the design-team can focus on building out the View without interfering with business logic in event handlers. A View always has a reference to the ViewModel, because it data binds to it.

Remove the MainPage.xaml from the BasicMVVM project and add a new View (.xaml page) to the Views folder named VendorsView.xaml. Next, edit the WMAppManifest.xml file by changing the NavigationPage attribute to point to the new default task, as in the following:

<DefaultTask  Name ="_default" NavigationPage="Views/CustomersView.xaml"/>.

NOTE

In general, the WMAppManifest.xml file should not be manually edited, but in this case it is required.

In Expression Blend, add a ListBox to VendorsView.xaml and configure the ItemsSource to data bind to the VendorViewModel.Vendors collection by clicking the Advanced Options button next to the ItemsSource property in the Expression Blend Properties window and selecting Data Binding... to bring up the Create Data Binding dialog. Click the +CLR Object button, select VendorViewModel, and then click OK.

If the VendorViewModel class – or any .NET CLR class that you want to data bind – does not show up in the dialog box, make sure to compile the application. Static collections will not show either.


This generates a new Data Source named VendorViewModelDataSource in the left pane. Select Vendors in the right pane and then click OK. This configuration updates the XAML in three places. It adds a new resource to the VendorsView page, as in the following:

<phone:PhoneApplicationPage.Resources>
<BasicMVVM_ViewModels:VendorViewModel
x:Key="VendorViewModelDataSource" d:IsDataSource="True"/>
</phone:PhoneApplicationPage.Resources>

It configures LayoutRootGrid's DataContext property to point to the VendorViewModel class:

DataContext="{Binding Source={StaticResource VendorViewModelDataSource}}"

Finally, the work in Expression Blend configures the vendorsListBoxItemsSource property to data bind to the VendorViewModel.Vendors collection like so ItemsSource="{Binding Vendors}."

One of the goals of MVVM and separating concerns is to make the View as "thin" as possible. WPF and Silverlight 4 have support for separating concerns by allowing UI element events like Click to data bind to methods on the ViewModel via Commanding and the ICommand interface. This means that instead of having event handlers in the code-behind for the view, everything is instead configured via data binding in XAML. Figure 2 shows the UI.

Figure 2. BasicMVVM running in the emulator

Silverlight for Windows Phone 7, which is based on Silverlight 3 plus some additional Silverlight 4 features like the WebBrowser control and offline DRM, does not have full support for Commanding. For the BasicMVVM sample, the VendorsView has two code-behind events to support adding and removing a Vendor, as in the following:

private void insertVendorAppBarBtn_Click(object sender, EventArgs e)
{
VendorViewModel vm = LayoutRoot.DataContext as VendorViewModel;
vm.AddVendor();
}

private void RemoveVendorAppBarBtn_Click(object sender, EventArgs e)
{
VendorViewModel vm = LayoutRoot.DataContext as VendorViewModel;
vm.RemoveVendor(vendorsListBox.SelectedItem);
}

Notice that the path to the underlying ViewModel is still via the instance of the VendorViewModel class that is data bound to the LayoutRootGrid's DataContext property. It would be great to be able to avoid this type of code. Luckily, there are third-party, open-source frameworks that provide extensions to Silverlight that enable better support for MVVM.

Top Search -----------------
- Windows Server 2008 R2 : Work with RAID Volumes - Understand RAID Levels & Implement RAID
- Windows Server 2008 R2 Administration : Managing Printers with the Print Management Console
- Configuring Email Settings in Windows Small Business Server 2011
- Windows Server 2008 R2 : Configuring Folder Security, Access, and Replication - Implement Permissions
- Monitoring Exchange Server 2010 : Monitoring Mail Flow
- Windows Server 2008 R2 :Task Scheduler
- Windows Server 2008 R2 : File Server Resource Manager
- Windows Server 2008 R2 : Installing DFS
- Exchange Server 2010 : Managing Anti-Spam and Antivirus Countermeasures
- Windows Server 2008 R2 : Configuring Folder Security, Access, and Replication - Share Folders
Other -----------------
- Developing for Windows Phone and Xbox Live : Graphics Performance
- Developing for Windows Phone and Xbox Live : General Performance
- Windows Phone 7 : Media Management - Taking a Photo from Your Phone Camera
- Windows Phone 7 : Sensors - Indicating the User's Position via Coordinates
- Developing for Windows Phone and Xbox Live : Custom Avatar Animations (part 3)
- Developing for Windows Phone and Xbox Live : Custom Avatar Animations (part 2) - Creating the Content Processor
- Developing for Windows Phone and Xbox Live : Custom Avatar Animations (part 1) - Building the Custom Animation Type
- Windows Phone 7 : Sensors - Displaying Sunset and Sunrise
- Windows Phone 7 : Sensors - Indicating the User's Position?
- Windows Phone 7 : Sensors - Creating a Seismograph
 
 
Most view of day
- Microsoft Visio 2010 : Formatting Individual Shapes (part 2) - Curing Menu Cascade-itis
- Creating DVD Movies with Windows DVD Maker (part 5) - Changing Other DVD Options
- Duplicating and Copying DVDs (part 1) - Duplicating DVD Movies
- System Center Configuration Manager 2007 : Operating System Deployment - Task Sequences (part 1) - Variables
- Maintaining Windows 7 : Back Up Files
- Windows Phone 8 : Configuring Basic Device Settings - Controlling the Keyboard’s Behavior (part 3) - Customizing the Keyboard’s Behavior
- Adobe Dreamweaver CS5 : Using Library Items and Server-side Includes (part 1) - Using the Library Assets Panel - Adding a Library item
Top 10
- Windows Server 2012 : Managing networking using Windows PowerShell (part 2) - Examples of network-administration tasks
- Windows Server 2012 : Managing networking using Windows PowerShell (part 1) - Identifying networking cmdlets
- Sharepoint 2013 : Managing Site Security - Create Permission Levels for a Site
- Sharepoint 2013 : Managing Site Security - Edit a SharePoint Group’s Settings
- Sharepoint 2013 : Managing Site Security - Create a SharePoint Group for a Site
- Sharepoint 2013 : Assign Users’ Permissions on a Site
- Sharepoint 2013 : Get to a Site’s Permission Management Page (part 2) - Check What Permissions a User or a Group Has on a Site
- Sharepoint 2013 : Get to a Site’s Permission Management Page (part 1)
- Microsoft Exchange Server 2013 : Creating new mailboxes (part 4) - Automating mailbox settings,Ready-to-go custom attributes
- Microsoft Exchange Server 2013 : Creating new mailboxes (part 3) - Default folders, Manipulating mailbox settings
Windows XP
Windows Vista
Windows 7
Windows Azure
Windows Server
Windows Phone
Cars Review