Using EventAggregator with MEF


Update (4/18/2010): I’ve created another sample covering Silverlight 4 scenario. Event Aggregator was successfully ported to Silverlight 4 and marked with ExportAttribute to be able importing it without additional implications. You can find sample here.

Recently I was seeking for a possibility making MEF parts to respond host application as well as neighbouring parts events in a weak way. Actually I was looking for some sort of Event Broker similar to that coming as a Unity Sample (Enterprise Library 4.x). For the moment MEF (MEF 4 preview) does not support importing and exporting events or multicast delegates so custom solutions are required in order to accomplish such task. From one side I didn’t want mixing MEF with Unity/ObjectBuilder in order to inject event publications/subscriptions. From the other side I didn’t want mixing MEF with Composite WPF (former Prism) though I like their awesome EventAggregator very much.

After analysing CompositeWPF sources I found out that EventAggregator is presented by a completely isolated part having no dependencies on the rest of the library. So it means that it can be easily used outside the assembly without any implications. This was something I needed very much.

Extracting EventAggregator into a separate assembly takes a couple of minutes assuming that you have downloaded the sources. Implementing a simple MEF-powered application also takes a couple of minutes. MEF project got a useful set of samples within their wiki. Sample application demonstrating the use of EventAggregator with MEF as long as the extracted EventAggregator code can be found at the end of this article…

Small walkthrough

I will show how a simple user message can be broadcasted by an application and processed by a control that was imported from a MEF Catalog and injected into the main application layout

1. Preparing a simple message event

First of all you need to define a message format that will be sent to external components. It can be defined by the following class:

  public class UserMessage
  {
    public string Text { get; set; }

    public UserMessage(string text)
    {
      this.Text = text;
    }
  }

Next you have to define an event body that external components will be subsribing to. Hosting application will also use it for raising a message event:

   public sealed class UserMessagePostedEvent
    : CompositePresentationEvent<UserMessage>
  {
  }

You don’t need providing additional memebers to this class as CompositePresentaionEvent<T> already contains everything needed.

2. Preparing a simple event consumer

Now you need to define a MEF part that will be consuming user message events raised by hosting application or neighbours. Let’s turn a standard WPF TextBox into a MEF part:

  [Export("EventViewer")]
  public class EventViewer : TextBox, INotifyImportSatisfaction
  {
    [Import]
    private IEventAggregator eventAggregator;

    private SubscriptionToken subscriptionToken;

    #region INotifyImportSatisfaction Members

    public void ImportCompleted()
    {
      if (eventAggregator != null)
      {
        UserMessagePostedEvent messagePostedEvent =
          eventAggregator.GetEvent<UserMessagePostedEvent>();

        if (subscriptionToken != null)
          messagePostedEvent.Unsubscribe(subscriptionToken);

        subscriptionToken = messagePostedEvent.Subscribe(
          OnUserMessagePosted,
          ThreadOption.UIThread,
          false);
      }
    }

    private void OnUserMessagePosted(UserMessage message)
    {
      this.Text += Environment.NewLine + message.Text;
    }

    #endregion
  }

Note that Textbox called EventViewer imports EventAggregator via its IEventAggregator interface. Next on ImportCompleted event it creates a subscription token for UserMessagePostedEvent you’ve created earlier. After that subcription is wired with OnUserMessagePosted handler that will add received message text to a TextBox text.

3. Preparing a Window layout

Now you need creating some debugging layout to ensure application can raise events and imported parts can handle and react on messages. Here’s the simple xaml for the testing window:


<Window
  x:Class="EventAggregatorForMEF.Window1"
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  Title="EventAggregator for MEF" Height="600" Width="800">

  <Grid Name="LayoutRoot">
    <Grid.RowDefinitions>
      <RowDefinition Height="*"/>
      <RowDefinition Height="Auto"/>
    </Grid.RowDefinitions>
    <Button Name="SendMessage"
            Grid.Row="1"
            Content="Send message"
            Click="SendMessage_Click"
            MouseEnter="SendMessage_MouseEnter"
            MouseLeave="SendMessage_MouseLeave"
            />
  </Grid>

