While working with SilverLight, I frequently use the PRiSM library to support MVVM & modularity. One of the key component is supporting modularity is the Event Aggregator.
As per Martin Fowler “An Event Aggregator acts as a single source of events for many objects. It registers for all the events of the many objects allowing clients to register with just the aggregator.”
According to the PRiSM documentation:
“The EventAggregator service is primarily a container for events that allow decoupling of publishers and subscribers so they can evolve independently.”
Because the PRiSM library is released under the MS-PL, I can copy and modify it according to my need. The PRiSM Event Aggregator is used for WPF & SilverLight applications and some of the references are specific for those platforms.
To use the Event Aggregator in non WPF / SilverLight applications I isolated the required code, removed the WPF / SilverLight dependencies and built a separate assembly which can be downloaded below.
Included in the download, is a small console demo application which will be explained below.
The demo application we will have multiple Person instances which will communicate via the Event Aggregator. The Person object will consist of a property ‘Name’ to identify the current person instance and two public methods ‘Shout’ & ‘Whisper’. Both methods will publish the conversation via the Event Aggregator, only the Whisper methods will include an destination person for which the message meant.
In the constructor of Person, the name and the Event Aggregator will be injected to make the conversation possible.
For the two different types of communication (shout and whisper) we have to different classes (the LoudConversation and WhisperConversation class)which both inherit from the base class Conversation.
The base class Conversation contains the source person which communicated, and the text which got sent. The whisperConversation also contains an destination Person for which person instance the message is meant.
Besides getting the Event Aggregator injected, the person is subscribing to two different events ‘LoudConversationEvent’ and ‘WhisperConversationEvent’, both inherit from CompositePresentationEvent which defines the class that manages publication and subscription to events.
1: public Person(String name, IEventAggregator eventAggregator)
2: {
3: Name = name;
4: _eventAggregator = eventAggregator;
5: _eventAggregator.GetEvent<LoudConversationEvent>().Subscribe(Listen, ThreadOption.BackgroundThread, true, p => !p.Source.Equals(this));
6: _eventAggregator.GetEvent<WhisperConversationEvent>().Subscribe(Listen, ThreadOption.BackgroundThread, true, p => !p.Source.Equals(this) && p.Destination.Equals(this));
7: }
8:
9: private void Listen(Conversation conversation)
10: {
11: Console.WriteLine("{0}: Listened to '{1}': {2} ", this.Name, conversation.Source.Name, conversation.Text);
12: }
The CompositePresentationEvent is a generic class which encapsulates the LoudConversation and WhisperConversation class.
1: public class LoudConversationEvent : CompositePresentationEvent<LoudConversation>{}
2: public class WhisperConversationEvent : CompositePresentationEvent<WhisperConversation> { }
This subscription setup in the constructor of Person .will tell the event aggregator that the specific instance is interested in an certain type of event and that when the event arrives it should be delegated to the private method ‘Listen’. When subscribing to the an specific event you can optionally tell the Event Aggregator on which thread the delegated event should be handled by setting the ThreadOption (in my example, it should be handled on the background thread). The second parameter you can specify if the subscription should stay alive after a event has been received (in my case is is set to true, which is the default setting). The last optional parameter is a predicate where you can filter out certain type of events in which you are not interested in.
In the above example, I am only interested in the LoudConversationEvent when I am not the source of the message. That is pretty logical because why would I want to listen to something which I am broadcasting.
In the WhisperConversationEvent I am only interested when I am not the sender and where I am receiver specified by the Destination property of the WhisperConversation class. This doesn’t guarantee you that other instances which also subscribe to the above events will implement the exact same predicates!!
For a short demo what we described above we will execute the code below. We are first creating a single event aggregators, create a amount of Person instances which will have a conversation.
1: var eventAggregator = new EventAggregator();
2: var Marc = new Person("Marc", eventAggregator);
3: var Will = new Person("Will", eventAggregator);
4: var John = new Person("John", eventAggregator);
5: var Scott = new Person("Scott", eventAggregator);
6:
7: Console.WriteLine("******** Shout ********");
8: Marc.Shout("Hello, my name is Marc");
9:
10:
11: Console.WriteLine("******** Whisper ********");
12: Scott.Whisper("http://www.MichelTol.nl is a cool website :)", Will);
13: Console.ReadLine();
The result will be:
The order in which the ‘Listen’ delegates will be handled is unknown because of the fact that we specified at subscription time that the delegate will be handled at the background thread.
Download the Event Aggregator and demo here:
External sources: