My blog about application development on the .NET platform.

Using the event aggregator pattern to communicate between view models

If you are developing a composite user interface that contains several parts that need to be synchronized with each other, you need to be able to communicate between these different parts of your application.

This can be done using ordinary .NET events or by keeping a direct reference to each subscriber from each publisher class and simply call a method on the subscriber class from the publisher class once you want to publish some information. However, using this approach will result in a tight coupling between publishers and subscribers that makes the application harder to maintain. It could potentially also lead to memory leaks if a publisher of an event lives longer than a subscriber and you forget to, or don’t know when to, unsubscribe from an event.

Event Aggregator Pattern

By introducing an event aggregator in between the publishers and subscribers, you can remove this tight coupling. The subscriber observes the event aggregator instead of the publisher and the publisher knows only about the event aggregator and not about the subscribers.
Event Aggregator
In order to implement this pattern, which is also sometimes referred to as the message bus pattern, both the publishers and the subscribers need a reference to a class that represents the event aggregator. The responsibility of this class is simply to aggregate events. Publishers call publication methods of the event aggregator class to notify subscribers while subscribers call subscription methods to receive notifications.

Although you can implement your own event aggregator class, there is often little reason do to so as most MVVM frameworks out there has an event aggregator or message bus built in.

Prism

Prism – the official guidance for building composite XAML based applications from the Microsoft Patterns and Practices Team – for example ships with a Microsoft.Practices.Prism.Events.EventAggregator class that implements the following interface:

public interface IEventAggregator
{
    // Summary:
    //     Gets an instance of an event type.
    //
    // Type parameters:
    //   TEventType:
    //     The type of event to get.
    //
    // Returns:
    //     An instance of an event object of type TEventType.
    TEventType GetEvent<TEventType>() where TEventType : EventBase, new();
}

To be able to pass an event from a publisher to a subscriber using Prism’s event aggregator, the first thing you need to do is to define the actual type of the event to be passed. An event object is a class that inherits from the abstract Microsoft.Practices.Prism.Events.EventBase class. Prism comes with a Microsoft.Practices.Prism.Events.CompositePresentationEvent<TPayload> class that inherits from this one, where the TPayload type parameter specifies the type of argument that will be passed to the subscribers.

If you for example were to define a class for an event that passes an instance of the Item class shown below between a publisher and subscriber, it could be implemented something like this:

public class ItemSelectedEvent : CompositePresentationEvent<Item>
{
}

public class Item
{
    public int Id { get; set; }
    public string Name { get; set; }
    public double Price { get; set; }
    public int Quantity { get; set; }
}

Now, let’s look a simple code example that uses the above event. Assume that you have a view that lists some items on the left hand side of a screen and another separate view that uses the above event that displays the details of the currently selected item on the right hand side of the same screen. The sample markup code and image in this post is from a WPF desktop application but the same pattern and principles applies to all XAML technologies including WPF, Silverlight, Windows Store Apps and Windows Phone.

Application

<!-- The main window -->
<Window x:Class="Mm.EventAggregator.Prism.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:Mm.EventAggregator.Prism"
        Title="blog.magnusmontin.net" Height="350" Width="525">
    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition/>
            <ColumnDefinition/>
        </Grid.ColumnDefinitions>
        <local:ListView Grid.Column="0"/>
        <local:DetailsView Grid.Column="1"/>
    </Grid>
</Window>

<!-- The list view -->
<UserControl x:Class="Mm.EventAggregator.Prism.ListView"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             xmlns:local="clr-namespace:Mm.EventAggregator.Prism"
             mc:Ignorable="d" 
             d:DesignHeight="300" d:DesignWidth="300">
    <Grid>
        <ListBox ItemsSource="{Binding Items}" 
                 SelectedItem="{Binding SelectedItem}">
            <ListBox.ItemTemplate>
                <DataTemplate>
                    <TextBlock Text="{Binding Name}"/>
                </DataTemplate>
            </ListBox.ItemTemplate>
        </ListBox> 
    </Grid>
