samedi 14 mars 2009

Helper to create a weak event with strong typed signature

Introduction

When you create an event like this

public event EventHandler<MyEventArgs> MyEvent;

and you use it like this :

{
var receiver = new MyReceiverClass();
this.MyEvent += receiver.ReceiveMethod;

}

Receiver instance will be never cleaned in memory by Garbage Collector while “this” is referenced by any another object and, if "this" is Singleton object … receiver will be never free !!!! Even if receiver is not referenced somewhere … simply because it is referenced by the event of the Singleton :-(

.Net Framework provides us with some tools to avoid this problem. You can see e.g. this reference. It is the WeakEvent patterns.

This pattern works fine but it is a little hard and complex to create an object which receives an event with out strong reference : we need to implement IWeakEventListener interface or/and use a WeakEventManager.

My helper approaches the problem in a different way:

  • It is easy to use += / –= operator to wire/unwire event with exactly the same approach of that of a classical event
  • helper does not give a strong reference on receiver event
  • helper allows additionnal functions on FrameworkElement receivers:
    -> an unloaded instance will not received event!
    -> capacity to filter Execute calls when the event is wired on different receiver (not tested …)

Example of usage

To declare an event, you need just this code :

private UIWeakEventHandler<MyEventArgs> _myEvent;
public event EventHandler<MyEventArgs> MyEvent
{
add
{
_myEvent += value; // UIWeakEventHandler is created by += operator if necessary
}
remove
{
_myEvent –= value;
}
}

public void OnMyEvent(MyEventArgs e)
{
if (_myEvent != null)
_myEvent.Invoke(e);
}

and It”s All Folks :-)

MyEvent is a public event and supports classic += / –= operator so Visual Studio offers the traditionnal helper like auto-created-code when the developper press "+=" key sequence.

Why do I want to use this?

I need to create some events in my application on singleton instance and sometimes these events could be wired on WPF Window. When using a simple event, it is always necessary to wire on Loaded event of my Window and not forget to unwire event in Unloaded event of the same Window: when forgetting them will lead to some Leak memory error and it could take ages to find the origin of the problem. I hope this type of technical approach helps me!

Download

Download v2.10 (29/03/2009)

image

  • Complete refactoring ! I will create another post in fews days to explain all news functionnalities
    • IHandlersRepository interface : new name of IModelViewEventSupports

public interface IHandlersRepository
{
    void AddHandler(ModelRoutedEvent modelRoutedEvent, Delegate handler);
    void RemoveHandler(ModelRoutedEvent modelRoutedEvent, Delegate handler);
    void GetHandlers(ModelRoutedEvent modelRoutedEvent, object sender, EventArgs e, ref List<Delegate> result);
}

    • new RoutedPropertyChangedEvent and RoutedPropertyChangingEvent
    • Create FrameworkElementHandlersRepository class to optimize memory perfomance on FrameworkElement target (Same internal approach of RoutedEvent of .Net WPF framework)
    • Example with TreeView and Recursive ModelView entity
    • Include {MethodBinding} approach in TreeView Exampleimage

Download v1.20 

  • new interface IStorableEventsObject / IModelViewEventSupports to implements a “routed event pattern”  on ModelView object

Download v1.10

image

  • the main window can open secondary window via “New Windows” button
  • “Fire static event” calls a static event and changes a Label in secondary Windows
  • “Show counter” gives you the quantity of Window2 class instance
  • “GC.Collect” executes a memory collection >> Clic on “Show counter” to see the consequences of this collection

I never used this prototype in real application and so comments will be very welcomed :-)

Best regards

Class Diagram

WeakEventHandler

Todo list

  • I started to retro-analysis Routed event of WPF framework in order to understand the way is has been designed. For example, very good performances are certainly obtained by saving Event handler of a UIElement in the same UIElement (like Dependency proprerty mechanism): the static routed event instance is only an identifier of the event and does not memorises anything else.
    My own approach is different. I have a Weak-list of Event handler: when quantity of receiver instances is important (e.g. more than 10 000) >> will possibly result in having some performance problems during the invoking process.
    Routed approach will be possible only when invoker has a link with receiver. This link will be the Logical graph of user interface in "Routed Event and UIElement invoker". >> you can find a prototype of this approach in v1.20 version of download
  • I wish to interact between these static events and a graph of data object (ModelView part of Model - ModelView - View approach ) and when an instance of data object invokes a static event, My goal is that every ancestor (and only them) of this instance (in the graph) receives the event! >> you can find a prototype of this approach in v1.20 version of download

References