C# – MVVMLight Toolkit Messages

MVVM pattern is the most popular pattern to developers in WPF environment and there are already many platforms/frameworks supporting this pattern out there in internet. For example, I use Caliburn Micro Framework at work and use MVVM Light Toolkit at home for my private studying. Each framework has its own advantages and disadvantages so just choose one which you like or it satisfies you at most. Today I would like to make a small post to describe how the Messenger of MVVM Light Toolkit (MLT) works so that we can make the communication within application without violating the MVVM pattern.

First of all, I would like to quote the definition and classes of Messenger from its homepage so that we have a brief vision of this class (http://www.galasoft.ch/mvvm/)

A Messenger class (and diverse message types) is used to communicate within the application. Recipients only receive the message types that they register for. Additionally, a target type can be specified, in which case the message will only be transmitted if the recipient’s type matches the target parameter.
Messages can be anything from simple values to complex objects. You can also use specialized message types, or create your own types deriving from them.
- MessageBase: A simple message class, carrying optional information about the message’s sender.
- GenericMessage: A simple message with a Content property of type T.
- NotificationMessage: Used to send a notification (as a string) to a recipient. For example, save your notifications as constant in a Notifications class, and then send Notifications.Save to a recipient.
- NotificationMessage: Same as above, but with a generic Content property. It can be used to pass a parameter to the recipient together with the notification.
- NotificationMessageAction: Sends a notification to a recipient and allows the recipient to call the sender back.
- NotificationMessageAction: Sends a notification to a recipient and allows the recipient to call the sender back with a generic parameter.
- DialogMessage: Used to request that a recipient (typically a View) displays a dialog, and passes the result back to the caller (using a callback). The recipient can choose how to display the dialog, either with a standard MessageBox, with a custom popup, etc…
- PropertyChangedMessage: Used to broadcast that a property changed in the sender. It fulfills the same purpose than the PropertyChanged event, but in a less tight way.

As you can see, we have lots of types of messages supported in MLT – from the simplest one with string to the most complex one with object. I will go through all of these messages through this simple example below. Because of demo purposes, this example works really “silly” but I’m sure that it will help you to have a good look about Messenger class of MLT. The example will be built step by step to demonstrate how all of supported message in MLT work.

MessageBase
- Create a MVVMLight WPF Project in Visual Studio. In MainWindow.xaml, insert a Button like code below

<Grid x:Name="LayoutRoot">
	<Grid.RowDefinitions>
		<RowDefinition Height="8*"></RowDefinition>
		<RowDefinition></RowDefinition>
	</Grid.RowDefinitions>
	<StackPanel Grid.Row="1" Orientation="Horizontal">
		<Button Content="LogIn" Command="{Binding ShowLogInDlgCommand}" Margin="10,0" Padding="10"></Button>
	</StackPanel>
</Grid>

- This button is bound with a Command to show a new dialog allowing user to log in to application. The command is just a normal one calling to an action when a button was clicked

private ICommand showLogInDlgCommand;
public ICommand ShowLogInDlgCommand
{
	get
	{
		if (showLogInDlgCommand == null)
			showLogInDlgCommand = new RelayCommand(() => ShowLogInDlg());
		return showLogInDlgCommand;
	}
}

private void ShowLogInDlg()
{
	//Open login dialog
}

- So we have already prepared the code for calling new dialog. Now let’s create a login dialog by adding new MvmView (WPF) called LogInWindow and adding new MvvmViewModel (WPF) called LogInViewModel into ViewModel folder. Now we have new View and ViewModel, we must tell ViewModelLocator their relationship so that they can work with each other. Open ViewModelLocator.cs file, type in “mvvmlocatorproperty” to use code snippet creating new locator property.

mvvmlocatorproperty

Let’s call it “LogIn” with type of LogInViewModel. After creating property, copy the content of new created Cleanup() function to the old one, then delete it. The old Cleanup() function looks like below

public static void Cleanup()
{
	ClearMain();
	ClearLogIn();
}

- In the constructor of ViewModelLocator() call CreateLogIn() after CreateMain() like following

public ViewModelLocator()
{
	CreateMain();
	CreateLogIn();
}

- At the end we must build the data binding between View and ViewModel in LogInWindow.xaml to finish.

<Window x:Class="MvvmLight_Messenger_Example.LogInWindow"
	xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
	xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
	Title="LogInWindow" Height="300" Width="350"
	DataContext="{Binding LogIn, Source={StaticResource Locator}}" WindowStartupLocation="CenterScreen">

- Now we are finished with setting up new dialog. Let’s call it from ShowLogInDlg() function, but if we initialize a new instance of LogInWindow, we are violating the pattern. We should only call View from View. Therefore the Messenger class will play in role

private void ShowLogInDlg()
{
	//Open login dialog
	Messenger.Default.Send<StringMessage>(new StringMessage() { Message = MessengerMessages.ShowLoginDialog });
}

public class StringMessage : MessageBase
{
	public string Message { get; set; }

	public StringMessage()
	{
		Sender = "StringMessage";
	}
}

public static class MessengerMessages
{
	public static readonly string ShowLoginDialog = "{01BD6EF9-B929-4988-8369-EF112BC37520}";
}

- As you can see, I use Messenger to broadcast a message of MessageBase class. However this class is very simplest one with 2 properties: Sender and Target which can only set by the child class of MessageBase. Therefore I have to inherit it, add new property Message and set the Sender property to identify the sender.

public class MessageBase
{
	public MessageBase();
	public MessageBase(object sender);
	public MessageBase(object sender, object target);
	public object Sender { get; protected set; }
	public object Target { get; protected set; }
}

- Now we need to “listen” to this message and react when it comes in MainWindow.xaml.cs.

public MainWindow()
{
	InitializeComponent();
	Closing += (s, e) => ViewModelLocator.Cleanup();
	Messenger.Default.Register<StringMessage>(this, (message) => StringMessageActionHandler(message));
}

private void StringMessageActionHandler(StringMessage message)
{
	if (message.Message == MessengerMessages.ShowLoginDialog)
	{
		LogInWindow wnd = new LogInWindow();
		wnd.Title = "Show login from sender : " + message.Sender.ToString();
		wnd.ShowDialog();
	}
}

- So that’s how a simple message is sent within application for communicating between a ViewModel and a View. We broadcast a message with predefined type and register it anywhere where we want to react with it. With this way, we do not violate the design of pattern because we don’t initialize any View directly in ViewModel.
DialogMessage
- Now let’s expand the example by defining some functionalities of LogInWindow

<Grid>
	<Grid.RowDefinitions>
		<RowDefinition></RowDefinition>
		<RowDefinition></RowDefinition>
	</Grid.RowDefinitions>
	<TextBox Text="{Binding UserPassword}"></TextBox>
	<Button Grid.Row="1" Content="LogIn" Command="{Binding LoginCommand}"></Button>
</Grid>

- This LogInWindow is very simple one with a textbox and a button login binding with LoginCommand. If the user enters the right password “MVVM”, they are allowed to do something. If not, an error message comes and asks for resetting textbox to try again.

private void CheckLogIn()
{
	if (UserPassword == "MVVM")
	{
		//Login successfully
	}
	else
	{
		var message = new DialogMessage("Login failed, do you want to clear entered password and try again?", LogInDialogMessageCallback)
		{
			Button = MessageBoxButton.OKCancel,
			Caption = "Clear entered password?"
		};

		Messenger.Default.Send(message);
	}
}

private void LogInDialogMessageCallback(MessageBoxResult result)
{
	if (result == MessageBoxResult.OK)
	{
		UserPassword = "";
	}
}

- In the code listing above, I broadcast a DialogMessage to display a MessageBox asking user for his choice and handle the result in callback function (clearing the textbox if user chooses OK). Therefore in LogInWindow.xaml.cs, I register to listen to this message, call a MessageBox when it comes and call its callback function with result of MessageBox

public LogInWindow()
{
	...
	Messenger.Default.Register<DialogMessage>(this, message =>
	{
		var result = MessageBox.Show(message.Content, message.Caption, message.Button);
		message.ProcessCallback(result);
	});
}

- And that’s the way how you call a MessageBox with MLT. Of course, you can call any anything you want when you receive this message. But I often use this message only when I want to call a Messagebox and remember never call a View directly from ViewModel. Use Messenger and initialize View in View so that they are completely separated.

NotificationMessage
- We have notified the user when they enter wrong password. What’ll happen if they give the correct one? To keep the example simple as it can be, I would like to open an OpenFileDialog when user enters the correct password so that they can choose file from local computer. Again, I can’t call any Dialog directly from ViewModel, View must do it.

if (UserPassword == "MVVM")
{
	//Login successfully
	Messenger.Default.Send(new NotificationMessage(MessengerMessages.LoginSuccessfully));
}
else
{
	...
}

public LogInWindow()
{
	...
	Messenger.Default.Register<NotificationMessage>(this, (message) => NotificationMessageHandler(message));
}

private void NotificationMessageHandler(NotificationMessage message)
{
	if (message.Notification == MessengerMessages.LoginSuccessfully)
	{
		var dialog = new OpenFileDialog { Filter = "All files (*.*)|*.*", Multiselect = true };
		if (dialog.ShowDialog() == true)
		{
			//Handles selected files
		}
	}
}

- The workflow is exactly same as before, I send a message from ViewModel and catch it in View to open the dialog. What’s different here is that I don’t use StringMessage class but use a built-in NotificationMessage to carry the string-message. That also means the NotificationMessage has the same meaning and functionality like StringMessage.

GenericMessage
- Now we have already opened OpenFileDialog, get selected files and what’s next? Ok, I would like to display the selected files with their info in MainWindow. We are now on the higher level. We communicate not only between View and its ViewModel but also View and another ViewModel.

private void NotificationMessageHandler(NotificationMessage message)
{
	if (message.Notification == MessengerMessages.LoginSuccessfully)
	{
		var dialog = new OpenFileDialog { Filter = "All files (*.*)|*.*", Multiselect = true };
		if (dialog.ShowDialog() == true)
		{
			//Handles selected files
			Messenger.Default.Send(new OpenedFilesMessage(dialog.FileNames.Select(x => new FileInfo(x))));
		}
	}
}

public class OpenedFilesMessage : GenericMessage<IEnumerable<FileInfo>>
{
	public OpenedFilesMessage(IEnumerable<FileInfo> lstFiles)
		: base(lstFiles)
	{
	}
}

- I broadcast a complex message to application which bases on GenericMessage class. This message contains list of FileInfo from selected files. At the receiver, I register to receive this message and get its content back

public MainViewModel()
{
	if (IsInDesignMode)
	{
		// Code runs in Blend --> create design time data.
	}
	else
	{
		// Code runs "for real"
		Messenger.Default.Register<OpenedFilesMessage>(this, OpenedFilesCallback);
	}
}

private void OpenedFilesCallback(OpenedFilesMessage result)
{
	SelectedFiles = result.Content.ToList();
}

- The property SelectedFiles will be bound with a ListBox in MainWindow to display the result

<ListBox Grid.Row="0" ItemsSource="{Binding SelectedFiles}" Grid.IsSharedSizeScope="True">
	<ListBox.ItemTemplate>
		<DataTemplate>
			<Grid>
				<Grid.ColumnDefinitions>
					<ColumnDefinition SharedSizeGroup="col0"></ColumnDefinition>
					<ColumnDefinition SharedSizeGroup="col1"></ColumnDefinition>
					<ColumnDefinition SharedSizeGroup="col2"></ColumnDefinition>
				</Grid.ColumnDefinitions>
				<TextBlock Grid.Column="0" Text="{Binding Name}" Margin="10,0"></TextBlock>
				<TextBlock Grid.Column="1" Text="{Binding Length}" Margin="10,0"></TextBlock>
				<TextBlock Grid.Column="2" Text="{Binding LastAccessTime}" Margin="10,0"></TextBlock>
			</Grid>
		</DataTemplate>
	</ListBox.ItemTemplate>
</ListBox>

- And with this code, we finished with GenericMessage class which allows us to carry a message with a pre-defined Content and get it back at the receiver.

PropertyChangedMessage
- Until now, we have a lot of properties in our example like SelectedFiles of MainViewModel, UserPassword of LogInViewModel (see in attached source code). These properties are used only for updating bindings, but no broadcast. Then how we can broadcast this type of property? Let’s expand ListBox by binding the SelectedItem with a property

<ListBox Grid.Row="0" ItemsSource="{Binding SelectedFiles}" Grid.IsSharedSizeScope="True" SelectedItem="{Binding SelectedItem}">

public const string SelectedItemPropertyName = "SelectedItem";

private FileInfo _selectedItem = null;

public FileInfo SelectedItem
{
	get
	{
		return _selectedItem;
	}

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

		var oldValue = _selectedItem;
		_selectedItem = value;

		// Update bindings, broadcast
		RaisePropertyChanged(SelectedItemPropertyName, oldValue, value, true);
	}
}

