Using MessagingCenter in Xamarin Forms for ViewModel to ViewModel Navigation
When following the MVVM pattern your viewmodels should not communicate with each other directly, meaning no interdependencies. Navigation on the different platforms is from one view to another but we generally want to take control of the navigation structure at viewmodel level when coding cross platform apps (although admittedly, Xamarin Forms does give us more options here...). At its simplest this means that if you want to trigger navigation from one viewmodel to another you need to raise some sort of navigation event in the source view model and subscribe to this in the corresponding view. This event code would then navigate to the target view, which in turn would instantiate the target viewmodel. Simple. But the code can get messy. Most MVVM frameworks provide a message framework which can be used in this situation, and Xamarin Forms is no exception.
This code uses the MessageCenter provided by Xamarin Forms to perform this function. It's not meant to be a complete and standalone working project, but is rather some code snippets to demonstrate how the MessagingCenter can be used for this purpose.
As per the diagram above, the source viewmodel will send a message requesting a particular navigation:
MessagingCenter.Send<NavigationMessage>(new NavigationMessage() { Parameter = someObject }, eNavigationMessage.ShowTargetView.ToString());
A couple of points here. Firstly, you'll need a NavigationMessage class as follows:
public class NavigationMessage
{
public object Parameter { get; set; }
}
Secondly, note that second parameter to the Send method - a string. his can be used to differentiate between different messages of the same message type, so for navigation purposes this would contain things like “ShowCustomerView”, “ShowAccountView”, etc. depending on which view you’re requesting navigation to. Because these strings are used more than once (see the destructor notes below) and because it’s general good practice I’ve used an enumeration as follows:
public enum eNavigationMessage
{
ShowTargetView
}
Your source view will subscribe to this message. When the message is received it will navigate to the target view.
public SourceView()
{
this.BindingContext = ViewModelLocator.SourceViewModel;
InitializeComponent();
}
protected override void OnAppearing()
{
SubscribeToMessages();
base.OnAppearing();
}
private void SubscribeToMessages()
{
MessagingCenter.Subscribe<NavigationMessage>(this, eNavigationMessage.ShowTargetView.ToString(), (navigationMessage) =>
{
try
{
// Perform the actual navigation here
if (navigationMessage.Parameter != null)
{
Navigation.PushAsync(new TargetView(navigationMessage.Paraneter));
}
else
{
Navigation.PushAsync(new TargetView());
}
}
catch(Exception ex)
{
ServiceLocator.ErrorHandlingService.ReportError(ex, "SourceView.NavMessage", navigationMessage);
}
});
}
All views will instantiate their corresponding viewmodel (simple sample shown in the constructor above).
Lastly you’ll need to clean up these message subscriptions, like this:
protected override void OnDisappearing()
{
UnsubscribeFromMessages();
base.OnDisappearing();
}
private void UnsubscribeFromMessages()
{
MessagingCenter.Unsubscribe<NavigationMessage>(this, eNavigationMessage.ShowTargetView.ToString());
}
I’ll try and put up some more posts fairly soon (pending deadlines) which will illustrate the use of this method in the context of an app which provides other functionality.