</Window>

Testing layout is represented by a standard WPF Grid panel. Panel consists of two rows. First row occupies the most of the free space (Height=”*”) and the second row takes the Height enough to display a “Send Message” button. First row in this case serves a placeholder for the control being imported from MEF catalog.

Additionally “Send Message” button will raise three events “Click”, “MouseEnter”, “MouseLeave” that will take part in the testing process.

4. Code behind

  public partial class Window1
    : Window, INotifyImportSatisfaction
  {
    CompositionContainer _container;
    IEventAggregator eventAggregator = new EventAggregator();

    [Import("EventViewer")]
    private TextBox viewer = null;

    public Window1()
    {
      InitializeComponent();
      Compose();
      this.Loaded += new RoutedEventHandler(Window1_Loaded);
    }

    private void Compose()
    {
      _container = new CompositionContainer(
        new AssemblyCatalog(Assembly.GetExecutingAssembly()));

      CompositionBatch batch = new CompositionBatch();
      batch.AddPart(this);
      batch.AddExportedObject<IEventAggregator>(eventAggregator);

      _container.Compose(batch);
    }

    void Window1_Loaded(object sender, RoutedEventArgs e)
    {
      PostUserMessage("Window loaded...");
    }

    #region INotifyImportSatisfaction Members

    public void ImportCompleted()
    {
      if (viewer != null)
      {
        this.LayoutRoot.Children.Insert(0, viewer);
        Grid.SetRow(viewer, 0);
      }
    }

    #endregion

    void ButtonClick(object sender, RoutedEventArgs e)
    {
      PostUserMessage("Button clicked...");
    }

    void ButtonMouseEnter(object sender, MouseEventArgs e)
    {
      PostUserMessage("Mouse entered button..");
    }

    void ButtonMouseLeave(object sender, MouseEventArgs e)
    {
      PostUserMessage("Mouse left button..");
    }

    void PostUserMessage(string text)
    {
      eventAggregator
        .GetEvent<UserMessagePostedEvent>()
        .Publish(new UserMessage(text));
    }
  }

First you initialize the CompositionContainer (for more details on CompositionContainer please refer the MEF’s Programming Guide). Also you insert EventAggregator into the container as an IEventAggregator export and declare a placeholder variable for importing an “EventViewer” external part (as a TextBox).

Upon InsertCompleted event the imported Event Viewer control is injected into the main window layout (first row that occupies most of the free space).

Finally I’ve declared three event handlers for the button control that raise a simple event by means of a helper method called PostUserMessage.

After running an application you will be able to see that external part successfully wires the EventAggregator events and is able processing messages send by external components.

WPF sample with EventAggregator assembly can be downloaded here

About these ads

8 thoughts on “Using EventAggregator with MEF

  1. Denis, instead of manually registering EventAggregator, you can simple have it in the catalog and let MEF construct it. Add an [Export(typeof(IEventAggregator))] to the class and put it in an assembly. You can then use the aggregate catalog when you construct the container and pass in an assembly catalog that references your EventAggregator assembly, and you can add to it your executing assembly.

    Example:

    var catalog = new AggregateCatalog();
    catalog.Add(new AssemblyCatalog(typeof(EventAggregator).Assembly));
    catalog.Add(new AssemblyCatalog(Assembly.GetExecutingAssembly());

    you can also avoid the direct reference to the EventAggregator assemby (and only reference the interface assembly) by using a DirectoryPartCatalog and passing a folder name that contains the binary.

  2. Pingback: Event Aggregation with MEF (with and without EventAggregator) - Glenn Block
  3. Pingback: Event Aggregation with MEF (with and without EventAggregator) - Glenn Block
  4. Pingback: Use Event Aggregator to make your application more extensible - Kazi Manzur Rashid's Blog
  5. Pingback: Michał Jaskólski » Agregacja zdarzeń w .NET
  6. Pingback: Michał Jaskólski » Agregacja zdarzeń w .NET

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