- In this property I would like to broadcast its change within application. That means every time when I selected item in Listbox, this property will trigger the RaisePropertyChanged message to application and I will show a FileAttributesWindow to display attributes of this selected file. Let’s create new View called FileAttributesWindow and new ViewModel called FileAttributesViewModel. FileAttributesWindow has some TextBlocks to show attributes of selected file like this

<Grid DataContext="{Binding SelectedFile}" Margin="10">
	<StackPanel>
		<TextBlock Text="{Binding Attributes, StringFormat='Attributes: \{0\}'}" Background="AliceBlue"></TextBlock>
		<TextBlock Text="{Binding CreationTime, StringFormat='Creation Time: \{0\}'}" Background="AntiqueWhite"></TextBlock>
		<TextBlock Text="{Binding CreationTimeUtc, StringFormat='Creation Time Utc: \{0\}'}" Background="Aqua"></TextBlock>
		<TextBlock Text="{Binding Directory, StringFormat='Directory: \{0\}'}" Background="Aquamarine"></TextBlock>
		<TextBlock Text="{Binding DirectoryName, StringFormat='DirectoryName: \{0\}'}" Background="Azure"></TextBlock>
		<TextBlock Text="{Binding Exists, StringFormat='Exists: \{0\}'}" Background="Beige"></TextBlock>
		<TextBlock Text="{Binding Extension, StringFormat='Extension: \{0\}'}" Background="Bisque"></TextBlock>
		<TextBlock Text="{Binding FullName, StringFormat='FullName: \{0\}'}" Background="BlanchedAlmond"></TextBlock>
		<TextBlock Text="{Binding IsReadOnly, StringFormat='Is Read Only: \{0\}'}" Background="BurlyWood"></TextBlock>
		<TextBlock Text="{Binding LastAccessTime, StringFormat='Last Access Time: \{0\}'}" Background="CadetBlue"></TextBlock>
		<TextBlock Text="{Binding LastAccessTimeUtc, StringFormat='Last Access Time Utc: \{0\}'}" Background="Chartreuse"></TextBlock>
		<TextBlock Text="{Binding LastWriteTime, StringFormat='Last Write Time: \{0\}'}" Background="Coral"></TextBlock>
		<TextBlock Text="{Binding LastWriteTimeUtc, StringFormat='Last Write Time Utc: \{0\}'}" Background="CornflowerBlue"></TextBlock>
		<TextBlock Text="{Binding Length, StringFormat='Length: \{0\}'}" Background="Cornsilk"></TextBlock>
		<TextBlock Text="{Binding Name, StringFormat='Name: \{0\}'}" Background="Cyan"></TextBlock>
	</StackPanel>
