(X) Hide this
    • Login
    • Join
      • Generate New Image
        By clicking 'Register' you accept the terms of use .

WinRT Business Apps with Prism: PubSubEvents

(1 votes)
Brian Noyes
>
Brian Noyes
Joined Jun 10, 2010
Articles:   19
Comments:   117
More Articles
3 comments   /   posted on May 21, 2013

Tweet
 

This is part 4 in the series WinRT Business Apps with Prism.

Introduction

In this article, I am going to cover the loosely coupled communication mechanism from the newly released Prism for Windows Runtime, called PubSubEvents. This communication mechanism is also known as EventAggregator because it is based on the design pattern of the same name. If you have had exposure to Prism 4, then you might also be familiar with EventAggregator from there. If you already know how to use Prism 4’s EventAggregator, you know how to use the one that ships with Prism for Windows Runtime, because this is simply a ported version, no functional differences other than the projects in which you can use it. For those folks, the quick and dirty is this: the EventAggregator of Prism 4 was ported to a Portable Class Library that uses a SynchronizationContext under the covers for the thread dispatching features, which removes the coupling to WPF and Silverlight and avoids taking a new dependency on WinRT for what the EventAggregator does. This library is set up so that it works with any .NET 4.0 or later, Silverlight 4 or later, Windows Phone 7.1 or later, or WinRT project.

In this article, for those that don’t already have a lot of experience with EventAggregator, I will show you how you can use it to communicate between components in your application, typically between ViewModels or between client side services, so that those components do not have to take direct dependencies on each other. Additionally, I’ll show how you can benefit from the fact that Prism PubSubEvents use weak references by default, alleviating the need to worry about unsubscribing from events to avoid memory leaks, how to use it’s thread dispatching capabilities, and how to wire up filter methods to keep your event handlers from being called unless some criteria is met.

Step 1: Set up an app with two decoupled ViewModels

I’m using a modified version of the EventAggregator QuickStart from the Prism for Windows Runtime guidance as my starting point. You can download that starting point here.

The official QuickStart is pretty close to the same as the completed sample from this article. The main difference is that the official one does not use the MvvmAppBase class on the App class, so there is a bunch of boilerplate startup code in the App class. This is because this QuickStart was written and documented prior to the MvvmAppBase class being factored out. If you look at the sample code from this article, you will see that the App class is simple and clean by using the MvvmAppBase class instead, the structure of which was discussed in Part 1 and Part 2 of this series.

This app has a parent ViewModel on the MainPage, with two simple child Views/ViewModels – PublisherViewModel and SubscriberViewModel. Here is what the UI will look like running.

Figure1

Step 2: Grab the Prism Libraries through NuGet

To build the app starting point solution, you will need to add in the Microsoft.Practices.Prism.StoreApps and Microsoft.Practices.Prism.PubSubEvents libraries. You can do that by downloading the code from here, or you can use NuGet to pull in the binaries. I’m going to do the latter for this article. So head out to Nuget, and do a search on the terms Prism.StoreApps and Prism.PubSubEvents. At the time I am writing this, the NuGet feed has not been updated, so I had to select Pre-release at the top. But that should be updated soon since Prism for Windows Runtime officially released on May 17 2013.

Figure2

Figure3

At this point, if you downloaded the starting point solution in Step 1, you should be able to build and run, but the buttons don’t do anything noticeable yet.

Step 3: Add the EventAggregator

To do pub/sub events, you need a middleman object to decouple the publisher and the subscriber. The idea is that they can each take a dependency on the middleman, then they don’t have to directly depend on each other. The EventAggregator is that middleman in Prism PubSubEvents. It is a singleton service that acts as a repository or registry of event objects. The event objects are the things that actually do the dispatching of calls from a publisher out to the subscribers for a particular event. So you need to make a reference to the singleton IEventAggregator reference available to the publishers and subscribers. You could do this through a container as was covered in Part 2, but for this article I am just going to use manual dependency injection.

The way the app is structured, the MainPageViewModel explicitly constructs the two child ViewModels (PublisherViewModel and SubscriberViewModel) and renders their views via DataTemplates.

   1: public class MainPageViewModel : ViewModel
   2: {
   3:     public MainPageViewModel()
   4:     {
   5:         SubscriberViewModel = new SubscriberViewModel();
   6:         PublisherViewModel = new PublisherViewModel();
   7:     }
   8:  
   9:     public SubscriberViewModel SubscriberViewModel { get; set; }
  10:     public PublisherViewModel PublisherViewModel { get; set; }
  11: }

 

   1: <Page.Resources>
   2:     <DataTemplate x:Key="SubscriberTemplate">
   3:         <views:SubscriberView />
   4:     </DataTemplate>
   5:     <DataTemplate x:Key="PublisherTemplate">
   6:         <views:PublisherView />
   7:     </DataTemplate>
   8: </Page.Resources>
   9: ...
  10: <ContentControl Content="{Binding PublisherViewModel}"
  11:             ContentTemplate="{StaticResource PublisherTemplate}"
  12:             HorizontalContentAlignment="Center"
  13:             VerticalContentAlignment="Center" />
  14: <ContentControl Grid.Column="1"
  15:             Content="{Binding SubscriberViewModel}"
  16:             ContentTemplate="{StaticResource SubscriberTemplate}"
  17:             HorizontalContentAlignment="Center"
  18:             VerticalContentAlignment="Center" />

