The Event Aggregator

Caliburn.Micro comes pre-bundled with an Event Aggregator, conveniently called EventAggregator. For those unfamiliar, an Event Aggregator is a service that provides the ability to publish an object from one entity to another in a loosely based fashion. Event Aggregator is actually a pattern and it's implementation can vary from framework to framework. For Caliburn.Micro we focused on making our Event Aggregator implementation simple to use without sacrificing features or flexibility.

Getting Started

As previously mentioned we provide an implementation of Event Aggregator for you. This implementation implements the IEventAggregator interface, however, you can provide your own implementation if required. Please take a moment to familiarize yourself with the IEventAggregator signature.

    public interface IEventAggregator {
    	Action<Action> PublicationThreadMarshaller { get; set; }
    	bool HandlerExistsFor(Type messageType);
    	void Subscribe(object subscriber);
    	void Unsubscribe(object subscriber);
    	void Publish(object message);
    	void Publish(object message, Action<Action> marshal);
    }

Creation and Lifecycle

To use the EventAggregator correctly it must exist as an application level service. This is usually achieved by creating an instance of EventAggregator as a Singleton. We recommend that you use Dependency Injection to obtain a reference to the instance although we do not enforce this. The sample below details how to create an instance of EventAggregator, add it to the IoC container included with Caliburn.Micro (although you are free to use any container you wish) and request it in a ViewModel.

    // Creating the EventAggregator as a singleton.
    public class Bootstrapper : BootstrapperBase {
        private readonly SimpleContainer _container =
            new SimpleContainer();

         // ... Other Bootstrapper Config

        protected override void Configure(){
            _container.Singleton<IEventAggregator, EventAggregator>();
        }

        // ... Other Bootstrapper Config
    }

    // Acquiring the EventAggregator in a viewModel.
    public class FooViewModel {
        private readonly IEventAggregator _eventAggregator;

        public FooViewModel(IEventAggregator eventAggregator) {
            _eventAggregator = eventAggregator;
        }
    }

Note that we are utilizing the Bootstrapper, and specifically the Configure method, in the code above. There is no requirement to wire up the EventAggregator in a specific location, simply ensure it is created before it is first requested.

Publishing Events

Once you have obtained a reference to the EventAggregator instance you are free to begin publishing Events. An Event or message as we call it to distinguish between .Net events, can be any object you like. There is no requirement to build your Events in any specific fashion. As you can see in the sample below the Publish method can accept any entity that derives from System.Object and will happily publish it to any interested subscribers.

    public class FooViewModel {
        private readonly IEventAggregator _eventAggregator;

        public FooViewModel(IEventAggregator eventAggregator) {
            _eventAggregator = eventAggregator;

            _eventAggregator.Publish(new object());
            _eventAggregator.Publish("Hello World");
            _eventAggregator.Publish(22);
        }
    }

Publishing using a custom thread Marshal

By convention, the EventAggregator publishes on the UI thread. You can override this in two places, globally and per publish. Consider the following code below which publishes the message supplied on a background thread.

    _eventAggregator.Publish(new object(), action => {
                    Task.Factory.StartNew(action);
                });

You can also swap out the default marshaller globally.

    _eventAggregator.PublicationThreadMarshaller = action => { action(); };

Subscribing To Events

Any entity can subscribe to any Event by providing itself to the EventAggregator via the Subscribe method. To keep this functionality easy to use we provide a special interface (IHandle<T>) which marks a subscriber as interested in an Event of a given type.

    IHandle<TMessage> : IHandle {
	    void Handle<TMessage>(TMessage message);
    }

Notice that by implementing the interface above you are forced to implement the method Handle(T message) were T is the type of message you have specified your interest in. This method is what will be called when a matching Event type is published.

    public class FooViewModel : IHandle<object> {
        private readonly IEventAggregator _eventAggregator;

        public FooViewModel(IEventAggregator eventAggregator) {
            _eventAggregator = eventAggregator;
            _eventAggregator.Subscribe(this);
        }

        public void Handle(object message) {
            // Handle the message here.
        }
    }

You may be wondering about IHandle<T> implementing IHandle. IHandle is a Marker Interface used internally to assist with reflecting over various IHandle Implementations, you do not need to work with this interface directly.

Subscribing To Many Events

