Posterous theme by Cory Watilo

Windows Forms Eventing: Generic Pub/Sub

In my last post, I covered how you can implement a publish/subscribe system for sending events between components of your Windows Forms application. Using this model, it is easy to create separate components that don’t rely on each other’s implementation details in order to provide a consistent experience for the user. A button on the toolbar can be disabled by an action on the data entry screen…without hard references between the two.

However, the implementation from last time required a new aggregator and set of interfaces for each message type that needed to be passed around. Let’s fix that with generics.

Listener Interface

First we replace the IEventReciever interface from last time with a generic IListener interface that uses a generic type for the message object.

public interface IListener<T>
{
void Handle(T message);
}
All your subscribers now implement an IListener<Message> for each message type they can handle. (This is great because one class can listen for multiple types of messages!)

Event Aggregator Interface

Similarly, the Event Aggregator needs a generics reset. Notice that the Add and Remove methods now accept any old object.

public interface IEventAggregator
{
void SendMessage<T>(T message);
void AddListener(object listener);
void RemoveListener(object listener);
}

Event Aggregator

public class EventAggregator
: IEventAggregator
{
private readonly List<object>
listeners = new List<object>();

#region IEventAggregator Members

public void SendMessage<T>(T
message)
{
listeners.CallOnEach<IListener<T>>(x => { x.Handle(message); });
}

public void AddListener(object listener)
{
if (listeners.Contains(listener)) return;
listeners.Add(listener);
}

public void RemoveListener(object listener)
{
listeners.Remove(listener);
}

#endregion

}

Not much new here….except for that CallOnEach method. Where did that come from?

Extension Methods

We need to add a few utility methods to the IEnumerables so that we can send our messages:

public static void CallOnEach<T>(this IEnumerable
enumerable, Action<T> action) where T : class

{
foreach (object o in enumerable)
{
o.CallOn(action);
}
}

public static void CallOn<T>(this object target,
Action<T> action) where T : class

{
var subject = target as T;
if (subject != null)
{
action(subject);
}
}

Put those in a likely static class somewhere.

Are we there yet?

This Event Aggregator is getting powerful. We should kill it before it develops language skills.

With the code we have so far, we can easily create a new message in the system without modifying the aggregator code. I like adding the methods as child classes of their receivers (when they are receiver-specific).

There is, however, still a problem. In the type of application that needs this eventing system, you are likely going to need to do some background threading to keep the UI responsive. The Event Aggregator we have doesn’t do anything to keep itself or the rest of the application synchronized.

What happens when a background thread sends a message that the receiver needs to act on by talking to the UI thread? Do you write a bunch of Invoke() code everywhere?

As it turns out, there is a better way. And my next post will show you how to upgrade your EventAggregator to be the thread master!