</Grid>

And what we have to do now is registering the PropertyChangedMessage in constructor of FileAttributesViewModel and set its SelectedFile property with the value of selected item from Main

public FileAttributesViewModel()
{
	Messenger.Default.Register<PropertyChangedMessage<FileInfo>>(this, (message) => DispatcherHelper.CheckBeginInvokeOnUI(() => this.SelectedFile = message.NewValue));
}

- So in this section, we have learned how to broadcast a property changes and catch PropertyChangedMessage globally to handle changes as we want. The FileAttributesWindow will show details of each selected item from MainWindow.

PropertyChangedMessage broadcast

NotificationMessageAction
- In last section, I would like to discuss about NotificationMessageAction which sends a notification to a recipient and allows the recipient to call the sender back with a generic parameter. To demonstrate how this message work, let’s add a new button “Load new files” in main which allows user to selected another files after login

<StackPanel Grid.Row="1" Orientation="Horizontal">
	<Button Content="LogIn" Command="{Binding ShowLogInDlgCommand}" Margin="10,0" Padding="10"></Button>
	<Button Content="Load new files" Command="{Binding LoadNewFilesCommand}" Margin="10,0" Padding="10"></Button>
</StackPanel>

- When user clicks on this button, we will broadcast a message of NotificationMessageAction where T is a List to receive the selected files from user.

