How to Use MVVM in a Universal Windows App

The Model-View-ViewModel (MVVM) pattern helps developers separate an application's business and presentation logic from its user interface. Maintaining a clear separation between application logic and user interface helps address development and design issues, making an application easier to test, maintain, and develop. It can also improve the reusability of code and it allows multiple developers to collaborate more easily when working on the same project.

1. Introduction

Using the MVVM pattern, the user interface of the application and the underlying presentation and business logic are separated into three components:

  • The view component encapsulates the user interface and user interface logic.
  • The view model component encapsulates presentation logic and state.
  • The model layer encapsulates the application's business logic and data.

There are several frameworks available for implementing the MVVM pattern in a Windows application. Which framework is best for your project depends on your requirements. For this tutorial, we will use MVVM Light, a popular and easy-to-use MVVM framework.

This tutorial shows you how to create a Universal Windows app with MVVM Light support. You will learn how to:

  • create a Universal Windows app and add support for MVVM Light
  • implement the directory structure
  • add the view model layer
  • wire the data context
  • implement the messenger service to pass messages between view models

2. Project Setup

Step 1: Create a Universal Windows App

Let's start by creating a Universal Windows app. Select New Project from the File menu in Visual Studio. Expand Templates > Visual C# > Windows > Windows 8 > Universal and select Blank App (Universal Windows 8.1) from the list of project templates. Name your project and click OK to create the project.

Creating a Universal Windows App

This creates two new apps (Windows Phone 8.1 and Windows 8.1) and one shared project. The Windows Phone 8.1 and Windows 8.1 projects are platform specific projects and are responsible for creating the application packages (.appx) targeting the respective platforms.  The shared project is a container for code that runs on both platforms.

Step 2: Add MVVM Light Support

Right-click the solution name in the Solution Explorer and select Manage Nuget Packages for Solution.

Choose Manage Nuget Packages for Solution

Select the Browse tab and search for MVVM Light. Select the package MvvmLightLibs from the search results. Check both the Windows 8.1 and Windows Phone 8.1 projects, and click Install to add the MVVM Light libraries to the apps.

Search and Install MvvmLightLibs

At this point, you have added MVVM Light support to both your applications.

3. Project File Structure

A Universal Windows app that adopts the MVVM pattern requires a particular directory structure. The following snapshot shows a possible project file structure for a Universal Windows app.

Project File Structure

Let me walk you through the project structure of a typical Univesal Windows app that adopt the MVVM pattern:

  • Controls: This directory contains reusable user interface controls (application independent views). Platform specific controls are added directly to the platform specific project.
  • Strings: This directory contains strings and resources for application localization. The Strings directory contains separate directories for every supported language. The en-US directory, for example, contains resources for the English (US) language.
  • Models: In the MVVM pattern, the model encapsulates the business logic and data. Generally, the model implements the facilities that make it easy to bind properties to the view layer. This means that it supports "property changed" and "collection changed" notifications through the INotifyPropertyChanged and INotifyCollectionChanged interfaces.
  • ViewModels: The view model in the MVVM pattern encapsulates the presentation logic and data for the view. It has no direct reference to the view or any knowledge about the view's implementation or type.
  • Converters: This directory contains the value converters. A value converter is a convenient way to convert data from one type to another. It implements the IValueConverter interface.
  • Themes: The Themes directory contains theme resources that are of type ResourceDictionary. Platform specific resources are added directly to the specific project and shared resources are added to the shared project.
  • Services: This section can include classes for web service calls, navigation service, etc.
  • Utils includes utility functions that can be used across the app. Examples include AppCacheFileUtilsConstantsNetworkAvailabilityGeoLocation, etc.
  • Views: This directory contains the user interface layouts. Platform specific views are added directly to the platform specific project and common views are added to the shared project.

Depending on the type of view, the name should end with:

  • Window, a non-modal window
  • Dialog, a (modal) dialog window
  • Page, a page view (mostly used in Windows Phone and Windows Store apps)
  • View, a view that is used as subview in another view, page, window, or dialog

The name of a view model is composed of the corresponding view’s name and the word “Model”. The view models are stored in the same location in the ViewModels directory as their corresponding views in the Views directory.

4. Adding the View Model Layer

The view model layer implements properties and commands to which the view can bind data and notify the view of any state changes through change notification events. The properties and commands the view model provides, define the functionality offered by the user interface. The following list summarizes the characteristics, tasks, and responsibilities of the view model layer:

  • It coordinates the view's interaction with any model class.
  • The view model and the model classes generally have a one-to-many relationship.
  • It can convert or manipulate model data so that it can be easily consumed by the view.
  • It can define additional properties to specifically support the view.
  • It defines the logical states the view can use to provide visual changes to the user interface.
  • It defines the commands and actions the user can trigger.