</UserControl>

<!-- The details view -->
<UserControl x:Class="Mm.EventAggregator.Prism.DetailsView"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             mc:Ignorable="d" 
             d:DesignHeight="300" d:DesignWidth="300">
    <Grid>
        <ContentControl Content="{Binding Item}">
            <ContentControl.ContentTemplate>
                <DataTemplate>
                    <Grid HorizontalAlignment="Left" VerticalAlignment="Top">
                        <Grid.RowDefinitions>
                            <RowDefinition/>
                            <RowDefinition/>
                            <RowDefinition/>
                            <RowDefinition/>
                        </Grid.RowDefinitions>
                        <Grid.ColumnDefinitions>
                            <ColumnDefinition/>
                            <ColumnDefinition/>
                        </Grid.ColumnDefinitions>
                        <TextBlock Text="Id: " FontWeight="Bold" Grid.Row="0"/>
                        <TextBlock Text="Name: " FontWeight="Bold" Grid.Row="1"/>
                        <TextBlock Text="Price: " FontWeight="Bold" Grid.Row="2"/>
                        <TextBlock Text="Quantity: " FontWeight="Bold" Grid.Row="3"/>
                        <TextBlock Text="{Binding Id}" Grid.Row="0" Grid.Column="1"/>
                        <TextBlock Text="{Binding Name}" Grid.Row="1" Grid.Column="1"/>
                        <TextBlock Text="{Binding Price, StringFormat=c2}" Grid.Row="2" Grid.Column="1"/>
                        <TextBlock Text="{Binding Quantity}" Grid.Row="3" Grid.Column="1"/>
                    </Grid>
                </DataTemplate>
            </ContentControl.ContentTemplate>
        </ContentControl> 
    </Grid>
</UserControl>

Note that the two separate views each have their own separate view model and don’t know anything about each other. When the user selects an item in the list, the ListViewModel class – the DataContext of the ListView view – below uses the event aggregator to publish, or raise, an event that carries the newly selected Item object as payload:

public class ListViewModel
{
    protected readonly IEventAggregator _eventAggregator;
    public ListViewModel(IEventAggregator eventAggregator)
    {
        this._eventAggregator = eventAggregator;
        this.Items = new List<Item>();
        this.Items.Add(new Item { Id = 1, Name = "Item A", Price = 100.00, Quantity = 250 });
        this.Items.Add(new Item { Id = 2, Name = "Item B", Price = 150.00, Quantity = 150 });
        this.Items.Add(new Item { Id = 2, Name = "Item C", Price = 300.00, Quantity = 100 });
    }

    public List<Item> Items { get; private set; }

    private Item _selectedItem;
    public Item SelectedItem
    {
        get { return _selectedItem; }
        set
        {
            _selectedItem = value;

            //Publish event:
            _eventAggregator.GetEvent<ItemSelectedEvent>().Publish(_selectedItem);
        }
    }
}

Any other view model or class in the application can now choose to subscribe to this event by simply calling the Subscribe method for the same event and passing in a callback action to be executed when an event of this type is received. This is exactly what the DetailsViewModel – the DataContext of the details view – class below does. The Action<Item> that is passed in as a parameter to the Subscribe method sets the Item property of the view model to new Item object that is being passed with the ItemSelectedEvent from the ListViewModel:

public class DetailsViewModel : INotifyPropertyChanged
{
    protected readonly IEventAggregator _eventAggregator;
    public DetailsViewModel(IEventAggregator eventAggregator)
    {
        this._eventAggregator = eventAggregator;
        this._eventAggregator.GetEvent<ItemSelectedEvent>()
            .Subscribe((item) => { this.Item = item; });
    }

    private Item _item;
    public Item Item
    {
        get { return _item; }
        set { _item = value; NotifyPropertyChanged(); }
    }

