Using the Event Aggregator Pattern to Communicate Between View Models
Introduction
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.
http://magnusmontin.files.wordpress.com/2014/02/eventaggregator.png?w=515&h=155
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:
|
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:
|
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 article 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.
http://magnusmontin.files.wordpress.com/2014/02/details1.png?w=525&h=123
|
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:
|
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:
|
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 article 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 it to get a reference to the event aggregator when creating the view model classes is straight forward:
|
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