So I can simply construct the EventAggregator up in the App class, and pass it into the MainPageViewModel by using a Register call on the ViewModelLocator. It is important to construct the EventAggregator on the UI thread so that it can set up the underlying SynchronizationContext correctly for thread dispatching, so I wait until the OnInitialize override to construct it, then pass it into the MainPageViewModel as a parameter, keeping it in a member variable on the App class to make the singleton lifetime match the App lifetime.

   1: sealed partial class App : MvvmAppBase
   2: {
   3:     IEventAggregator _EventAggregator;
   4:  
   5:     protected override void OnLaunchApplication(LaunchActivatedEventArgs args)
   6:     {
   7:         NavigationService.Navigate("Main", null);
   8:     }
   9:  
  10:     protected override void OnInitialize(IActivatedEventArgs args)
  11:     {
  12:         base.OnInitialize(args);
  13:         _EventAggregator = new EventAggregator();
  14:         ViewModelLocator.Register(typeof(MainPage).ToString(), 
  15:             () => new MainPageViewModel(_EventAggregator));
  16:     }
  17: }

Next I need to modify the MainPageViewModel to accept the constructor parameter, and to pass it down to the PublisherViewModel and SubscriberViewModel.

   1: public class MainPageViewModel : ViewModel
   2: {
   3:     public MainPageViewModel(IEventAggregator eventAggregator)
   4:     {
   5:         SubscriberViewModel = new SubscriberViewModel(eventAggregator);
   6:         PublisherViewModel = new PublisherViewModel(eventAggregator);
   7:     }
   8:  
   9:     public SubscriberViewModel SubscriberViewModel { get; set; }
  10:     public PublisherViewModel PublisherViewModel { get; set; }
  11:  
  12:     ...
  13: }

Now likewise, they each need parameterized constructors to accept and hold the reference to IEventAggregator.

   1: public class PublisherViewModel : ViewModel
   2: {
   3:     private readonly IEventAggregator _EventAggregator;
   4:     
   5:     public PublisherViewModel(IEventAggregator eventAggregator)
   6:     {
   7:         _EventAggregator = eventAggregator;
   8:         ...
   9:     }
  10:     ...
  11: }

 

   1: public class SubscriberViewModel : ViewModel
   2: {
   3:     private readonly IEventAggregator _EventAggregator;
   4:     
   5:     public SubscriberViewModel(IEventAggregator eventAggregator)
   6:     {
   7:         _EventAggregator = eventAggregator;
   8:         ...
   9:     }
  10:     ...
  11: }

 

Step 4: Define an event class for the needed communication

For the sample app, the idea is that the publisher will publish when a shopping cart has changed that the subscriber also needs to monitor and display information about. When using Prism PubSubEvents, you define an event class for each distinct communication along with a strongly typed payload that can be passed along with the event. The event for our sample looks like this:

   1: public class ShoppingCartChangedEvent : PubSubEvent<ShoppingCart> { }

Believe it or not, that is all there is to defining the event. All the implementation is in the PubSubEvent base class. You just need to define a class that represents the specific event you want to publish and maps it through the generic argument to the payload entity you want to pass when it fires. If you have no payload, you can just use object as the type and pass null.

Step 5: Publish

Wherever it is appropriate in your code (based on user interaction or other triggers) you just need one line of code to publish:

   1: _EventAggregator.GetEvent<ShoppingCartChangedEvent>().Publish(_cart);

You call the GetEvent method on IEventAggregator to get a reference to a singleton instance of your event class (managed by the EventAggregator itself), and then call Publish on that event, passing the strongly typed payload. In the sample app, this is done from the command handlers for the buttons in that view, specifically the PublishOnUIThread method for now.

   1: private void PublishOnUIThread()
   2: {
   3:     AddItemToCart();
   4:     _EventAggregator.GetEvent<ShoppingCartChangedEvent>().Publish(_cart);
   5: }

Step 6: Subscribe

