Click here to Skip to main content
Click here to Skip to main content
Go to top

Get Delegate from Event's Subscription

, 4 Apr 2009
Rate this:
Please Sign up or sign in to vote.
This code allows you to get a delegate that subscribed to a Control's event. The technique used is applicable to events in general.

Introduction

This code allows you to get a delegate that subscribed to a Control's event. The technique used is applicable to events in general.

Background

Recently I came across a problem. The task was to extend the functionality of some third-party control. No source code is available, all public classes are sealed. I needed to change the behavior of a button that showed SaveFileDialog and then rendered a report with default settings. There was no way to change the settings of render but to rewrite all control's toolbar.

But all I needed was to change the handler of a Click event on a control's button. To do that, one should unsubscribe old handler (-=) and subscribe a new one (+=). The question is - how to get the old handler when you don't have access to it?

To think about it, the same situation arises when a developer wants to unsubscribe from an anonymous delegate or lambda expression. There is no name to reference them (apart from storing it beforehand in a variable. But anonymous delegates, you know... should stay anonymous).

Research

First let's see the event storing background. I was to modify the behavior of a ToolStripButton so we'll dig from it.

Reflector shows that there are only two events in the ToolStripButton class. But there are a bunch of them in the base ToolStripItem class. The Click event for example is defined like:

public event EventHandler Click
{
    add { base.Events.AddHandler(EventClick, value); }
    remove { base.Events.RemoveHandler(EventClick, value); }
}

We see that there is an Events collection that stores all subscribed delegates. And the kind of event is determined by the key object - EventClick in this case:

public abstract class ToolStripItem: Component, ...
{
    ...
    internal static readonly object EventClick;
    ...
}

The Events collection (of type EventHandlerList) is stored in System.ComponentModel.Component class and has an interesting method Find:

public sealed class EventHandlerList : ...
{
    ...
    private ListEntry Find(object key);
    ...
    private sealed class ListEntry
    {
        ...
        internal Delegate handler;
        ...
    }
}

Implementation

In summary, to get access to a delegate that subscribed to the Click event, one could write something like:

control.Events.Find( control.EventClick ).handler 

But encapsulation won't allow it. Luckily there is a way to achieve that - reflection. The code will be:

var handler = (EventHandler) GetDelegate( toolStripButton, "EventClick" );
private static object GetDelegate( Component issuer, string keyName )
{
    // Get key value for a Click Event
    var key = issuer
        .GetType( )
        .GetField( keyName, BindingFlags.Static | 
		BindingFlags.NonPublic | BindingFlags.FlattenHierarchy )
        .GetValue( null );
    // Get events value to get access to subscribed delegates list
    var events = typeof( Component )
        .GetField( "events", BindingFlags.Instance | BindingFlags.NonPublic )
        .GetValue( issuer );
    // Find the Find method and use it to search up listEntry for corresponding key
    var listEntry = typeof( EventHandlerList )
        .GetMethod( "Find", BindingFlags.NonPublic | BindingFlags.Instance )
        .Invoke( events, new object[] { key } );
    // Get handler value from listEntry 
    var handler = listEntry
        .GetType( )
        .GetField( "handler", BindingFlags.Instance | BindingFlags.NonPublic )
        .GetValue( listEntry );
    return handler;
}

Available event keys (it's not only "EventClick" you know...) can be accessed with a method:

private static IEnumerable<string> GetEventKeysList( Component issuer )
{
    return
        from key in issuer.GetType( ).GetFields( BindingFlags.Static | 
		BindingFlags.NonPublic | BindingFlags.FlattenHierarchy )
        where key.Name.StartsWith( "Event" )
        select key.Name;
}

The code with the article contains a project that demonstrates all techniques described.

Conclusion

The event manipulation syntax in C# was made restrictive with a purpose. It will not break event encapsulation. It is by design and it is the right thing.

A situation when you need to access an unknown delegate that subscribed to a known event is rare. But it exists. I hope this article saved you some time when you came across such a problem.

History

  • 5th April, 2009: Initial post

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

Alexander Kostikov

Russian Federation Russian Federation
No Biography provided

Comments and Discussions

 
QuestionPotential uses? PinmemberDmitri Nesteruk4-Apr-09 23:55 
AnswerRe: Potential uses? PinmemberAlexander Kostikov5-Apr-09 7:13 
AnswerRe: Potential uses? PinmemberOrlandoCurioso5-Apr-09 9:28 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Mobile
Web04 | 2.8.140916.1 | Last Updated 5 Apr 2009
Article Copyright 2009 by Alexander Kostikov
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid