Weak Event Patterns
In applications, it is possible that handlers that are attached to event sources will not be destroyed in coordination with the listener object that attached the handler to the source. This situation can lead to memory leaks. Windows Presentation Foundation (WPF) introduces a design pattern that can be used to address this issue, by providing a dedicated manager class for particular events and implementing an interface on listeners for that event. This design pattern is known as the weak event pattern.
Why Implement the Weak Event Pattern?
Listening for events can lead to memory leaks. The typical technique for listening to an event is to use the language-specific syntax that attaches a handler to an event on a source. For example, in C#, that syntax is: source.SomeEvent += new SomeEventHandler(MyEventHandler).
This technique creates a strong reference from the event source to the event listener. Ordinarily, attaching an event handler for a listener causes the listener to have an object lifetime that is influenced by the object lifetime of the source (unless the event handler is explicitly removed). But in certain circumstances, you might want the object lifetime of the listener to be controlled by other factors, such as whether it currently belongs to the visual tree of the application, and not by the lifetime of the source. Whenever the source object lifetime extends beyond the object lifetime of the listener, the normal event pattern leads to a memory leak: the listener is kept alive longer than intended.
The weak event pattern is designed to solve this memory leak problem. The weak event pattern can be used whenever a listener needs to register for an event, but the listener does not explicitly know when to unregister. The weak event pattern can also be used whenever the object lifetime of the source exceeds the useful object lifetime of the listener. (In this case, useful is determined by you.) The weak event pattern allows the listener to register for and receive the event without affecting the object lifetime characteristics of the listener in any way. In effect, the implied reference from the source does not determine whether the listener is eligible for garbage collection. The reference is a weak reference, thus the naming of the weak event pattern and the related APIs. The listener can be garbage collected or otherwise destroyed, and the source can continue without retaining noncollectible handler references to a now destroyed object.
Who Should Implement the Weak Event Pattern?
Implementing the weak event pattern is interesting primarily for control authors. As a control author, you are largely responsible for the behavior and containment of your control and the impact it has on applications in which it is inserted. This includes the control object lifetime behavior, in particular the handling of the described memory leak problem.
Certain scenarios inherently lend themselves to the application of the weak event pattern. One such scenario is data binding. In data binding, it is common for the source object to be completely independent of the listener object, which is a target of a binding. Many aspects of WPF data binding already have the weak event pattern applied in how the events are implemented.
How to Implement the Weak Event Pattern
Implementing the weak event pattern consists of the following three aspects:
Derive a manager from the WeakEventManager class.
Implement the IWeakEventListener interface on any class that wants to register listeners for the weak event without generating a strong reference to the source.
When registering listeners, do not use the conventional add and remove accessors of the event where you want the listener to use the pattern. Instead, use the AddListener and RemoveListener implementations in the dedicated WeakEventManager for that event.
To implement the weak event pattern, you typically create a manager class with a 1:1 relationship to the event. For example, if you have an event named Spin, you would create a SpinEventManager class that is the dedicated weak event manager for the event. If the event exists in more than one class, behaves generally the same in each class, and shares the same event data type, the same manager can be used for each event.
When you derive from the WeakEventManager class, you override two virtual methods and expose several other members whose names are not specifically governed by a virtual template, but should exist nonetheless. The overrides are used to initiate or terminate event delivery mode by the WPF infrastructure. The other members provide functionality so that your own IWeakEventListener implementations can use the WeakEventManager to attach listeners to the event.
The IWeakEventListener interface has a single interface method named ReceiveWeakEvent. The ReceiveWeakEvent implementation must be a centralized implementation that directs any event reference that exists on that class to the appropriate WeakEventManager.
Suppose that you have a ClockwiseSpin event (defined by a Spinner type) that is a conventional event. If you have a SpinListener listener class that wants to be a listener, the conventional technique (not using the weak event pattern) for attaching the handler would be to use the += operator:
spinnerInstance.ClockwiseSpin += new EventHandler(MyOnCWSpinHandler);
If you have a class that implements IWeakEventListener and accounts for the ClockwiseSpin event and its manager in the implementation, the syntax to use the weak event pattern is:
Your handling logic for that event is specified within one of the cases of the ReceiveWeakEvent implementation on your class, not as a conventional delegate-based handler.
Implementing the Pattern for External Events
One interesting aspect of the weak event pattern is that you can implement the pattern against an event that is not part of your code base. From the perspective of the source, the way that handlers are attached to its event does not differ, and is controlled by the WeakEventManager. You only need to define a WeakEventManager for that event, and then account for that event as part of the ReceiveWeakEvent logic on any prospective listener that wants to use the weak event pattern to listen to that event.