Likewise, in the Subscriber, you can hook up the subscription wherever it makes sense, but typically in a ViewModel that is on construction, as soon as the EventAggregator reference is injected:

   1: public SubscriberViewModel(IEventAggregator eventAggregator)
   2: {
   3:     _EventAggregator = eventAggregator;
   4:     ...
   5:     _EventAggregator.GetEvent<ShoppingCartChangedEvent>().Subscribe(HandleShoppingCartUpdate);
   6: }

The handling method that you pass as a delegate reference to this method needs to have a void return (events are fire and forget) and takes in a single parameter of the payload type:

   1: private void HandleShoppingCartUpdate(ShoppingCart cart)
   2: {
   3:     ItemsInCart = cart.Count;
   4: }

That is it for the basic hookup of pub/sub events in Prism. At this point you should be able to run, click on the “Add Item to Cart (UI Thread) button, and see the count increment in the subscriber view.

Step 7: Define a background subscriber for showing weak references

There is a separate class defined in the sample called BackgroundSubscriber:

   1: public class BackgroundSubscriber
   2: {
   3:     CoreDispatcher _dispatcher;
   4:     public BackgroundSubscriber(CoreDispatcher dispatcher)
   5:     {
   6:         _dispatcher = dispatcher;
   7:     }
   8:  
   9:     public void HandleShoppingCartChanged(ShoppingCart cart)
  10:     {
  11:         var threadId = Environment.CurrentManagedThreadId;
  12:         var count = cart.Count;
  13:  
  14:         // Assign into local variable because it is meant to be fire 
  15:         // and forget and calling would require an await/async
  16:         var dialogAction = _dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
  17:             {
  18:                 MessageDialog dialog = new MessageDialog(
  19:                     string.Format(CultureInfo.InvariantCulture, 
  20:                     "Shopping cart updated to {0} item(s) in background subscriber on thread {1}", 
  21:                     count, threadId));
  22:                 var showAsync = dialog.ShowAsync();
  23:             });
  24:     }
  25: }

There is a little complexity in the code there, but basically this is a class with a method that can be targeted by the pub/sub event that will pop a message dialog even if it is called on a background thread and will show what thread it was called on and the number of items in the shopping cart.

The reason for this class is that it is an object that we can explicitly new up, hold a reference to from the view model, then release the reference and cause garbage collection, and see if it sticks around if there is still an event subscription targeting it.

So in the subscriber view you can see two buttons – Add Background Subscriber and GC Background Subscriber. The command handler for the first creates the subscriber, adds an event subscription for it, and holds it in a member variable.

   1: private void AddBackgroundSubscriber()
   2: {
   3:     // Create subscriber and hold on to it so it does not get garbage collected
   4:     _subscriber = new BackgroundSubscriber(Window.Current.Dispatcher);
   5:     _EventAggregator.GetEvent<ShoppingCartChangedEvent>().Subscribe(
   6:         _subscriber.HandleShoppingCartChanged);
   7: }

The second button command handler releases the member variable reference to the subscriber and forces a garbage collection – but note that no code has unsubscribed the event handling that pointed to the object through its handling method.

   1: private void GCBackgroundSubscriber()
   2: {
   3:     // Release and GC, showing that we don't have to unsubscribe 
   4:     //  to keep the subscriber from being garbage collected
   5:     _subscriber = null;
   6:     GC.Collect();
   7: }

After adding that code, if you run, click on the Add Background Subscriber button, and then start firing the events from the UI thread, you will see the message dialog pop each time. Then press the GC Background Subscriber button, fire a few more events, and you will see that the background subscriber is no longer there to receive events because it has been garbage collected even though there was never an Unsubscribe call to release the reference that was passed into the event through a delegate.

This is there to address the number one cause of memory leaks in .NET applications – event subscriptions between two objects with different lifetimes where the subscribing object has a shorter lifetime from the consuming code that the object it is subscribing on. A normal event subscription through a delegate holds a strong reference back to the subscriber that will prevent it from being garbage collected if it does not get unsubscribed, even if all other references are released.

But Prism events use a WeakReference under the covers by default, which the garbage collector ignores in its reference counting scheme, so it will allow the underlying object to get garbage collected if nothing else has a reference to it, thus alleviating you from needing to worry about unsubscribing at the appropriate time.

If you want to have a strong reference, just use an overload of the Subscribe method with a second parameter named keepSubscriberReferenceAlive:

   1: _EventAggregator.GetEvent<ShoppingCartChangedEvent>().Subscribe(
   2:     _subscriber.HandleShoppingCartChanged, true);

 

Step 8: Leverage Thread Dispatching