In the next steps, we add two files to the view model layer, ViewModelLocator.cs and MainViewModel.cs.

Step 1: Add the MainViewModel Class

First, right-click the shared project and select Add, New Folder. Name the folder ViewModels. Next, right-click the ViewModels folder and select Add, New Item to add the MainViewModel class.

Modify the MainViewModel class to look like this:

The class contains a public property HelloWorld of type string. You can add additional methods, observable properties, and commands to the view model.

Step 2: Add the ViewModelLocator Class

We will add a public property for all the view models in the ViewModelLocator class and create a new resource, which we will use in the designer.

Right-click the ViewModels folder and select Add, New Item. Select a class and name it ViewModelLocator.cs. Update the ViewModelLocator class as shown below.

The ViewModelLocator class contains a public property Main whose getter returns an instance of the MainViewModel class. The constructor of ViewModelLocator registers the MainViewModel instance to the SimpleIoc service.

Next, open App.xaml file and add a new resource with the ViewModelLocator to be used in the designer.

5. Wiring Up the Data Context

The view and the view model can be constructed and associated at runtime in multiple ways. The simplest approach is for the view to instantiate its corresponding view model in XAML. You can also specify in XAML that the view model is set as the view's data context.

When the MainPage.xaml page is initialized, an instance of the MainViewModel is automatically constructed and set as the view's data context. Note that the view model must have a default parameter-less constructor for this approach to work.

Another approach is to create the view model instance programmatically in the view's constructor and set it as the data context.

Another approach is to create a view model instance and associate it with its view using a view model locator. In the sample app, we use the ViewModelLocator class to resolve the view model for MainPage.xaml.

Now that the view's data context has been set to the MainViewModel class, we can access its properties in the view. You can bind the text of a TextBlock to the HelloWorld property defined in the view model.

6. Messenger Service

The messenger service in MVVM Light allows for communication between view models or between view models and views. Let's say you have a view model that is used to provide business logic to a search function and two view models on your page that want to process the search to show the output. The messenger would be the ideal way to do this in a loosely bound way.

The view model that gets the search data would simply send a "search" message that would be consumed by any view model that was currently registered to consume the message. The benefits of using a messenger service are:

  • easy communication between view models without each view model having to know about each other
  • more message consumers can be added with little effort
  • it keeps the view models simple

To send a message:

To receive a message:

In the sample application, we will send a message from MainViewModel, which will be received by MainPage.xaml. These are the steps required for using the messenger service.

Step 1: Create Class to Contain the Message to Be Passed

Create a new class in the project and name it ShowMessageDialog.

Step 2: Instantiate Message Class and Broadcast Message

In MainViewModel.cs, create an instance of ShowMessageDialog and use the Messenger object to broadcast the message.

This broadcasts the message. All that is left for us to do, is to register a recipient and respond to the message.

Step 3: Register for Message and Handle When Received

Open MainPage.xaml.cs and register for the message in the constructor.

ReceiveMessage is a method that you need to implement. It will take the Message object and use the DialogService to display a dialog box.

Step 4: Create a Command to Send Message

Now that we can send and receive a message, we need to call the ShowMessage method. MVVM Light provides support for RelayCommand, which can be used to create commands in the view model. Add a public property ShowMessageCommand in the MainViewModel class that invokes the ShowMessage method.

Next, add a Button to MainPage.xaml and bind the ShowMessageCommand to its Command property.

Deploy the app to see if everything works as expected. Here's a snapshot of how MainPage.xaml looks on Windows 8.1.

MainPagexaml UI

When you click the Click Me button, a dialog box pops up.

Dialog box popup on MainPagexaml

Messenger is a powerful component that can make communication easier, but it also makes the code more difficult to debug because it is not always clear at first sight which objects are receiving a message.

Conclusion

By implementing the MVVM pattern, we have a clear separation between the view, view model, and model layers. Typically, we try to develop the view model so that it doesn’t know anything about the view that it drives. This has multiple advantages:

  • The developer team can work independently from the user interface team.
  • The view model can be tested easily, simply by calling some commands and methods, and asserting the value of properties.
  • Changes can be made to the view without having to worry about the effect it will have on the view model and the model.

Feel free to download the tutorial's source files to use as a reference.

Tags:

Comments

Related Articles