Sunday, October 7, 2012

Weak Event Pattern Improvements : WPF 4.5 RC New Feature

In this post we are going to discuss improvements in WPF 4.5 RC for Weak Even Pattern. This introduces a mediator between event source and subscriber. The greatest thing is that it just maintains a weak reference listeners causing no memory leaks.

Some Background on Weak Event Manager
Event subscription cause memory leaks because Event Source holds strong references of the listener. Now listeners cannot be collected because garbage collection algorithms only get rid of those objects without any hard references. In order to handle this problem, idea of weak references came along. There is no need of maintaining any hard references for events. This is generally taken care of by introducing a mediator between a source and listener. There might be any number of sources for a source event from an instance. All the listeners should get notified when the actual event is raised.

Event sources must be defined in a certain way to publish these events. Before WPF 4.5, even listeners were needed to follow a certain pattern. They were needed to implement IWeakEventListener interface. As a developer, we don't want our class hierarchies to be effected by the API we use.

Improvements in WPF 4.5
In WPF 4.5 RC, weak event pattern is improved. In addition to listeners, WeakEventManagers also support Handlers. The handlers are defined like event handlers but our classes don't need to implement a particular interface. Plus since there are no hard references maintained, there are no possible memory leaks.

WeakEventManager is a DispatcherObject
You must notice that WeakEventManager is a DispatcherObject. Being a DispatcherObject it can't be used on a non-UI thread. This makes perfect sense based on the events it is introduced for. They are specifically UI related events.

Unit Testing & WeakEventManager
WeakEventManager doesn't implement any interface so it would be difficult to mock the manager's calls. This makes it more difficult to unit test the client code using WeakEventManager.

Difference with PRISM Event Aggregator / MVVM Light Messenger
We need to notice that this WeakEventManager is different than PRISM's EventAggregator or MVVM Light's Messenger. They are provided by their respective toolkits for messages publication and subscription. They are similar in the sense that they are also based on weak references. But they are very different in their usage. They are specially designed to support application wide messages for disconnected event source and subscribers. For the case of WeakEventManager, we need to specifically identify the source we are interested in.

Using Weak Event Manager
As a listener of these events, you need to use the appropriate WeakEventManager. There are basically few options available.
  1. Existing WeakEventManager
  2. WPF has provided a list of WeakEventManager(s) for the generally used events. You just need to find the appropriate event manager available for the event you need to subscribe. They all inherit from the abstract WeakEventManager class.

  3. Generic WeakEventManager
  4. If you can't find any specific WeakEventManager for your event, then the generic WeakEventManager can always be used. It actually uses reflection to find event given its name. It is also more verbose to use. That is why this can be our last option. But this relieves us from being worried about a particular WeakEventManager to be available.

  5. Custom WeakEventManager
  6. It would make more sense for control library authors to provide the WeakEventManagers for their events. In this way, the library users don't have to worry about using Generic Weak Event Manager causing possible efficiency issues. We just need to directly inherit from WeakEventManager providing definition of the overridable / abstract members.

Using IWeakEventListener
Before showing an example of how we can decorate a type to be a listener, let us first introduce some setup code. Here is ViewModelBase. It implements INotifyPropertyChanged which would allow our view models to support change propagation. We will be using this as base class of all our view models in this post.

Below is the view model to keep student info. A Student is being identified with StudentId. It also has a property called FirstName to keep the first name of the student. There is one more property IsDirty which is to identify that the Student info has been modified and it doesn't have default values for its properties.

Now we add a view model called MainViewModel. This keeps a collection of students maintained as ObservableCollection of StudentViewModel. It keeps adding an additional Student once the default student is modified.

The view model implements IWeakEventListener interface which requires the definition of ReceiveWeakEvent method. The method would get executed when any event is raised for which the object of this type subscribes as a listener. The implementation of the interface just shows that the object of this type can be used as a listener of weak events raised from WeakEventManager. In the method itself, we are just adding a new student, unsubscribing from listening the previous default student and add to listen to the new student view model instance.

SubscribeEventHandlers and UnsubscribeEventHandlers are the main code pieces in this example. It is here in these methods that the actual subscription and unsubscription of listener is taking place. We are just using the static methods AddListener and RemoveListener from PropertyChangedEventManager for this purpose. The WeakEventManager keeps a list of all subscribers and calls the ReceiveWeakEvent methods of all listeners whenever the specified event from the particular source object is raised.

Now lets use this view model in a view. The above view is being used as the MainWindow of the application. The above view model is being used as a DataContext of the view below. The Students collection is shown in a DataGrid.

Using WPF 4.5 WeakEventManager's Handler
In WPF 4.5, the weak event pattern is further improved. We are not required to implement IWeakEventListener for a type being served as a listener of an event. Now we can just add handlers instead. Using AddHandler and RemoveHandler methods of the event manager, we can add and remove handlers of these events. Here We need to specify the event source, event handler and the particular property identified by property name. Other managers might need other details.

Using Generic WeakEventManager
There is also generic weak event manager provided by the WPF 4.5. Although there are performance implications because the event is identified by a string parameter. So the framework would be using reflection to get the event details from the type. Since this is generic, we can't specify the particular property name here unlike the previous two methods.

Let us run the application, the view is show as follows:

When the view is shown, try updating the FirstName column of the last row and notice a new row being added.


No comments: