Composite Presentation Events and Silverlight
Recently, I have been working on my first Silverlight application. It has given me the opportunity to learn a lot about a technology that I have never used before. One of the many tools that I have learned to use over the past few weeks is Microsoft’s Composite Presentation Events. The application that I am working on relies heavily on these events. In this application, just about everything that occurs is asynchronous. Thus, the application is filled with subscriptions and publications of predefined events. It’s fairly simple to utilize. One simply has to use an EventAggregator to acquire the desired event, then either subscribe to that event or publish that event.
IEventAggregator aggregator = new EventAggregator();
var fryEvent = aggregator.GetEvent<BeginEatFrenchFryEvent>();
// ...
fryEvent.Subscribe(OnBeginEatFrenchFry, ThreadOption.UIThread);
// ...
fryEvent.Publish("lunch is served");
BeginEatFrenchFryEvent is defined as:
public class BeginEatFrenchFryEvent : CompositePresentationEvent<string> { }
The action to take when an event is published is the first parameter. In this case, it’s OnBeginEatFrenchFry:
public void OnBeginEatFrenchFry(string payload)
{
// Will see the 'Publish' call and execute.
}
OnBeginEatFrenchFry will receive the string “lunch is served” and then execute. Simple, eh? Well, if one wants all of the event subscriptions to stay alive forever, then that’s all there is to it. However, what if one does not want the subscription to last more than, say, once? There is an Unsubscribe method, but is there a way to tell the application up-front to only subscribe to a published event once? The short answer is no. The long answer is no, due to bugs that exist in Composite Presentation Events and limitations of Silverlight.
We recently had a problem in this application where event subscriptions were getting stacked. Subscriptions were compounding on every subsequent load of a view. First four events would fire. On the next load, 8. The next, 16. We had found one event that was firing no less than 150 times before the view would finish loading. Needless to say, this slowed down the application a fair bit.
So back to our problem: How can we prevent events from remaining subscribed? Well, subscribe has more than two parameters. There’s a third parameter, bool keepSubscriberReferenceAlive. At first, we thought that this was the simple solution: Just past false to this parameter and it will automatically unsubscribe. Unfortunately, not only is this not the case, but it also gets eaten by the implementation unless you pass the fourth parameter to subscribe (a Predicate<bool> which serves as a filter; more on this later). Using .NET Reflector, we saw that the implementation of Subscribe is very interesting. If you pass no third or fourth parameters in, the values false and null are used for the third and fourth parameters, respectively. In the implementation of Subscribe that takes four parameters, if the fourth parameter (the filter) is null, then a hard-coded value of true is passed instead of whatever keepSubscriberReferenceAlive was set to. The only real workaround at this point is to pass a dummy filter as a fourth parameter so that the bool one passes in actually gets used.
fryEvent.Subscribe(OnBeginEatFrenchFry, ThreadOption.UIThread,
false, dummyVariable => true);
Problem solved? Not quite. This is where the aforementioned Silverlight limitation comes into play. Silverlight does not support weak references to delegates or anonymous methods. Unfortunately, the above filter is such a method. Where did we go from there? Create a strong referenced dummy filter, of course.
public static class StaticFilter
{
public static bool AllowAllValues<T>(T dummy)
{
return true;
}
}
So at the end of the day, we have the following:
fryEvent.Subscribe(OnBeginEatFrenchFry, ThreadOption.UIThread,
false, StaticFilter.AllowAllValues);
Finally we reach the end of our endeavors in trying to get an event to automatically unsubscribe. Does this work? Unfortunately, no. The event remains subscribed even if the above subscription is used. As far as we can tell, keepSubscriberReferenceAlive is useless to us.
Anyway, we eventually found that the only way to guarantee that a given event is unsubscribed from is to explicitly unsubscribe from it. The Unsubscribe method works either two ways: Either by passing in the SubscriptionToken returned by the Subscribe method, or by giving it the Action (in the above example, OnBeginEatFrenchFry) that was used in the Subscribe method. The former can get really hairy. If you have several events, that’s several SubscriptionTokens you have to manage. The latter is much cleaner, however, as all you need is the Action method one used to subscribe to the event.
fryEvent.Unsubscribe(OnBeginEatFrenchFry);
One could easily put this within the method itself to automatically unsubscribe after one usage. What we eventually did was create a static class, called OnceEvent, that had its own Subscribe method that internally took care of the unsubscribing so that the client would only have to worry about one line of code instead of many.
Phew, this certainly ended up being longer than I intended. I think I’ll stop here. There was a lot of other stuff that I skipped regarding this topic, but I hope that what I have written will provide others who are using these technologies in tandem with solid advice on how to get around some of the insidious limitations and bugs lying within them.