    public event PropertyChangedEventHandler PropertyChanged;
    protected void NotifyPropertyChanged([CallerMemberName] string propertyName = "")
    {
        if (PropertyChanged != null)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }
    }
}

There are also overloads of the Subscribe method that lets you specify on which thread you want to receive the events and supply a delegate that filters the event before the registered handler is called. See the “Event Aggregator” link below for more information about this.

Note that the DetailsViewModel class implements the INotifyPropertyChanged interface and raises its PropertyChangedEvent once the Item property gets in order for the details of the newly selected item to be automatically reflected in the details view.

Also note that the publisher (ListViewModel) and the subscriber (DetailsViewModel) must call the Publish and Subscribe methods of the very same EventAggregator object respectively for the communication between them to work. You will typically only have a single instance of an event aggregator per application. In a real-world composite application, an IoC container, such as for example Microsoft’s Unity, is often used to create and resolve the instance of this one.

If you want to be able to compile and run the sample code that is presented in this post without using an IoC container, you can use the following ApplicationService singleton class that has a property which returns an EventAggregator object to be used by all view models:

using Prism = Microsoft.Practices.Prism.Events;
internal sealed class ApplicationService
{
    private ApplicationService() { }

    private static readonly ApplicationService _instance = new ApplicationService();

    internal static ApplicationService Instance { get { return _instance; } }

    private Prism.IEventAggregator _eventAggregator;
    internal Prism.IEventAggregator EventAggregator
    {
        get
        {
            if (_eventAggregator == null)
                _eventAggregator = new Prism.EventAggregator();

            return _eventAggregator;
        }
    }
}

Using it to get a reference to the event aggregator when creating the view model classes is straight forward:

public partial class ListView : UserControl
{
    public ListView()
    {
        InitializeComponent();
        this.DataContext = new ListViewModel(ApplicationService.Instance.EventAggregator);
    }
}

public partial class DetailsView : UserControl
{
    public DetailsView()
    {
        InitializeComponent();
        this.DataContext = new DetailsViewModel(ApplicationService.Instance.EventAggregator);
    }
}

Summary

To summarize, using an event aggregator to communicate between components in an application is a good choice when you want to reduce the complexity that comes with registering and subscribing to multiple events from a lot of different client or subscriber classes. It can greatly simplify memory management and decrease dependencies between your classes and modules.

The decoupling between publishers and subscribers of events enables you to easily add new classes or modules that can respond to events without changing a single line of code in the publisher classes.

See also

Prism: http://msdn.microsoft.com/en-us/library/ff648465.aspx
Download Prism: http://compositewpf.codeplex.com/
Event Aggregator http://msdn.microsoft.com/en-us/library/ff921122.aspx
IOC Containers and MVVM http://msdn.microsoft.com/en-us/magazine/jj991965.aspx
Setting Up the Unity Container http://msdn.microsoft.com/en-us/library/ff648211.aspx


8 Comments on “Using the event aggregator pattern to communicate between view models”

  1. Emilio says:

    Do you know if mvvm light Messenger class is using event aggregator?

  2. Albert Li says:

    Very simple, beautiful article!

    There is a small error, in ListView, the binding of selecteditem should be “SelectedItem=”{Binding SelectedItem, Mode=TwoWay}”>

    I tested with SilverLight 5, works great! Thank You!

  3. haiyun592 says:

    This is one of the best articles about Event Aggregator in the MVVM programming!

    There is one small error, in ListView, it should be SelectedItem=”{Binding SelectedItem, Mode=TwoWay}”

    Thank You!

  4. Hi,

    The SelectedItem dependency property of the ListBox binds two-way by default so this is not an error.

  5. Can we download the source?

  6. Vikram says:

    Thanks a lot for the article. It is crisp clear.

  7. constantin says:

    Great article, I recommend placing a note regarding the replacement of CompositeEvent by PubSubEvent in Prism 6

  8. KSA PHANI BHUSHAN says:

    Thank you for a great article.


Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s