March 2013
Volume 28 Number 03
MVVM - Messenger and View Services in MVVM
By Laurent Bugnion | March 2013
In a previous article, “IOC Containers and MVVM,” I introduced the concept of how an IOC container can help create and locate instances of classes (services, helpers) in various consumer classes. The article showed that IOC containers can be very helpful in decoupled applications, such as those developed according to the Model-View-ViewModel pattern. This pattern is widely used to develop XAML-based applications (in Silverlight, Windows Presentation Foundation, Windows Phone, and Windows 8) because it fits well with the data-binding system and with the tools used to design the application, notably the Visual Studio Designer and Blend. For an introduction to the MVVM pattern applied to Windows 8, read the article “Using the MVVM Pattern in Windows 8” in MSDN Magazine.
When implementing the MVVM pattern, we have a separation between the view (the XAML code coupled with its code-behind), the ViewModel and the model. Typically, we try to develop the ViewModel so that it doesn’t know anything about the view that it drives. This has multiple advantages: the developer team can work in an independent manner from the UI team; the ViewModel can be unit-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 affecting the ViewModel and the model.
In a typical XAML application, the developer uses the powerful data-binding system to declaratively synchronize a property from a XAML UI element with a property of another object in the application. This synchronization can go in one direction only (for example, when a TextBlock’s Text property changes to reflect the value of an object’s property), or in two ways (for example, when a TextBox’s Text property updates the String value of an object’s property, or when a CheckBox’s IsChecked property updates a Boolean value). Data binding is very comfortable and convenient, especially when it’s used in a visual designer like Blend or the Visual Studio Designer. However, it has limitations. For instance, a simple data binding cannot trigger an animation on the UI or cause a dialog to be shown to the user. Even basic actions such as navigation to a different page cannot be caused by simple data binding.
Figure 1 shows two-way data binding between a XAML view and its ViewModel (point 1). In addition, other possible interactions are represented.
Figure 1. Dependencies between Layers in MVVM
Point 2 shows the normal event flow. This is probably the best-known way for the XAML to communicate with the attached code-behind. It is very familiar to developers coming from more traditional environments, such as Windows Forms or even HTML/JavaScript. While events can be useful, they also cause issues when the XAML needs to be decoupled from the code. This can be the case when a XAML DataTemplate needs to be moved to a ResourceDictionary, for example. It can also be an issue when the code that handles the event needs to be moved from the code-behind to another object, such as the ViewModel. Events create a tight coupling between the XAML and the code-behind and limit the amount of refactoring that can be done easily.
Point 3 shows an alternative way for the XAML to trigger an action in the code. Typically, commands (which are an implementation of the ICommand interface) are exposed as properties of a ViewModel and are attached to a UI element by data binding. For instance, a Button control supports a Command property. The bound command will be invoked when the Button control is clicked. Commands also have limitations, notably the fact that only a few UI elements expose a Command property and only for one event (usually the Click event). If another event needs to be handled, the default commanding implementation is not sufficient. In another installment in this series, I’ll describe ways to work around command limitations, notably with the use of MVVM Light’s RelayCommand component, the EventToCommand behavior and (if needed) custom attached behavior implementations.
Point 4 shows the view’s code-behind calling a method on the ViewModel directly. This may sound like a violation of the principle that a view should be decoupled from its ViewModel. Actually, it’s not a problem for the view to know its ViewModel because the view will rarely need to be abstracted. A view’s code-behind is rarely unit tested; while possible, it is not very easy to trigger event handlers programmatically to test their action. So, while the ViewModel should be kept ignorant of the view, the contrary is not absolutely necessary. In fact, the code shown in Figure 2 is very frequently found in MVVM applications. While a principle of MVVM states that developers should keep the code-behind thin, it is sometimes easier to have a small snippet of code-behind handling a special situation than to look for complicated workarounds.
Figure 2. Getting the ViewModel in the View’s Code-Behind
public MainViewModel Vm
{ get
{
return (MainViewModel)DataContext;
}
}
In Windows 8, one possible use of calling the ViewModel from the view is to palliate an annoying issue: the lack of an UpdateSourceTrigger property on bindings. In WPF, which is the most advanced XAML platform available in terms of number of features, it’s possible to specify when a binding must be updated. For example, when a TextBox’s Text property is data bound to a String property on the ViewModel, we can have the XAML markup shown in Figure 3 (WPF only).
Figure 3. UpdateSourceTrigger Property in WPF XAML Markup
<TextBox Text="{Binding ZipCode,
Mode=TwoWay,
UpdateSourceTrigger=PropertyChanged}"
Style="{StaticResource ZipCodeTextBoxStyle}" />
Thanks to the PropertyChanged value of the UpdateSourceTrigger enumeration, the binding will be triggered every time the user types a character in the TextBox. Another possible value for UpdateSourceTrigger is LostFocus, which means that the binding will be triggered only when the user sets the focus on another element (for example, a button or the window itself).
In Windows RT, however, this property is missing on the Binding class. Such a two-way binding will always be triggered when the TextBox loses the focus. While this is okay in many cases, it can cause unwanted issues. For instance, if a validation message is shown, it is a better experience if the validation message is updated as soon as the user types a character, as shown in Figure 4.
Figure 4. Working Around the Lack of UpdateSourceTrigger.PropertyChanged in Windows RT
<!-- XAML markup -->
<TextBox Text="{Binding ZipCode, Mode=TwoWay}"
TextChanged="ZipCodeTextChanged"
Style="{StaticResource ZipCodeTextBoxStyle}" />
// Code behind
private void ZipCodeTextChanged(object sender, TextChangedEventArgs e)
{
var textbox = (TextBox)sender;
Vm.ZipCode = textbox.Text;
}
Now the ViewModel’s ZipCode property is updated every time the user types one character, which triggers the validation and updates the error message as the user types.
Communicating from the ViewModel to the View
Observant readers will notice that Figure 1 has one arrow missing: in this figure, there is no way for the ViewModel to communicate with the view. As mentioned earlier, the ViewModel should ideally have no knowledge of the view that it is attached to. In fact, it is very common for a given ViewModel to be attached to multiple views—for instance, because one view might become too complex and be split into two pages. To guarantee flexibility, the ViewModel must have only an abstracted knowledge of what the view can do.
There are multiple ways to solve this issue. The two solutions that I propose here are using MVVM Light’s Messenger class and using view services.
First, let’s implement a status message system using the MVVM Light Messenger. This class is an implementation of an event bus, a decoupled eventing system in which the sender of the event (or message) and the recipient don’t have any knowledge of each other. This is quite convenient for a status message system because each component of the application can choose to show status messages to the user without worrying about dependencies.
A Word of Caution About Messenger
Messenger is a powerful component that can greatly facilitate the task of communication, 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. Use with care!
The MVVM Light Toolkit has a number of predefined message classes available in the GalaSoft.MvvmLight.Messaging namespace. However, any type of message can be sent, and it is very easy to define your own messages. For example, the RssReader application that was developed in the first article in this series can be enhanced with a status message that’s shown to the user while the RSS feed is being loaded.
First, a status message class is defined, as shown in Figure 5. This will be the payload sent by the ViewModel to whomever is registered to receive this type of message.
Figure 5. Defining the Message Type
public class StatusMessage
{
public StatusMessage(
string status,
int timeoutMilliseconds)
{
Status = status;
TimeoutMilliseconds = timeoutMilliseconds;
}
public string Status
{
get; private set;
}
public int TimeoutMilliseconds
{
get; private set;
}
}
The MainViewModel’s Refresh method can be modified slightly, as shown in Figure 6. Before calling the asynchronous method, a StatusMessage is sent. Then, after the list of articles has been parsed and handled, another StatusMessage is sent, with a timeout of 3 seconds. Of course, this assumes that a recipient is registered and that this recipient knows to handle the message. Like all abstractions, the StatusMessage is a contract between the sender and the recipient.
Figure 6. Setting the Status
public async Task Refresh()
{
Items.Clear();
Messenger.Default.Send(new StatusMessage("Getting articles", 0));
var list = await _rssService.GetArticles();
foreach (var item in list)
{
Items.Add(item);
}
Messenger.Default.Send(new StatusMessage("Done", 3000));
}
On the view side, the class responsible for showing the status (in our case, the MainPage) needs to register to receive the StatusMessage type. To keep the Messenger as clean as possible, the message handler is registered in the OnNavigatedTo method (which is overridden from the base class), and it is unregistered in the OnNavigatingFrom method. This way we make sure that only one page at a time is registered to show the status. (See Figure 7.)
Figure 7. Showing the Status in Windows RT
protected override void OnNavigatedTo(NavigationEventArgs e)
{
Messenger.Default.Register<StatusMessage>(
this, HandleStatusMessage);
base.OnNavigatedTo(e);
}
protected override void OnNavigatingFrom(NavigatingCancelEventArgs e)
{
Messenger.Default.Unregister<StatusMessage>(
this, HandleStatusMessage);
base.OnNavigatingFrom(e);
}
private void HandleStatusMessage(StatusMessage msg)
{
Status.Message = msg.Status;
Status.Show(msg.TimeoutMilliseconds);
}
The page uses a UserControl named Status to show the message. This user control is placed on top of everything else, and its visibility is toggled to Visible with its Show methods. If a timeout (in milliseconds) is passed to the Show method, the Visibility value will be set back to Collapsed after the timeout expires. The same mechanism can be used to display custom messages or more complex dialogs to the user.
Implementing a DialogService
Using the Messenger service to pass messages from the ViewModel to the view is a good solution that works well, but it has the disadvantage of being a little unclear when a new developer reads the code. Because the sender and the receiver don’t know each other, the workflow can be difficult to follow when debugging. Another interesting alternative is to use an abstracted service registered to the IOC container, just like the RssService that is used to fetch the RSS articles. Instead of being a service oriented toward the model, like the RssService, the DialogService is oriented toward the view, which is why it is named a view service.
First, the interface for the DialogService is created by specifying the most common methods used to show a message to the user. Messages can be informative or used to convey an error. In the sample code, we have the IDialogService interface shown in Figure 8.
Figure 8. The IDialogService Interface
public interface IDialogService
{
void ShowMessage(string message, string title, string buttonText);
void ShowError(string errorMessage, string title, string buttonText);
void ShowError(Exception error, string title, string buttonText);
}
The implementation of the IDialogService depends on the platform used. To keep things simple, the sample code uses the default MessageBox in Windows Phone and the MessageDialog in Windows RT. In production projects, however, a designer would create a custom message box, which would be implemented as a UserControl just like the StatusControl shown earlier in this article. The Windows RT implementation of the DialogService is shown in Figure 9. Note that in this article’s sample these methods are part of the MainPage, but in a production application they would instead be moved to a Page base class, such as the LayoutAwarePage class used by many Windows RT apps (see the Common folder).
Figure 9. Implementation of IDialogService in MainPage for Windows RT
public sealed partial class MainPage : IDialogService
{
public MainPage()
{
InitializeComponent();
}
// More methods removed for brevity...
// IDialogService implementation
public async void ShowMessage(string message, string title, string buttonText)
{
// Using the same MessageDialog for errors and messages.
// In a real-life production implementation, a custom
// dialog box would be designed, and these methods would probably
// be passed into a Page base class (for instance in
// the LayoutAwarePage class in the Common folder.
var dialog = new MessageDialog(message, title);
dialog.Commands.Add(new UICommand(buttonText));
dialog.CancelCommandIndex = 0;
await dialog.ShowAsync();
}
public void ShowError(string errorMessage, string title, string buttonText)
{
// Using the same MessageDialog for errors and normal messages.
ShowMessage(errorMessage, title, buttonText);
}
public void ShowError(Exception error, string title, string buttonText)
{
ShowMessage(error.Message, title, buttonText);
}
}
Of course, the MainPage needs to register itself as an IDialogService in the IOC container. Because only one page is visible at a time, it also needs to unregister itself when it is navigated away from. This leaves space for the next page to register itself as the new IDialogService implementation. In MainPage, we now have the OnNavigatedTo and OnNavigatedFrom methods shown in Figure 10.
Figure 10. Registering and Unregistering the IDialogService in MainPage
protected override void OnNavigatedTo(NavigationEventArgs e)
{
Messenger.Default.Register<StatusMessage>(
this, HandleStatusMessage);
SimpleIoc.Default.Register<IDialogService>(() => this);
base.OnNavigatedTo(e);
}
protected override void OnNavigatedFrom(NavigationEventArgs e)
{
Messenger.Default.Unregister<StatusMessage>(
this, HandleStatusMessage);
SimpleIoc.Default.Unregister<IDialogService>();
base.OnNavigatedFrom(e);
}
In the MainViewModel, the IDialogService is obtained from the IOC container and made available in a property. The MainViewModel’s Refresh method can be modified by enclosing the call to the RssService’s GetArticles method in a try/catch. If an exception occurs, the DialogService is used to show the error to the user. Similarly, if the call to GetArticles was successful, but the returned list is empty, the user is also notified as shown in Figure 11.
Figure 11. Refresh Method with Error Handling
public IDialogService DialogService
{
get
{
return ServiceLocator.Current.GetInstance<IDialogService>();
}
}
public async Task Refresh()
{
Items.Clear();
try
{
Messenger.Default.Send(new StatusMessage("Getting articles", 0));
var list = await _rssService.GetArticles();
if (list.Count == 0)
{
DialogService.ShowMessage(
"We couldn't find any articles",
"Nothing found",
"Too bad!");
}
foreach (var item in list)
{
Items.Add(item);
}
Messenger.Default.Send(new StatusMessage("Done", 3000));
}
catch (Exception ex)
{
// Hide status
Messenger.Default.Send(new StatusMessage(string.Empty, 1));
DialogService.ShowError(
ex,
"Error when loading",
"Oops");
}
}
Unit Testing the Services
The abstractions we’re using allow unit testing of the ViewModel. For example, we can make sure that the error message is sent correctly by simulating an error case. For this, we create a test-only implementation of the IRssService class (also called a stub). This implementation always throws an exception, as shown in Figure 12.
In the MainViewModel class, the exception is caught by the try/catch clause described earlier, and the IDialogService instance is used to send the error message. By creating a stub implementation of the view services (INavigationService to avoid unwanted navigation while testing, and IDialogService to catch the messages), it is possible to test whether the MainViewModel shows error messages properly.
In addition, the Messenger class is used to register a test handler for the StatusMessage type, and the unit test can assert that a status message is sent by the Refresh method. Of course, these tests can be refined until a large portion of the MainViewModel code is covered.
Figure 12. Unit Testing Whether Messages Are Sent Properly
[TestClass]
public class MainViewModelTest
{
[TestMethod]
public void TestSendingErrorMessages()
{
var vm = new MainViewModel(
new TestRssService(),
new TestNavigationService());
StatusMessage receivedMessage = null;
Messenger.Default.Register<StatusMessage>(
this,
m => receivedMessage = m);
var dialogService = new TestDialogService();
SimpleIoc.Default.Register<IDialogService>(() => dialogService);
vm.Refresh();
// Test that a status message was sent
Assert.IsNotNull(receivedMessage);
Assert.AreEqual(dialogService.MessageReceived, TestRssService.TestErrorMessage);
}
public class TestDialogService : IDialogService
{
public string MessageReceived
{
get;
private set;
}
public void ShowMessage(string message, string title, string buttonText)
{
MessageReceived = message;
}
public void ShowError(string errorMessage, string title, string buttonText)
{
MessageReceived = errorMessage;
}
public void ShowError(Exception error, string title, string buttonText)
{
MessageReceived = error.Message;
}
}
public class TestNavigationService : INavigationService
{
public void GoBack() {}
public void NavigateTo(Type pageType) {}
public void NavigateTo(Type sourcePageType, object parameter) {}
}
public class TestRssService : IRssService
{
public const string TestErrorMessage = "This is a test error only";
public const int Timeout = 1234;
public Task<IList<RssArticle>> GetArticles()
{
throw new InvalidOperationException(TestErrorMessage);
}
}
}
Wrapping Up
This article is part of a series about MVVM in Windows 8 and the MVVM Light Toolkit. It builds on the sample application created in the first article, which shows how to use an IOC container and dependency injection to organize your application’s services and ViewModels. In this article, you learned how the different layers of the application communicate with each other, with a special emphasis on the ViewModel-to-view communication using the MVVM Light Messenger event bus for status messages and then a view service implementation for a dialog service. It also showed how these services can be replaced by test implementations to unit test the ViewModel.
The next article will show how to create design-time data in your application to maximize the use of Blend and the Visual Studio Designer and to improve the designer-developer workflow.
References
The MVVM Light Toolkit is available at https://mvvmlight.codeplex.com.
Laurent Bugnion is senior director for IdentityMine Inc., a Microsoft partner working with technologies such as Windows Presentation Foundation, Silverlight, Surface, Windows 8, Windows Phone and UX. He’s based in Zurich, Switzerland.
Thanks to the following technical experts for reviewing this article:
Karl Erickson has been involved in XAML developer education for the past six years, working with WPF, Silverlight, Windows Phone and Windows 8. His most recent project is the Reversi XAML/C# sample board game app.