Communicate Between Two View Models Using Messenger

Introduction

 
During complex application development, it is often required that we communicate between two view models. One straight forward way is to maintain a viewmodel single instance throughout the application and start accessing it from anywhere. However, this can cause maintenance difficulties when have dependencies. Even if we manage to handle the dependency issue, testing will become complicated. 
 
In this kind of scenario, we can go with Messenger. Messenger is the MVVM Light framework. It will make exchanging data between two view models easy. Basically, Messenger will work like an Observer design pattern. For more information on the observer design pattern, refer to this article Observer Design Pattern
 
We will how we can communicate between 2 View Models using Messenger. Then, we will create 2 MVVM Controls by name ControlA, ControlB and try to call a method in which is there is one view model within another view model.
 
Prerequisites
  • The reader should know about MVVM  
Steps  
 
First, create WPF Project
 
Go to Nuget manager and install MVVM Light.
 
 
Create a Folder structure like below.
 
 
We will add 3 buttons in ControlA and when the button is clicked from ControlA, we will put a message in ControlB.
 
In Control B:
 
Under ViewModel, add the class ControlBViewModel then add the code shown below.
  1. using System.ComponentModel;  
  2. using MSG = GalaSoft.MvvmLight.Messaging;  
  3.   
  4. namespace MVVM.Messenger.Sample.Demo.ControlB.ViewModel  
  5. {  
  6.     public class ControlBViewModel : INotifyPropertyChanged  
  7.     {  
  8.         public ControlBViewModel()  
  9.         {  
  10.             ContentA = "None of the button pressed";  
  11.             MSG.Messenger.Default.Register<MessageName>(this, UpdateContent);  
  12.         }  
  13.         public event PropertyChangedEventHandler PropertyChanged;  
  14.         public void OnPropertyChanged(string propertyName) => PropertyChanged?.Invoke(thisnew PropertyChangedEventArgs(propertyName));  
  15.   
  16.         private string _contentA;  
  17.         public string ContentA  
  18.         {  
  19.             get => _contentA;  
  20.             set  
  21.             {  
  22.                 _contentA = value;  
  23.                 OnPropertyChanged(nameof(ContentA));  
  24.             }  
  25.         }  
  26.         public void UpdateContent(MessageName message)  
  27.         {  
  28.             ContentA = $"{message.Name} Pressed";  
  29.         }  
  30.     }  
  31. }  
In the above class we can see that we have created a method called UpdateContent. From this, we will call from ControlA. In ControlB, using Messenger we are registering the method UpdateContent. Here, registering the method uniqueness depends on the parameter that we pass to that method. For instance, in the method UpdateContent, we are passing the parameter type MessageName. Basically, when we call any method using Messenger by passing parameter MessageName, Messenger will trigger UpdateContent Method.
 
That’s why I created a MessageName class specifically for UpdateContent in MessangerHelper file, as shown below. 
  1. namespace MVVM.Messenger.Sample.Demo  
  2. {  
  3.     public class MessageName  
  4.     {  
  5.         public string Name { getset; }  
  6.     }  
  7. }  
Under Vthe iewModel folder, create a UserControl with the name ControlBView
  1. <UserControl x:Class="MVVM.Messenger.Sample.Demo.ControlB.View.ControlBView"  
  2.             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"  
  3.              xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"  
  4.              xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"   
  5.              xmlns:d="http://schemas.microsoft.com/expression/blend/2008"  
  6.              xmlns:viewModel="clr-namespace:MVVM.Messenger.Sample.Demo.ControlB.ViewModel"  
  7.              Width="400" Height="100">  
  8.     <UserControl.Resources>  
  9.         <viewModel:ControlBViewModel x:Key="vm"/>  
  10.     </UserControl.Resources>  
  11.     <UserControl.DataContext>  
  12.         <Binding Source="{StaticResource vm}"/>  
  13.     </UserControl.DataContext>  
  14.     <Grid>  
  15.         <Grid.RowDefinitions>  
  16.             <RowDefinition Height="30"/>  
  17.             <RowDefinition Height="*"/>  
  18.         </Grid.RowDefinitions>  
  19.         <Grid Grid.Row="0" Background="Orange">  
  20.             <Label Content="UserControlB" Height="30" Width="Auto" Foreground="White"/>  
  21.         </Grid>  
  22.         <Grid Grid.Row="1" Margin="15" HorizontalAlignment="Center" VerticalAlignment="Center">  
  23.             <Label Height="100" Width="Auto" Content="{Binding ContentA}" FontSize="16"/>  
  24.         </Grid>  
  25.   
  26.     </Grid>  
  27. </UserControl>  
In the above code, we can see that ContentA Parameter was bounded to lable, which is actually defined in ViewModel using MVVM
 
In ControlA:
 