private void LoadNewFiles()
{
	var message = new NotificationMessageAction<List<FileInfo>>(MessengerMessages.LoadNewFiles, LoadNewFilesCallback);
	Messenger.Default.Send(message);
}
private void LoadNewFilesCallback(List<FileInfo> files)
{
	SelectedFiles = files;
}

- The message will be registered at MainWindow.xaml.cs to call OpenFileDialog and give selected files back to the callback function to display on Listbox through setting SelectedFiles property

public MainWindow()
{
	...
	Messenger.Default.Register<NotificationMessageAction<List<FileInfo>>>(this,(message) => NotificationMessageActionHandler(message));
}

private void NotificationMessageActionHandler(NotificationMessageAction<List<FileInfo>> message)
{
	if (message.Notification == MessengerMessages.LoadNewFiles)
	{
		if (MessageBox.Show("Do you really want to load new files?","MVVM Light Messages",MessageBoxButton.OKCancel) == MessageBoxResult.OK)
		{
			OpenFileDialog openFileDialog = new OpenFileDialog();
			openFileDialog.Filter = "All files(*.*)|*.*";
			openFileDialog.Multiselect = true;
			if (openFileDialog.ShowDialog() == true)
			{
				message.Execute(openFileDialog.FileNames.Select(x => new FileInfo(x)).ToList());
			}
		}

	}
}

- Now we reach the end of this post. I hope you have now a quick look about how Messenger class of MLT works and know how to use it in your application for communicating between View and ViewModel. The truth is I don’t explain much about the code but I think the code says itself more than I can. Therefore download the code “MvvmLight Messenger Example“, examine it and let’s rock with Messenger of MLT.

3 thoughts on “C# – MVVMLight Toolkit Messages”

  1. Thanks for a great article. Might be a little out-dated because of the Simple IoC. Any chance you could update your article to fit the newest version of MVVM Light?

  2. @Y. : I updated the project with current version of MVVM Light. It seems to be that everything still works.

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>