views:

230

answers:

1

I want to set an event handler only if this is not set:

If GetHandlers(MyWindow.Closed, AddressOf MyWindow_Closed).Length = 0 Then
    AddHandler MyWindow.Closed, AddressOf MyWindow_Closed
EndIf
+1  A: 

You can't really query the current value of the event's delegate, except in the code that defines the event. What is your intent here? Normally you shouldn't be too concerned (necessarily) with other subscribers? There are ways of hacking past the encapsulation to find the current value, but they are not recommended (it just isn't a good idea).

If your concern is whether you are already handling that event with that handler (i.e. you don't want to double-subscribe, then you can always either a: fix the code so it doesn't do this, or b: cheat (C# example):

// remove handler **if subscribed**, then re-subscribe
myWindow.Closed -= MyWindow_Closed;
myWindow.Closed += MyWindow_Closed;

To get the invocation list is... brittle but doable. In simple cases you can just use reflection to get the field, and snag the value. But with forms etc it uses sparse techniques (to minimise the space for events without subscribers). In the case of FormClosed, this is keyed via EVENT_FORMCLOSED.

It might make more sense with an example (C#, sorry):

    Form form = new Form();
    form.FormClosed += delegate { Console.WriteLine("a");}; // just something, anything
    form.FormClosed += delegate { Console.WriteLine("b");}; // just something, anything
    object key = typeof(Form).GetField("EVENT_FORMCLOSED",
        BindingFlags.NonPublic | BindingFlags.Static).GetValue(null);
    EventHandlerList events = (EventHandlerList )
        typeof(Component).GetProperty("Events",
        BindingFlags.NonPublic | BindingFlags.Instance).GetValue(form, null);
    FormClosedEventHandler handler = (FormClosedEventHandler)events[key];
    foreach (FormClosedEventHandler subhandler in handler.GetInvocationList())
    {
        subhandler(form, null); // access the two events separately
    }

In the case of an ObservableCollection<T>, the delegate is directly on a field, so less indirection is required:

ObservableCollection<SomeType> list = ...
NotifyCollectionChangedEventHandler handler = (NotifyCollectionChangedEventHandler)
    list.GetType()
    .GetField("CollectionChanged", BindingFlags.Instance | BindingFlags.NonPublic)
    .GetValue(list);
Marc Gravell
I actually do need to trigger the GetInvocationList from the event, any workaround?
Shimmy
@Shimmy - updating answer.
Marc Gravell
In my case, I need to get the invocation list of INotifyCollectionChanged.CollectionChanged event :(I am struggling with it badly
Shimmy
@Shimmy - is this the WPF window? Or what? The *exact* (concrete) type matters when talking about reflection. So what is it that implements `INotifyCollectionChanged` in your case?
Marc Gravell
Actually maybe it's not the right place, please take a look, http://stackoverflow.com/questions/2178703/need-help-translating-c-to-vb
Shimmy