Another feature of the PubSubEvents is the ability to tell Prism when you subscribe what thread you want to be notified on. If you were to publish from the non-UI thread button in the publisher using a Task to fire on a background thread:

   1: private void PublishOnBackgroundThread()
   2: {
   3:     AddItemToCart();
   4:     Task.Factory.StartNew(() => 
   5:         {
   6:             Debug.WriteLine(String.Format("Publishing from thread: {0}", 
   7:                 Environment.CurrentManagedThreadId));
   8:             _EventAggregator.GetEvent<ShoppingCartChangedEvent>().Publish(_cart);
   9:         });
  10: }

You would see you have a problem on the subscriber side when it tries to update a property that is bound from the UI – it throws an exception because the event comes into the subscriber on the background thread of the publisher. All you need to do to fix that is use a different overload of the Subscribe method that takes a ThreadOption parameter. This allows you to dictate from the subscriber that you want to be notified on either the Publisher thread (the default), the UIThread, or a Background thread.

   1: _EventAggregator.GetEvent<ShoppingCartChangedEvent>().Subscribe(
   2:     HandleShoppingCartUpdate, ThreadOption.UIThread);

The PubSubEvents class will handle getting the call dispatched onto the UI thread using a SynchronizationContext it sets up when it is constructed. This means if you are going to use the UIThread option, you need to make sure the EventAggregator is constructed on the UI thread. If you don’t you will get an exception when the Subscribe call is made telling you that. Using SynchronizationContext instead of a Dispatcher means the code can be portable to any .NET platform. If you choose the Background option, a Task will be used to call the subscriber on a Task Scheduler background thread.

Step 9: Leverage Filtering

Sometimes you don’t want your event handler to be called unless some criteria is met first. You could do this by putting a guard condition in the handling method, but from a testability and separation of concerns perspective, that should happen outside of the method that is focused on handling the event. So Prism PubSubEvents lets you pass a filter criteria, in the form of a Predicate<T> delegate (which is equivalent to a Func<T,bool> delegate). You can do that by passing a full delegate reference to a method that takes in the payload type and returns a bool, or you can pass a lambda expression directly to the Subscribe method.

So for our example, if we want to show a warning on the screen if the count of items in the shopping cart exceeds 10, we can do the subscription like this:

   1: _EventAggregator.GetEvent<ShoppingCartChangedEvent>().Subscribe(
   2:     HandleShoppingCartUpdateFiltered,
   3:     ThreadOption.UIThread, false, (cart) => cart.Count > 10);

HandleShoppingCartUpdateFiltered just sets a property that causes a warning to show. But that won’t be called unless the filter criteria (cart.Count > 10) is met.

Summary

In this article, I showed you how simple PubSubEvents in Prism are to use. You saw that once you have a reference to the IEventAggregator in the publisher and subscriber, it is one line of code to publish, one line of code to subscribe, and effectively one line of code to define the event itself. On the subscriber side you have the options of not using weak references (which it does by default), controlling the thread you are notified on, and passing a filter condition that gets evaluated and must pass (return true) before the handling method is called. The publish and subscribe methods are strongly typed based on the payload type specified by the event class.

This allows you to have communications between ViewModels or between repositories and other client side services without needing to pass explicit references between them – meaning more loose coupling in your architecture.

 

About the Author:

Brian Noyes is CTO of Solliance (www.solliance.net), a software development company offering Architecture as a Service, end-to-end product development, technology consulting and training. Brian is a Microsoft Regional Director and MVP, Pluralsight author, and speaker at conferences worldwide.  Brian worked directly on the Prism team with Microsoft patterns and practices. Brian got started programming as a hobby while flying F-14 Tomcats in the U.S. Navy, later turning his passion for code into his current career. You can contact Brian through his blog at http://briannoyes.net/ or on Twitter @briannoyes.


Subscribe

Comments

  • RohitSharma

    Re: WinRT Business Apps with Prism: PubSubEvents


    posted by RohitSharma on May 21, 2013 17:06

    Some people rate EventAggregator as anti-pattern, it can be abused as well, see https://groups.google.com/forum/?fromgroups#!topic/wpf-disciples/8_hWnG_rW_4 and an alternate implementation to EA http://www.codeproject.com/KB/WPF/ServiceTrees.aspx



  • RohitSharma

    Re: WinRT Business Apps with Prism: PubSubEvents


    posted by RohitSharma on May 21, 2013 17:07

    Sorry forgot to point out the main link explaining why this is an anti pattern http://dkturner.blogspot.com/2010/06/global-events-considered-harmful.html


    Thanks,

    Rohit

    http://rohiton.net

  • RohitSharma

    Re: WinRT Business Apps with Prism: PubSubEvents


    posted by RohitSharma on May 21, 2013 17:08

    I am feeling embarrassed, i see it was your student who came up with this idea :).

Add Comment

Login to comment:
  *      *       

From this series