It is not uncommon for a single entity to want to listen for multiple event types. Because of our use of generics this is as simple as adding a second IHandle<T> interface to the subscriber. Notice that Handle method is now overloaded with the new Event type.

    public class FooViewModel : IHandle<string>, IHandle<bool> {
        private readonly IEventAggregator _eventAggregator;
    
        public FooViewModel(IEventAggregator eventAggregator) {
            _eventAggregator = eventAggregator;
            _eventAggregator.Subscribe(this);
        }
    
        public void Handle(string message) {
            // Handle the message here.
        }

        public void Handle(bool message) {
            // Handle the message here.
        }
    }

Polymorphic Subscribers

Caliburn.Micro's EventAggregator honors polymorphism. When selecting handlers to call, the EventAggregator will fire any handler who's Event type is assignable from the Event being sent. This results in a lot of flexibility and helps reuse.

    public class FooViewModel : IHandle<object>, IHandle<string> {
        private readonly IEventAggregator _eventAggregator;
    
        public FooViewModel(IEventAggregator eventAggregator) {
            _eventAggregator = eventAggregator;
            _eventAggregator.Subscribe(this);
            _eventAggregator.Publish("Hello");
        }
    
        public void Handle(object message) {
            // This will be called
        }

        public void Handle(string message) {
            // This also
        }
    }

In the example above, because String is derived from System.Object both handlers will be called when a String message is published.

Querying Handlers

When a subscriber is passed to the EventAggregator it is broken down into a special object called a Handler and a weak reference is maintained. We provide a mechanism to query the EventAggregator to see if a given Event type has any handlers, this can be useful in specific scenarios were at least one handler is assumed to exist.

    public class FooViewModel : IHandle<object> {
        private readonly IEventAggregator _eventAggregator;

        public FooViewModel(IEventAggregator eventAggregator) {
            _eventAggregator = eventAggregator;
            _eventAggregator.Subscribe(this);
        }

        public void Handle(object message){
            if (_eventAggregator.HandlerExistsFor(typeof(SpecialMessageEvent))){
                _eventAggregator.Publish(new SpecialEventMessage(message));
            }
        }
    }

Coroutine Aware Subscribers

If you are using the EventAggregator with Caliburn.Micro as opposed to on it's own via Nuget, access to Coroutine support within the Event Aggregator becomes available. Coroutines are supported via the IHandleWithCoroutine<T> Interface.

    public interface IHandleWithCoroutine<TMessage> : IHandle {
		IEnumerable<IResult> Handle(TMessage message);
    }

The code below utilizes Coroutines with the EventAggregator. In this instance Activate will be fired asynchronously, Do work however, will not be called until after Activate has completed.

    public class FooViewModel : Screen, IHandleWithCoroutine<EventWithCoroutine> {
        private readonly IEventAggregator _eventAggregator;

        public FooViewModel(IEventAggregator eventAggregator) {
            _eventAggregator = eventAggregator;
            _eventAggregator.Subscribe(this);
        }

        public IEnumerable<IResult> Handle(EventWithCoroutine message) {
            yield return message.Activate();
            yield return message.DoWork();
        }
    }

    public class EventWithCoroutine {
        public IResult Activate() {
            return new TaskResult(Task.Factory.StartNew(() => {
                    // Activate logic
                }));
        }

        public IResult DoWork() {
            return new TaskResult(Task.Factory.StartNew(() => {
                // Do work logic
            }));
        }
    }

Task Aware Subscribers

Caliburn.Micro also provides support for task based subscribers where the asynchronous functionality of Coroutines is desired but in a more light weight fashion. To utilize this functionality implement the IHandleWithTask<T> Interface, seen below.

    public interface IHandleWithTask<TMessage> : IHandle {
        Task Handle(TMessage message);
    }

Any subscribers that implement the above interface can then handle events in Task based manner.

    public class FooViewModel : Screen, IHandleWithTask<object> {
        private readonly IEventAggregator _eventAggregator;

        public FooViewModel(IEventAggregator eventAggregator) {
            _eventAggregator = eventAggregator;
            _eventAggregator.Subscribe(this);
        }

        public Task Handle(object message) {
            return Task.Factory.StartNew(() => message);
        }
    }

Unsubscribing and Leaks

The problem with standard .Net events is that they are prone to memory leaks. We avoid this situation by maintaining a weak reference to subscribers. If the only thing that references a subscriber is the EventAggregator then it will be allowed to go out of scope and ultimately be garbage collected. However, we still provide an explicit way to unsubscribe to allow for conditional handling as seen below.

    public class FooViewModel : Screen, IHandle<object> {
        private readonly IEventAggregator _eventAggregator;

        public FooViewModel(IEventAggregator eventAggregator) {
            _eventAggregator = eventAggregator;
            _eventAggregator.Subscribe(this);
        }

        public void Handle(object message) {
            // Handle the message here.
        }

        protected override void OnActivate() {
            _eventAggregator.Subscribe(this);
            base.OnActivate();
        }

        protected override void OnDeactivate(bool close) {
            _eventAggregator.Unsubscribe(this);
            base.OnDeactivate(close);
        }
    }

