Climb Onboard on the MVVM Message Bus

In a previous post I blogged on various ways to tackle the problem of showing dialogs in an MVVM application. There I advocated using events as a communication mechanism.  The ViewModel fires an event; the View can subscribe to the event and respond in a way that interacts with the user, which could be by displaying a dialog.  It is important to avoid showing dialogs or other visuals directly from the ViewModel in order to maintain the separation of concerns that is the raison d’etre of MVVM and which produces benefits such as better maintainability and testability.

It is possible to use events for communication between View and ViewModel because the View has a direct reference to the ViewModel and can therefore subscribe to events on the ViewModel.  But what about when you want one ViewModel to communicate with another ViewModel?  While you might be tempted to create a direct reference between ViewModels, the result would be a tight coupling that can make the app structure more brittle and harder to maintain.  While this may seem innocent enough at first, over time you can end up with spaghetti code.  To take the pasta analogy a step further, what MVVM tries to do is provide better maintainability by separating the app into layers, like lasagna.  If you have ViewModels referencing one another, you’re introducing spaghetti back into the recipe and will end up with bad case of indigestion.

A classic way to address this problem is by funneling inter-ViewModel communication through a mediator, also called an event aggregator or message bus.  This is why I incorporated a MessageBus class into my Simple MVVM Toolkit.  It has a Register method, which accepts a string token and a method to callback when someone sends a message to the bus using the specified token.  There is also an Unregister method you can call when you no longer wish to receive messages (make sure to call this when doing clean-up in your ViewModel’s Dispose method).  More than one ViewModel can register to receive messages, which are broadcast to subscribers in a fire-and-forget manner.  The Notify method will transparently marshal the call to the UI thread, while BeginNotify will invoke the subscriber’s method on a Thread Pool thread (for non-UI operations).

The payload of a message is the NotificationEventArgs class, which is also used for event-based communication between Views and ViewModels.  The nice thing about this class is that it overloaded constructors that accept a TOutgoing type argument, so you can pass any object in a strongly-typed manner, as well as a TIncoming argument for passing data from the message recipient back to the sender, for loosely-coupled two-way communication via the MessageBus.

A great example where the MessageBus comes in handy is application navigation that is driven by logic in a ViewModel. Typically the MainPage View will contain a navigation Frame element with its Source property bound to a property on its ViewModel that is of type Uri.  When the ViewModel sets this property, displays the desired page in the frame, usually with the assistance of a UriMapper.

nav-app

<navigation:Frame x:Name="ContentFrame" 
    Style="{StaticResource ContentFrameStyle}" 
    Source="{Binding Path=SelectedPage, Mode=TwoWay}" 
    Navigated="ContentFrame_Navigated" 
    NavigationFailed="ContentFrame_NavigationFailed">
    <navigation:Frame.UriMapper>
        <uriMapper:UriMapper>
        <uriMapper:UriMapping Uri="" 
            MappedUri="/Views/Home.xaml"/>
        <uriMapper:UriMapping Uri="/{pageName}" 
            MappedUri="/Views/{pageName}.xaml"/>
        </uriMapper:UriMapper>
    </navigation:Frame.UriMapper>
</navigation:Frame>

Notice the three hyperlink buttons sitting on the main page.  Each has a Command property that is bound to a NavigateCommand property on the ViewModel.  For the CommandParameter each passes the name of a View.  All the Navigate command does is set the SelectedPage property to a Uri constructed using the name of the passed-in page name.

<HyperlinkButton x:Name="customerLink"
    Style="{StaticResource LinkStyle}" 
    Command="{Binding Path=NavigateCommand}"
    CommandParameter="CustomerView"
    TargetName="ContentFrame" Content="customer"/>
private void Navigate(string pageName)
{
    Uri pageUri = new Uri("/" + pageName, UriKind.Relative);
    this.SelectedPage = pageUri;
}

So far, none of this requires communication between two different ViewModels.  But what if you wanted to navigate to a particular page based on logic in a ViewModel shown in the navigation frame.  For example, on the CustomerView you have a Save button, which when clicked will cause the Home page to be displayed in the navigation frame on the main page.  To accomplish this feat you would want to send a message from the CustomerViewModel to the MainPageViewModel with the name of the page to display.  Enter the MessageBus!

public void Save()
{
    MessageBus.Default.Notify(MessageTokens.Navigation, this,
        new NotificationEventArgs(PageNames.Home));
}

The Notify method passes a message token string, which is defined as a constant in the MessageTokens class.  Message senders and subscribers simply agree on the token used to exchange messages.  Also passed in the name of the page we want to navigate to, which in this case is the Home page.

