Introducing Event Aggregator in ClientUI 5 SP1
In my recent blog posts, I have covered two of the major new features introduced in ClientUI 5 service pack 1. Check out my post for LightSwitch support here, and the real-time streaming capability in XPS Document Viewer here.
Since its debut last year, ClientUI has been well recognized not only with its amazing and beautiful user interface controls, but also a rock-solid framework that built the foundation of the entire control library. ClientUI was the first in the industry that pioneering the adoption of MVVM design pattern in both Silverlight and WPF application, as well as setting new standards for MVVM-ready control architecture. Click here to read the overview of MVVM development, and here for in-depth technical details.
ClientUI supports MVVM development through comprehensive built-in libraries such as delegate command, command reference, and advanced data binding support. This allows developers to build full-blown MVVM applications without any third party frameworks. Confused which MVVM framework to choose, or should you roll out your own? Check out Bill’s latest blog post as he shares his journey finding one that works best for him.
In this blog post, I will discuss about one of the top requested features that we’ve added in the latest service pack release. I’m pleased to introduce our advanced event aggregator library, now built right into the ClientUI Framework. Let’s start with the basics.
Event Aggregator Basics
One of the key principles of MVVM development is data binding. And data binding is all about communication. By the means of communication, it is exactly how the View talks to the ViewModel for user interaction, and how the ViewModel talks to the Model for data processing and transactions. What’s really cool is that one layer doesn’t require dependencies to another thus enabling great separation of concern that yield some real benefits as detailed here.
As you’re building MVVM applications toward more complex requirements, you will eventually find certain scenarios where the communication flow becomes too difficult to achieve. This is particularly true for scenarios that require communication across different ViewModels; or across different modules in a large, composite application.
Consider a basic scenario where you have a data grid that displays member list and a form to edit the selected member. With MVVM pattern, you will create two distinct ViewModels, one for the member list and another for the editing form. Now consider a quite common business requirement where the member list should be refreshed whenever a member is updated. How would you achieve such requirement? What options do you have? Let’s explore the possibilities.
It’s natural to think about using standard CLR event as your first bet since that’s the only built-in event handling available in .NET Framework. While you’re declaring the new events, you will realize that you’re going to create dependencies between these ViewModels because CLR event requires strong reference to the target instance. Consequently, it will add significant maintenance overhead in the long run.
The second approach, you can create a parent-child link between the two ViewModels and simply call the method in the parent from the child ViewModel. This might seem to work at first, but you will soon realize the shortfall as more ViewModels are required to link to the parent. Although there could be workarounds such as interface refactoring, this approach is definitely still far from ideal.
Since we’re unable to find a solid solution in the existing framework, we need a better design pattern that dictates how event communication works – one that allows the event to be listened and raised across different ViewModels without strong type reference; and more importantly, one that works consistently, reliably and easy to maintain in the long run. That event pattern is so-called Event Aggregator.
Event Aggregator addresses the challenges above by allowing a ViewModel to listen to an event without strong referencing to the target ViewModel. This means that there could be multiple ViewModels listening to an event at the same time, and the event can also be raised from different ViewModels – all without requiring the knowledge of existence of one and another.
See the following illustration to get the big picture of Event Aggregator’s conceptual view.
At this point, I hope you already get some insights on the key challenges around communication flow in MVVM, and understand why Event Aggregator is needed.
Usage and Examples
ClientUI 5 service pack 1 includes a built-in event aggregator service that works consistently in both Silverlight and WPF – and certainly, with the same API. ClientUI’s event aggregator is implemented accordingly to match Martin Fowler’s description and concept of an event aggregator.
ClientUI’s event aggregator is designed to be simple and easy-to-use, yet powerful enough to cover a number of advanced scenarios. It takes only a single line of code to subscribe to an event, the same is true for the event publication. See the following examples.
1 2 3 4 5 |
// Subscribe to an event EventAggregator.Default.Subscribe<MailNotificationEvent, Mail>(OnMailReceived); // Publish an event EventAggregator.Default.Publish<MailNotificationEvent, Mail>(this.MailEntity); |
As shown in the above example, the event aggregator is designed with default static instance allowing developers to subscribe or publish an event in code-efficient manner. Both the Subscribe and Publish methods comprised of two generic parameters for the method signature which are TEventType and TPayLoad. The TEventType represents the type of the event to subscribe or publish, while the TPayLoad represents the type of the message to pass to the event.
In summary, at the heart of ClientUI’s event aggregator is the six methods listed below. You basically deal with three things, subscribing to an event, publishing an event, and unsubscribe from a previous subscription.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
public TEventType GetEvent<TEventType>() where TEventType : DelegateEventBase, new(); public void Publish<TEventType, TPayload>(TPayload payload) where TEventType : DelegateEventBase, new(); public SubscriptionToken Subscribe<TEventType, TPayload>(Action<TPayload> action) where TEventType : DelegateEventBase, new(); public SubscriptionToken Subscribe<TEventType, TPayload>(Action<TPayload> action, SubscribeOptions options) where TEventType : DelegateEventBase, new(); public SubscriptionToken Subscribe<TEventType, TPayload>(Action<TPayload> action, SubscribeOptions options, Predicate<TPayload> filter) where TEventType : DelegateEventBase, new(); public void Unsubscribe<TEventType, TPayload>(Action<TPayload> action) where TEventType : DelegateEventBase, new(); |
In addition, ClientUI’s Event Aggregator also supports several advanced features such as event filtering, use background thread, reference keep alive and more. However, we don’t include the support for UI thread invocation which is intentionally decided. The reason is that accessing the View (UI) in the ViewModel would break the nature of MVVM which emphasizes on clear separation, hence the decision is consistent with our goal to build libraries that enforce design pattern and development best practices.
To wrap up this post, I’ve prepared a reference sample that demonstrates how event aggregator works in the simplest way. I chosen the mail notification scenario for this sample where the notification event is subscribed and published from different ViewModel instances, see the following illustration.
Finally, download the sample bits and get yourself familiar with the event aggregator basics. The sample package includes both Silverlight and WPF projects which demonstrate the event aggregator using the same codebase. Again, please make sure you have ClientUI 5 SP1 installed to enjoy this new addition, or click here to download it.
As usual, I welcome any questions and feedback. Please drop them in the comment box. Thanks!
Best,
Jimmy
Great article and sample. I have a few apps where this will be a great benefit.