Under the ViewModel folder, add the class ControlAViewModel and then add the code shown below.
  1. using System.ComponentModel;  
  2. using System.Windows.Input;  
  3. using MVVM.Messenger.Sample.Demo.ControlA.Business;  
  4.   
  5. namespace MVVM.Messenger.Sample.Demo.ControlA.ViewModel  
  6. {  
  7.     public class ControlAViewModel : INotifyPropertyChanged  
  8.     {  
  9.         public event PropertyChangedEventHandler PropertyChanged;  
  10.         public void OnPropertyChanged(string propertyName) => PropertyChanged?.Invoke(thisnew PropertyChangedEventArgs(propertyName));  
  11.   
  12.         private ICommand actionCommand;  
  13.         public ICommand ActionCommand  
  14.         {  
  15.             get => actionCommand = actionCommand ?? new RelayCommand(this);  
  16.         }  
  17.   
  18.     }  
  19. }  
Under the View folder add UserControl ControlAView and add the below code:
  1. <UserControl x:Class="MVVM.Messenger.Sample.Demo.ControlA.View.ControlAView"  
  2.             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"  
  3.              xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"  
  4.              xmlns:viewModel="clr-namespace:MVVM.Messenger.Sample.Demo.ControlA.ViewModel"  
  5.              Width="400" Height="150">  
  6.     <UserControl.Resources>  
  7.         <viewModel:ControlAViewModel x:Key="vm"/>  
  8.     </UserControl.Resources>  
  9.     <UserControl.DataContext>  
  10.         <Binding Source="{StaticResource vm}"/>  
  11.     </UserControl.DataContext>  
  12.     <Grid>  
  13.         <Grid.RowDefinitions>  
  14.             <RowDefinition Height="30"/>  
  15.             <RowDefinition Height="*"/>  
  16.         </Grid.RowDefinitions>  
  17.         <Grid Grid.Row="0" Background="Orange">  
  18.             <Label Content="UserControlA" Height="30" Width="Auto" Foreground="White"/>  
  19.         </Grid>  
  20.         <Grid Grid.Row="1" HorizontalAlignment="Center" VerticalAlignment="Center">  
  21.             <StackPanel Orientation="Horizontal">  
  22.                 <Button Name="Button_A" Height="100" Width="100" Content="Button A" Command="{Binding ActionCommand}"  
  23.                         CommandParameter="{Binding ElementName=Button_A, Path=Content}"/>  
  24.                 <Button Name="Button_B" Height="100" Width="100" Content="Button B" Margin="5" Command="{Binding ActionCommand}"  
  25.                         CommandParameter="{Binding ElementName=Button_B, Path=Content}"/>  
  26.                 <Button Name="Button_C" Height="100" Width="100" Content="Button C" Margin="5" Command="{Binding ActionCommand}"  
  27.                         CommandParameter="{Binding ElementName=Button_C, Path=Content}"/>  
  28.             </StackPanel>  
  29.         </Grid>  
  30.   
  31.     </Grid>  
  32. </UserControl>  
In the above UserControl, we can see that we bounded ActionCommnd to Buttons. This will redirect a click event to ViewModel, then from ViewModel to RelayCommand as per the MVVM pattern
 
Under the Business folder, add a class called RelayCommand, then add the code below.
  1. using System;  
  2. using System.Windows.Input;  
  3. using MSG = GalaSoft.MvvmLight.Messaging;  
  4. using MVVM.Messenger.Sample.Demo.ControlA.ViewModel;  
  5.   
  6. namespace MVVM.Messenger.Sample.Demo.ControlA.Business  
  7. {  
  8.     public class RelayCommand : ICommand  
  9.     {  
  10.         private ControlAViewModel _controlAViewModel;  
  11.         public RelayCommand(ControlAViewModel controlAViewModel)  
  12.         {  
  13.             _controlAViewModel = controlAViewModel;  
  14.         }  
  15.   
  16.         public event EventHandler CanExecuteChanged;  
  17.         public bool CanExecute(object parameter) => true;  
  18.   
  19.         public void Execute(object parameter)  
  20.         {  
  21.             if (parameter is null)  
  22.                 return;  
  23.             MessageName messageName = new MessageName  
  24.             {  
  25.                 Name = parameter.ToString()  
  26.             };  
  27.             MSG.Messenger.Default.Send(messageName);  
  28.   
  29.         }  
  30.     }  
  31. }  
In the above class, in the Execute method, we can see that whenever we click the button from ControlAView, it will redirect to the Execute method. The Button content uses the same content in MessageName.Name. We are sending it to ControlBViewModel using Messenger as Messenger.Default.Send(messageName).
 
Therefore, when build execute this syntax, the messenger starts looking for the method, which is registered with this parameter taken as the input, then it will trigger that method. In our case, it will be UpdateContent. So basically, Messenger will work like an Observer design pattern.
 
For your reference, I uploaded the solution.
 
Below is the output snap of all 3 buttons.
 
 

Summary

 
In this article, we learned about messenger and how to register a method and call it from another ViewModel.