.NET & Funky Fresh

Syndication

News

  • <script type="text/javascript" src="http://ws.amazon.com/widgets/q?ServiceVersion=20070822&amp;MarketPlace=US&amp;ID=V20070822/US/bluspiconinc-20/8001/8b68bf4b-6724-40e7-99a5-a6decf6d8648"> </script>
Caliburn.Micro Soup to Nuts Part 8–The EventAggregator

In Caliburn.Micro we have a series of supporting services for building presentation tiers. Among them is the EventAggregator, a service which supports in-process publish/subscribe. There are various implementations of this pattern available in other frameworks, but I think you’ll find that Caliburn.Micro’s implementation sports the cleanest API and the richest set of features. Let’s start by having a look at the IEventAggregator interface:

public interface IEventAggregator {
    void Subscribe(object instance);
    void Unsubscribe(object instance);
    void Publish(object message, Action<System.Action> marshal = null);
}

Below we’ll dig into how the default implementation of this interface (EventAggregator) works.

Subscribe

With the Caliburn.Micro EventAggregator we don’t actually leverage events under the covers. Events are prone to memory leaks and it’s extremely difficult to avoid them or to write a flawless weak event implementation. Instead, we allow you to subscribe an object instance. Under the covers we hold a weak reference. That makes things quite simple. But, how do we know what messages to subscribe to and what methods to use as callbacks? Well, we follow a declarative model by requiring subscribers to implement an interface which states which message they are interested in and provides the callback method. The interface looks like this:

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

And it’s used like this:

public class MyViewModel : IHandle<SomeMessage>{
    public void Handle(SomeMessage message){
        //do something with the message
    }
}

Because we use an interface, you can implement IHandle<T> as many times as you want on a given class. This approach keeps things declarative, strongly-typed and free from memory leaks. You can even use explicit interface implementations to hide the handlers if you desire.

Unsubscribe

There’s not too much to say about this. When you call this method, we search our list of subscribers and remove the specified instance. You may be wondering why this is even necessary if subscribers are held with weak references to begin with. It turns out that there are a number of cases where you want to control the subscription’s activation. Imagine you have a Screen Collection and only the Active Screen is supposed to receive events. In this case, you’d want to subscribe when the screen was activated and unsubscribe when it was deactivated. See the v1.1 HelloWP7 sample for an example of how this works with the new Launcher/Choosers mechanism.

Publish

As you might expect, calling Publish actually sends a message to all it’s subscribers. Here’s how you might use it:

public class MyOtherViewModel{
    IEventAggregator events;

    public MyOtherViewModel(IEventAggregator events){
        this.events = events;
    }

    public void DoSomething(){
        events.Publish(new SomeMessage{
            SomeNumber = 5,
            SomeString = "Blah..."
        });
    }
}

Here, we get an instance of the IEventAggregator service via constructor injection. Then, when the DoSomething method is invoked, we publish the SomeMessage message along with its data. Assuming MyViewMododel above had subscribed itself to the aggregator, it would then have it's Handle method called with the SomeMessage instance. So how does this work? We do this by iterating our list of subscribers to see if any of them implement IHandle<T> where T is assignable from the message that is being published. All the matches that are found then have their handlers called on the UI thread.

Marker Interface

IHandle<T> inherits from a marker interface IHandle. This allows the use of casting to determine if an object instance subscribes to any events. This enables simple auto-subscribing if you integrate with an IoC container. Most IoC containers (including the SimpleContainer) provide a hook for being called when a new instance is created. Simply wire for your container’s callback, inspect the instance being created to see if it implement IHandle, and if it does, call Subscribe on the event aggregator. Just remember, you probably don’t want to use this technique if you need conditional subscription as I described above.

New in v1.1

Custom Publication Marshaling

Those familiar with v1.0 may notice a few interesting differences in the interface. Among them is the optional “marshal” parameter of the Publish method. By default messages will be automatically published synchronously on the UI thread. However, this is not always desirable. The optional parameter allows you to provide an action which marshals the publication to a custom thread. By using this extension point, a developer could easily write extension methods such as: PublishOnCurrentThread, PublishOnBackgroundThread or PublishOnUIThreadAsync.

Polymorphic Event Subscriptions

Above I stated that during publication we check for handlers “where T is assignable from the message being published.” We do this with reflection in v1.1 rather than casting (v1.0) in order to enable polymorphic event subscriptions. Let me explain what this means by providing an example:

public interface ICustomerMessage{
    //implementation here
}

public interface ICustomerCreated : ICustomerMessage{
    //implementation here
}

public interface ICustomerDeleted : ICustomerMessage{
    //implementation here
}

public class AnUnrelatedMessage{
    //implementation here
}

public class MyObserver :
    IHandle<object>, 
    IHandle<ICustomerMessage>, 
    IHandle<ICustomerDeleted> {
    
    public void Handle(object message){
        //will handle every message in the application
    }

    public void Handle(ICustomerMessage message){
        //handles ICustomerCreated and ICustomerDeleted
    }

    public void Handle(ICustomerDeleted message){
        //handles only ICustomerDeleted
    }
}

This turns out to be very powerful, especially when you need to evolve a system over time.

You can see the EventAggregator in action and step through some code by opening up the HelloEventAggregator sample under source. This sample creates a ShellViewModel with two child view models. Each child publishes a message that the other child is subscribed to. The ShellViewModel is subscribed to all messages via the polymorphic features of the aggregator. Here’s a screen shot after some messages have been published:

HelloEventAggregator


Posted 05-30-2011 12:06 PM by Rob Eisenberg

[Advertisement]

About The CodeBetter.Com Blog Network
CodeBetter.Com FAQ

Our Mission

Advertisers should contact Brendan

Subscribe
Google Reader or Homepage

del.icio.us CodeBetter.com Latest Items
Add to My Yahoo!
Subscribe with Bloglines
Subscribe in NewsGator Online
Subscribe with myFeedster
Add to My AOL
Furl CodeBetter.com Latest Items
Subscribe in Rojo

Member Projects
DimeCasts.Net - Derik Whittaker

Friends of Devlicio.us
Red-Gate Tools For SQL and .NET

NDepend

SlickEdit
 
SmartInspect .NET Logging
NGEDIT: ViEmu and Codekana
LiteAccounting.Com
DevExpress
Fixx
NHibernate Profiler
Unfuddle
Balsamiq Mockups
Scrumy
JetBrains - ReSharper
Umbraco
NServiceBus
RavenDb
Web Sequence Diagrams
Ducksboard<-- NEW Friend!

 



Site Copyright © 2007 CodeBetter.Com
Content Copyright Individual Bloggers

 

Community Server (Commercial Edition)