Welcome to the third and last part of this article series on using the MVVM pattern to build Windows 8 Store apps. In the two previous parts, we have covered the concepts of MVVM (part 1) and have learned how we can create ViewModel classes. We have also covered the use of a ViewModel Locator and finally used Commanding to allow us to declaratively (through XAML) link events happening in the UI with code in the ViewModel classes.
In this part, we are going to cover some more advanced concepts. We are starting out with messaging and will cover navigation as well. We’ll finish the series by looking at how we can add support for some of the Windows 8 specific application features such as the contracts. Let’s dive straight in!
Messaging between ViewModels
In our sample, we have two ViewModel classes: the MainViewModel and the EditViewModel. In a real-world application, we’ll end up with quite a few more ViewModel classes (as explained previously, you can say that you’ll have about the same amount of ViewModels then you have Views, so you can do the math yourself!). In many cases, these ViewModels will have to communicate with each other. In our scenario, the user can select a ToDo item in the list view; in the edit view, the details of the selected ToDo item will be shown. The selected item has to be passed from one ViewModel to the other one.
We *could* solve this by creating a reference of the EditViewModel in the MainViewModel (or vice-versa, depending on what you’d prefer) so that we can get/set the selected item. However, in this case, we are creating a hard reference between the two ViewModels: the two types are tightly coupled. If I want to test one of the ViewModels in isolation, this will create a difficulty. We’d have to be able to mock out the reference, build a mocked version… For our sample, that’s still doable, however, if you end up with a lot of ViewModels, this will be very difficult: you’ll end up in every ViewModel with a list of references to other ViewModels. This ends up as a real spaghetti of referenced ViewModels and takes away many of the advantages we have been trying to work towards.
A better approach is using a messaging system where the different ViewModels can register themselves with. Think of this as a pub-sub model. Some ViewModels (in our case, the MainViewModel) will be publishing a message (the selected ToDo item). Other ViewModels (in our case, the EditViewModel) are interested in receiving some types of messages. See the Pub-Sub thing appearing here?
For this to work, we need a component where we can register that we’ll want to send/receive messages. This component is often referred to as the mediator or the event aggregator. Using this intermediate, we don’t need to create references from one ViewModel to the other. Instead, we allow both ViewModels to communicate with each other using messages; in other words, they’re loosely coupled again.
We’re in luck again: MVVM Light contains a Messenger already by default. We’ll use this in our code. In fact, there’s more: a static, default instance is created on the Messenger class that we can use in simple applications. More complex applications will want to create more than one Messenger instance. Otherwise, too many messages might have to go through this single instance. While this is not a technical issue, think of it like this: if you have many listeners registered with the same Messenger, this might trigger code in many ViewModels, which you may not want.
Anyway, in our case, we’ll use the default instance for now. Below is the modified code of the InitializeCommands method of the MainViewModel. This is instructing to send a message via the Messenger to all ViewModels that are interested in receiving information.
private void InitializeCommands()
{
ItemClickCommand = new RelayCommand<ToDoItem>((item) =>
{
Messenger.Default.Send<ItemSelectedMessage>(new ItemSelectedMessage() { Item = SelectedToDo });
navigationService.Navigate("EditPage");
});
}
Notice that we are passing an instance of ItemSelectedMessage. This is a custom class that can contain extra information to be passed from one ViewModel to the other. Below is the code for this class:
public class ItemSelectedMessage
{
public ToDoItem Item { get; set; }
}
The EditViewModel is interested in receiving messages. In the InitializeMessenger, I’ve added the following code. It registers with the Messenger that whenever a message of type ItemSelectedMessage is received, it should be notified.
private void InitializeMessenger()
{
Messenger.Default.Register<ItemSelectedMessage>(this, OnItemSelectedMessageReceived);
}
private void OnItemSelectedMessageReceived(ItemSelectedMessage itemSelectedMessage)
{
SelectedToDoItem = itemSelectedMessage.Item;
}
The OnItemSelectedMessageReceived method will be called when the message is received. This sets the SelectedToDoItem to the item passed in the message. This way, the View can bind on this state property.
It’s recommended to create specific messages for the communication. While it’s possible to perform everything using regular strings, this will end up triggering a lot of registered ViewModels.
Services
At this point, we’ve covered most of the typical setup that’s required for an MVVM architecture. However, quite a few things remain unclear. How do we perform data access? How do we do navigation? How do we show a dialog to the user? Are these tasks that go directly in the ViewModel? Well, the answer is no. Should this then go in the View? Well, no either.
The solution is building a service (not a WCF service!). A service is simply a class that performs a task (one single task!). Such a task can be the navigation, or accessing data or showing dialogs. By creating separate classes for each of these tasks, we create blocks of functionality that can again be tested in isolation and can be mocked out when we test the ViewModel.
In our solution, the instances of these classes are created using a container. This means that they are registered application-wide so that anywhere in the system where they are required, they can be retrieved using this container. Let’s look at a few samples.
Data access using the ToDoDataService
The ToDoDataService is responsible for returning the data. It can be seen as an abstraction on top of the data layer. The data access is done by calling the generated proxy class in ToDoMvvm.ServiceClient. Instead of directly calling this proxy from the ViewModel classes, we insert the ToDoDataService. If at any point in time later on, the decision is made that the data access is to be done differently, then we only have to make a change in one place (in the ToDoDataService); the ViewModels themselves are not impacted by this.
Also in terms of testability, we can easily test this class in separation. And also for the ViewModels, we can pass it a mock version of the ToDoDataService so that the code is not dependant on the data access. We can see this reflected in the fact that in the ViewModels, we are working with an interface IToDoDataService:
private IToDoDataService toDoDataService;
The actual implementation is retrieved using IOC:
toDoDataService = SimpleIoc.Default.GetInstance<IToDoDataService>();
After this, we can use the data service from our ViewModel. In this case, in the MainViewModel, we are calling LoadAllToDos() on application start (from the constructor):
public async void LoadAllToDos()
{
ToDoItems = await toDoDataService.GetAllToDoItems();
}
The implementation of the ToDoDataService is shown next. Notice that it indeed directly talks with the service proxy to retrieve data.
public class ToDoDataService : IToDoDataService
{
public async Task<ObservableCollection<ToDoItem>> GetAllToDoItems()
{
ToDoServiceClient client = new ToDoServiceClient();
var result = await client.GetAllToDosAsync();
return result;
}
public async Task<ToDoItem> GetToDoItemById(int id)
{
ToDoServiceClient client = new ToDoServiceClient();
var result = await client.GetToDoItemByIdAsync(id);
return result;
}
public async void DeleteItemById(int id)
{
ToDoServiceClient client = new ToDoServiceClient();
await client.DeleteToDoItemByIdAsync(id);
}
}
Showing dialogs using a service
Another question that often arises is: “How do I show a dialog/error message to the user in a MVVM-based application”. For the answer, we again have to go to a custom service class. In the sample, the DialogService class is included. This class does what the name suggests: showing dialogs and delete confirmations. The code is shown below.
public class DialogService: IDialogService
{
public async void ShowDialog(string message)
{
MessageDialog messageDialog = new MessageDialog(message);
await messageDialog.ShowAsync();
}
public async void ShowDeleteConfirmation()
{
MessageDialog messageDialog = new MessageDialog("The item has been deleted");
await messageDialog.ShowAsync();
}
}
We can again register this class via its interface in the container.
SimpleIoc.Default.Register<IDialogService, DialogService>();
From then on, when a message has to be shown to the user, we can get a reference to this service from the ViewModel and ask it to show the dialog. Remember that it’s not the responsibility of the ViewModel to “know” how it has to show a dialog; all it knows is that a dialog has to be shown. The concrete implementation for this is not part of the ViewModel, we’ve moved this to a separate class.
Navigation in MVVM in Windows 8 Store apps
The final example I want to give you is navigation. When you think about navigation, it’s basically the same story as with showing dialogs: the ViewModel knows when navigation has to be done (it probably knows where to go as well). However, the concrete way of performing the navigation is not part of the ViewModel. We’ll therefore create another separate service that does the actual navigation for us. As before, this service is registered with the container through its interface. When we need to navigate from the ViewModel, we ask the container for the NavigationService instance, based on the interface type. We’ll get a concrete implementation of this class as result that we can use to perform the actual navigation with. At no point in time is the ViewModel responsible for the actual navigation: that is the task of the NavigationService.
Below you can see the code for the NavigationService.
public class NavigationService: INavigationService
{
private Frame frame;
public Frame Frame
{
get
{
return frame;
}
set
{
frame = value;
frame.Navigated += OnFrameNavigated;
}
}
private void OnFrameNavigated(object sender, Windows.UI.Xaml.Navigation.NavigationEventArgs e)
{
var view = e.Content as IView;
var viewModel = view.ViewModel;
viewModel.Initialize();
}
public void Navigate(string pageName)
{
switch (pageName)
{
case "MainPage":
var mainPageType = SimpleIoc.Default.GetInstance<IMainPage>();
Frame.Navigate(mainPageType.GetType());
break;
case "EditPage": var editPageType = SimpleIoc.Default.GetInstance<IEditPage>();
Frame.Navigate(editPageType.GetType());
break;
default:
var defaultPageType = SimpleIoc.Default.GetInstance<IMainPage>();
Frame.Navigate(defaultPageType.GetType());
break;
}
}
}
Another reason that we really need this to be a separate service (instead of performing the navigation directly from the ViewModel) is that we’ll need a reference to View objects in this case (this was the case for the DialogService as well by the way): we need to be passed a reference of the Frame (that is the root Frame of the application, but it could be some other element, part of your “master-page-template” as well). The root Frame is set from the OnLaunched() in the App.xaml.cs:
protected override void OnLaunched(LaunchActivatedEventArgs args)
{
Frame rootFrame = Window.Current.Content as Frame;
// Do not repeat app initialization when the Window already has content,
// just ensure that the window is active
if (rootFrame == null)
{
// Create a Frame to act as the navigation context and navigate to the first page
rootFrame = new Frame();
var navigationService = SimpleIoc.Default.GetInstance<INavigationService>();
navigationService.Frame = rootFrame;
if (args.PreviousExecutionState == ApplicationExecutionState.Terminated)
{
//TODO: Load state from previously suspended application
}
// Place the frame in the current Window
Window.Current.Content = rootFrame;
}
if (rootFrame.Content == null)
{
// When the navigation stack isn't restored navigate to the first page,
// configuring the new page by passing required information as a navigation
// parameter
if (!rootFrame.Navigate(typeof(MainPage), args.Arguments))
{
throw new Exception("Failed to create initial page");
}
}
// Ensure the current window is active
Window.Current.Activate();
}
Contracts in Windows 8: the MVVM way
The final topic I’d like to discuss is the way we can work with contracts in Windows 8. More specifically, I’d like to take you through the implementation of the Share Contract. Let’s say we want to make the application work as a Share source. In this case, no declaration needs to be made, however, we need to register with the DataTransferManager, registering that we can share data from a certain page. Again I’ve created a separate service class for the sharing to work: ShareService.
public class ShareService : IShareService
{
public string ShareData { get; set; }
public ShareService()
{
}
public void Initialize()
{
DataTransferManager dataTransferManager;
dataTransferManager = DataTransferManager.GetForCurrentView();
dataTransferManager.DataRequested -= this.DataRequested;
dataTransferManager.DataRequested += this.DataRequested;
}
private void DataRequested(DataTransferManager sender, DataRequestedEventArgs e)
{
e.Request.Data.Properties.Title = "ToDo item";
e.Request.Data.Properties.Description = "This is data from the ToDo app";
e.Request.Data.SetText(ShareData ?? string.Empty);
}
}
Of course, on a per-page base, we need to specify if the service is usable (in other words: if sharing is possible from the specific page). We do that in the Initialize() method:
public void Initialize()
{
SimpleIoc.Default.GetInstance<IShareService>().Initialize();
}
And that’s it: sharing works, entirely MVVM based! Yay J
Summary
In this third and last part, we’ve added several service classes that handle specific tasks within our application, such as navigation, Sharing via the Contracts and showing dialogs.
With this, this short article series is finished, I hope you know have a solid understanding of the MVVM pattern and how to apply it to develop Windows 8 Store apps! Thanks for reading!
About the author
Gill Cleeren is Microsoft Regional Director (www.theregion.com), Silverlight MVP (former ASP.NET MVP) and Telerik MVP. He lives in Belgium where he works as .NET architect at Ordina (http://www.ordina.be/). Passionate about .NET, he’s always playing with the newest bits. In his role as Regional Director, Gill has given many sessions, webcasts and trainings on new as well as existing technologies, such as Silverlight, ASP.NET and WPF at conferences including TechEd Berlin 2010, TechDays Belgium – Switzerland - Sweden, DevDays NL, NDC Oslo Norway, SQL Server Saturday Switserland, Spring Conference UK, Silverlight Roadshow in Sweden, Telerik RoadShow UK… He’s also the author of many articles in various developer magazines and for SilverlightShow.net and he organizes the yearly Community Day event in Belgium. He also leads Visug (www.visug.be), the largest .NET user group in Belgium. Gill is the author of “Silverlight 4 Data and Services Cookbook”. In 2012, the second edition, “Silverlight 5 Data and Services Cookbook” was released.
You can find his blog at www.snowball.be.
Twitter: @gillcleeren