In the constructor of the MainPageViewModel we subscribe to receive messages for the Navigation message token by calling the Register method on the MessageBus and passing both the token and a callback method, which is an EventHandler<NotificationEventArgs>.

public MainPageViewModel()
{
    MessageBus.Default.Register(MessageTokens.Navigation, 
        OnNavigationRequested);
}

void OnNavigationRequested(object sender, NotificationEventArgs e)
{
    Navigate(e.Message);
}

Notice that the callback method simply calls Navigate passing e.Message, which was set by the CustomerViewModel to be the name of the Home page (which happens to be “Home”).  Pretty cool, eh?

So there you have it.  You can download the sample Mvvm Navigation app as part of my Simple Mvvm Toolkit.  Also included is a Messaging sample app that demonstrates multicast and two-way messaging.  Enjoy.

About Tony Sneed

Sr. Software Solutions Architect, Hilti Global Application Software
This entry was posted in Technical and tagged . Bookmark the permalink.

20 Responses to Climb Onboard on the MVVM Message Bus

  1. william simons says:

    Tony, can you use your simple mvvm with authentication, authorization using message bus? I admit I do not know anything about prism mef navigation, silverlight is this possible? Thanks….
    usi

  2. Pingback: Building a Leak-Proof Eventing Model | Tony Sneed's Blog

  3. Tony Sneed says:

    Yes, the way to integrate authentication and authorization into a Silverlight app with Simple MVVM Toolkit is to use the project template called “SimpleMvvmRiaServices,” which uses WCF RIA Services. Here is a nice article on how to do that:

    http://www.silverlightshow.net/items/WCF-RIA-Services-Part-7-Authentication-and-Authorization.aspx

    Cheers,
    Tony

  4. Artin says:

    Hi Tony,

    I’ve been using your toolkit, I wanted to use this approach to support navigation in our application however the MessageBus is marked Internal. Am I doing something wrong?

    • Tony Sneed says:

      The most convenient way to use the MessageBus is to call two methods in the base ViewModel class: SendMessage, RegisterToReceiveMessages. Take a look at the sample app for navigation that comes with the toolkit for an example of using these methods for ViewModel-driven navigation.

      I am planning to make the MessageBus itself public in the next release of the toolkit, so that you will be able to exchange messages from classes that do not derive from ViewModelBase.

      Cheers,
      Tony

  5. Artin says:

    Unfortunately that’s my current situation. We use a Navigation manager outside of the view models. That’s why the source shown in this post was so compeling. I’ll either modify the source or look at a different solution for message. Thanks for your time.

  6. Tony Sneed says:

    No need for that. I’ve already posted the code for Simple MVVM Toolkit that exposes the MessageBus as a public class. 🙂

    http://simplemvvmtoolkit.codeplex.com/SourceControl/changeset/changes/11617

    In the next week I’ll incorporate this into the 2.1 release of the toolkit.

  7. Artin says:

    Thanks Tony,
    With a little bit of effort that worked.

    I wasn’t able to use
    MessageBus.Default.Register(MessageToken.Navigation, OnNavigationRequested);
    void OnNavigationRequested(object sender, NotificationEventArgs e)
    {
    }

    like you showed above however I got the following to work.
    MessageBus.Default.Register(MessageToken.Navigation, [instance of Navigation]);
    public class Navigation : INotifyable
    {
    public void Notify(string token, object sender, NotificationEventArgs e)
    {
    }
    }

    Thanks for all your help.
    Regards
    Artin

  8. tl says:

    FYI, installing simple MVVM from within vs2010 fails with error on finding it.
    it shows it in MS extend dialog, and if you click on download, it then launches browser
    and you get error.
    In MS website, again you get error,
    go to codeplex and grab the exe and all is good,
    just thought you should know.
    I hope this works well, and most important works on Mono for simple MVVM goodness
    in moonlight on linux .
    t

    • Tony Sneed says:

      It should work now. I re-uploaded the toolkit to the Visual Studio Extensions Gallery and tested it. Also, you might want to re-download the toolkit — I discovered and fixed a small bug and included a sample for validation with RIA Services.

  9. Pingback: Commands versus Event Triggers in MVVM | Tony Sneed's Blog

  10. Ray says:

    Can a view use the messagebus register method to receive a message from a viewmodel? I tried to mimic MessageBus.Default.Register(MessageToken.X, OnX);
    void OnX(object sender, NotificationEventArgs e)
    {
    }
    but OnX is not INotifyable?

    • Tony Sneed says:

      For communication between a View and its ViewModel, you should use event notifications instead of the MessageBus. Check out this blog post on the topic: https://blog.tonysneed.com/2011/01/28/tackling-the-problem-of-modal-dialogs-in-mvvm.

      On the other hand, it is possible to send messages between other kinds of classes, for example, between two views, or to and from the Application. For that you have a couple choices. You could implement INotifyable if you want to register to receive messages but don’t want or cannot unregister. This will allow subscribers to be garbage collected and not result in a memory leak. If, however, you don’t mind manually unregistering subscribers, there’s no need to implement INotifyable. Simply, register to receive messages, then unregister when you are no longer receiving messages, probably by implementing IDisposable.

      Cheers!

      • playheavy says:

        With regards to performing messaging between to view models, can you provide a code snippet to achive this communication, as I’m encountering the “cannot convert from method group to INotifiable” error:

        MessageBus.Default.Register(MessageTokens.History.ToString(),
        OnLoadHistory);
        }

        void OnLoadHistory(object sender, NotificationEventArgs e)
        {
        serviceAgent.FetchHistory(
        Guid.Parse(e.Message),
        (data, exception) => FetchHisotryCompleted(data, exception));
        }

        Thanks in advance!

      • Tony Sneed says:

        See the Documentation for the toolkit: http://simplemvvmtoolkit.codeplex.com/wikipage?title=Programming%20Reference.

        Search for “MessageBus” on the page and follow the code sample there. Let me know if you need further assistance.

        Cheers,
        Tony

  11. playheavy says:

    ….
    RegisterToReceiveMessages(MessageTokens.History.ToString(), OnLoadHistory);

    Worked perfectly!

    Thanks Tony

  12. Lev says:

    Hi Tony,

    I have been using your framework for a while, and have just recently run into an issue. I am trying to have multiple viewmodels subscribe to the same token, but run independent methods. The registration seems to work, but when I notify the token, only the first event handler gets triggered, and it gets triggered twice. I looked through the source on the CodePlex site and see that you’re iterating over all the subscribers, but I’ve resorted to using a large number of tokens that all get triggered at the same time. Is there a reason why it would only trip one of the event handlers instead of all of them?

    public class Class1 : ViewModelBase
    {
    public Class1()
    {
    RegisterToReceiveMessages(MyToken, OnMyToken1);
    }

    void OnMyToken1(object sender, NotificationEventArgs args) { }
    }

    public class Class2 : ViewModelBase
    {
    public Class2()
    {
    RegisterToReceiveMessages(MyToken, OnMyToken2);
    }

    void OnMyToken2(object sender, NotificationEventArgs args) { }
    }

    public class Class3
    {
    public void Run()
    {
    MessageBus.Default.BeingNotify(MyToken, this, new NotificationEventArgs ());
    }

    }

    Thanks
    Lev

    • Tony Sneed says:

      On Class3.Run, call MessageBus.Default.Notify instead of BeginNotify. It’s possible you are experiencing threading issues.

      • Lev says:

        I tried calling MessageBus.Default.Notify, but was still unsuccessful. It gets caught in the first handler every time. I should also mention that I am passing in an object derived from ModelBase as a TOutgoing for the Notify. A more complete example would have the Notify as:

        MessageBus.Default.Notify(MyToken, this, new NotificationEventArgs(string.Emtpy, m_typeModel));

        and all of the ViewModels would have

        RegisterToReceiveMessages(MyToken, OnMyToken1);

        and

        OnMyToken1(object sender, NotificationEventArgs e)

        Sorry for omitting those details originally.

      • Tony Sneed says:

        Instead of interacting directly with the MessageBus, you should be calling SendMessage from your sender view model. You also need to use the generic version of NotificationEventArgs.

        SendMessage("MyToken", new NotificationEventArgs(string.Empty, new Customer()));

        In your view models, each handler also needs to be the generic version.

        private void OnMessageReceived1(object sender, NotificationEventArgs eventArgs)
        {
        BannerText = string.Format("Message Received: {0}", eventArgs.Data.CustomerName);
        }
        private void OnMessageReceived2(object sender, NotificationEventArgs eventArgs)
        {
        MessageText = string.Format("Message Received: {0}", eventArgs.Data.CustomerCity);
        }

        Download this sample to see how this works: http://tonysneed.com/download/messagebus-viewmodels.zip There you will see handlers in both view models are called when another view model calls SendMessage, provided the same string is used for token name when each subscriber calls RegisterToReceiveMessages in its constructor. I was able to step through the code and verified that the handlers were properly registered and that both were invoked by the MessageBus when a message was sent to it with the correct token. Please let me know if this addresses your issue.

        Cheers,
        Tony

Leave a comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.