Monday, November 1, 2010

Creating a loosely coupled event publish/ subscribe framework for Silverlight

Applications need a loosely coupled event publishing and subscribing mechanism for communication between entities that don't have a direct reference to each other. In this post I will show how to create an Event aggregator model that can be used in our applications. The sample that is used in this post is from a Silverlight application I was working with. The EventManager class is responsible for registration and invoking of events based on a publish/ subscribe pattern.


public class EventManager
{
    public static void Subscribe(ViewModelBase reciever, string key, Action action)
    {
        EventHolder newEventHolder = new EventHolder(reciever, action);
        var eventHoldersForEventId = Singleton<List<KeyValuePair<string, List>>>>.Instance;

        if (eventHoldersForEventId.Any(x => x.Key == key))
        {
            var keyValuePair = eventHoldersForEventId.First(x => x.Key == key);
            var eventHolder = keyValuePair.Value.FirstOrDefault(x => x.ViewModel.ToString() == reciever.ToString() && x.InvokeEvent == action);
            if (eventHolder == default(EventHolder))
                keyValuePair.Value.Add(new EventHolder(reciever, action));
        }
        else
        {
            eventHoldersForEventId.Add(new KeyValuePair<string, List>>(key, new List>()));
            var KeyValuePair = eventHoldersForEventId.First(x => x.Key == key);
            KeyValuePair.Value.Add(newEventHolder);
        }
    }

    public static void Publish(string key, T data)
    {
        var eventHoldersForEventId = Singleton<List<KeyValuePair<string, List>>>>.Instance;

        if (eventHoldersForEventId.Any(x => x.Key == key))
        {
            var keyValuePair = eventHoldersForEventId.First(x => x.Key == key);
            var eventHolderCollection = keyValuePair.Value;
            eventHolderCollection.ForEach(x => x.InvokeEvent.Invoke(data));
        }
    }
}

The objects that are interested in listening to events can register themselves using a Subscribe method.
The subscribe method needs an instance of the object and a delegate to invoke when the event occurs.
Publish method is used by objects to notify an event.
public class EventHolder
    {
        public EventHolder(ViewModelBase viewModel, Action action)
        {
            ViewModel = viewModel;
            InvokeEvent = action;
        }

        public ViewModelBase ViewModel { get; private set; }
        public Action InvokeEvent { get; private set; }
    }

    public class EventHolder
    {
        public EventHolder(ViewModelBase viewModel, Action action)
        {
            ViewModel = viewModel;
            InvokeEvent = action;
        }

        public ViewModelBase ViewModel { get; private set; }
        public Action InvokeEvent { get; private set; }
    }
}

Events can be registered or subscribed by using
private void SubscribeToEvent()
{
    //TargetMessenger.Subscribe(this, (x) => _projectId = x);
           
    EventManager.Subscribe<int>(this, EventTopics.PUBLISH_PROJECTID_TO_MANAGEUSER, (data) =>
    {
        _projectId = data;
    });
}

Event publication is done as
EventManager.Publish<int>(EventTopics.PUBLISH_PROJECTID_TO_MANAGEUSER, projectId);

Singleton is a generic class for creating singleton objects
public class Singleton where T : new()
{
    Singleton() { }

    public static T Instance
    {
        get
        {
            if(_initializer == default(Lazy)) _initializer = new Lazy();
            return _initializer.Value;
        }
    }
    static Lazy _initializer = default(Lazy);
}

2 comments:

Fernando said...

Won't this be better with weak referenced events for garbage collector?

Prajeesh Prathap said...

Hi Broker,
Thanks for pointing that out, I'll come up with the changes that uses weak events shortly