In the code above Screen is used to expose lifecycle events on the ViewModel. More on this can be found on this in the Screens, Conductors and Composition article on this wiki.

Custom Result Handling

In more complex scenarios it may be required to override the default handling of Handlers which have results. In this instance it is possible to swap out the existing implementation in favour of your own. First we create a new handler type.

    IHandleAndReturnString<T> : IHandle {
	    string Handle<T>(T message);
    }

Next we create our new result processor. This can be configured in the bootstrapper.

    var standardResultProcesser = EventAggregator.HandlerResultProcessing;
    EventAggregator.HandlerResultProcessing = (target, result) =>
    {
        var stringResult = result as string;
        if (stringResult != null)
            MessageBox.Show(stringResult);
        else
            standardResultProcesser(target, result);
    };

Now, any time an event is processed returns a string, it will be captured and displayed in a MessageBox. The new handler falls back to the default implementations in cases were the result is not assignable from string. It is extremely important to note however, this feature was not designed for request / response usage, treating it as such will most definitely create bottle necks on publish.

Last edited May 13 at 8:36 AM by McDonnellDean, version 10

Comments

kinl99 Jan 11 at 7:16 AM 
Custom Publication Marshaling:

Could you please go a little more into detail on how to use your EventAggregator in a multithreaded app? Atm I'm going with e.g.:

System.Windows.Application.Current.Dispatcher.Invoke((Action)(() =>
{
eventAggregator.Publish(new MVVM_AddOns.EventArguments.AddLogEntryEventArgs(this)
{
logText = "log Text"
});
}));

to post something back to the UI Thread. But at some points it does not happen at all (wrong Dispatcher I think...) so I'm looking for the way it's meant to be implemented.

Thanks

swordfish Dec 2, 2012 at 9:00 PM 
I thought that the whole point of the event aggregator pattern was to decouple listners from publishers. In a proper decoupled system, how does the publisher know if a message should be published on the UI thread, HTTP thread, of any other thread?

To me, it makes more sense that a listener should perform the marshaling is a certain thread is required. If an marshalAction isn't provided, one could assume the current thread of the listener (SynchronizationContext.Current.Post()).

WinRT has some application life cycle and presentation events that I would like to 'forward' to the system. Some of the events are processed by services (processes running on a background track) and others by the UI ViewModels. These events are published on the background thread and propagated to the rest of the application.

I haven't look into Windows 8 development that deep, so it's possible that the whole application is running on the UI thread, but for now I assume it's not..

stooboo Dec 12, 2011 at 4:30 PM 
Hi buckley

In your example you're publishing your instance of Publisher,
....I think most people are creating classes specifically for the event i.e.

instead of
_eventAggregator.Publish(this);
it would be
_eventAggregator.Publish(New SomethingHappenedEvent("something");

it keeps the 'single responsibility'

cheers
Stu

buckley Dec 12, 2011 at 3:16 PM 
A simple put complete example. It wasn't immediately obvious how the consumer subscribed from the documentation.
I used the EA from MVVMLight in the past and there is a static instance (default instance or something) available so that you don't have to pass it around. You can write this yourself with CM but I think the author prefers it being injected as a singleton with an IoC (?).


using System;
using Caliburn.Micro;

namespace EventAggregation
{
internal class Program
{
private static void Main()
{
EventAggregator eventAggregator = new EventAggregator();

new Subscriber(eventAggregator);

var publisher = new Publisher(eventAggregator);

publisher.SomethingHapened();

Console.ReadKey();
}
}


public class Subscriber : IHandle<Publisher>
{
public Subscriber(IEventAggregator eventAggregator)
{
eventAggregator.Subscribe(this);
}

#region IHandle<Publisher> Members

public void Handle(Publisher message)
{
Console.WriteLine(message.AString);
}

#endregion
}

public class Publisher
{
private readonly EventAggregator _eventAggregator;
public string AString = "something";

public Publisher(EventAggregator eventAggregator)
{
_eventAggregator = eventAggregator;
}

public void SomethingHapened()
{
_eventAggregator.Publish(this);
}
}
}