Determine List of Event Handlers Bound to Event

Determine list of event handlers bound to event

In short, you're not meant to do this - but for debugging purposes...

An event is often backed by a private field - but not with controls; they use the EventHandlerList approach. You would have to access the form's protected Events member, looking for the object mapped to the (private) EVENT_FORMCLOSING object.

Once you have the FormClosingEventHandler, GetInvocationList should do the job.


using System;
using System.ComponentModel;
using System.Reflection;
using System.Windows.Forms;
class MyForm : Form
{
public MyForm()
{ // assume we don't know this...
Name = "My Form";
FormClosing += Foo;
FormClosing += Bar;
}

void Foo(object sender, FormClosingEventArgs e) { }
void Bar(object sender, FormClosingEventArgs e) { }

static void Main()
{
Form form = new MyForm();
EventHandlerList events = (EventHandlerList)typeof(Component)
.GetProperty("Events", BindingFlags.NonPublic | BindingFlags.Instance)
.GetValue(form, null);
object key = typeof(Form)
.GetField("EVENT_FORMCLOSING", BindingFlags.NonPublic | BindingFlags.Static)
.GetValue(null);

Delegate handlers = events[key];
foreach (Delegate handler in handlers.GetInvocationList())
{
MethodInfo method = handler.Method;
string name = handler.Target == null ? "" : handler.Target.ToString();
if (handler.Target is Control) name = ((Control)handler.Target).Name;
Console.WriteLine(name + "; " + method.DeclaringType.Name + "." + method.Name);
}
}
}

Check to see if an event handler is attached to and event

You can implement a class inherited from EventHandler. For this class you can implement any additional behavior you want. For instance, you can create a collection which will hold object-event maps and you can implement a method which searches for a given pair or pattern.

Get all events handlers attached to an element

The short answer is that it's not possible to reliably determine all the listeners on an element just using javascript.

The long answer is that there is no standard mechanism to list all attached listeners. Some libraries keep a list of listeners that have been attached using the library, but don't necessarily know about other listeners. Listeners added in the markup or as element properties can be found by testing related element properties and attributes (somewhat tedious testing for onclick, onchange, onblur, etc. for each element). But it's impossible to find a listener added using addEventListener or attachEvent unless a reference has been kept somewhere and it's made available (see comment about libraries).

Also, there are "delegated" listeners that give the appearance of being attached to an element when in fact they are attached to a parent element.

How to verify that an event handler has been added

If you just want to verify in the debugger that the event handler was added, you can cast the event to a System.Delegate and look at the invocation list. This can be done for instance in the Visual Studio Immediate Window. Here I am examining the invocation list of an event called "MyEvent", which turns out to have 3 handlers added:

((System.Delegate)MyEvent).GetInvocationList()
{System.Delegate[3]}
[0]: {Method = {Void handler11_MessageReceived(System.Object, MyProject.MyEventArgs)}}
[1]: {Method = {Void handler21_MessageReceived(System.Object, MyProject.MyEventArgs)}}
[2]: {Method = {Void handler21_MessageReceived(System.Object, MyProject.MyEventArgs)}}

You can also browse the invocation list in the Watch window.

It might even be useful to do this if you are unsure whether some event listeners had been properly disposed of later, for example.

Update: How to check programmatically that one event contains another.

Checking programmatically that an event handler has been added to an event is more complex than one might imagine, because events are actually a form of MulticastDelegate. A multicast delegate can be either an "atomic" delete created by (e.g.) a delegate statement or lambda expression, or a combined delegate created by adding together two or more multicast delegates. And as it turns out, when one combined delegate is added to or subtracted from a second, what actually happens is that its invocation list is added to or subtracted from another. E.g.

        Action a = () => Console.WriteLine("a");
Action b = () => Console.WriteLine("b");
Action c = () => Console.WriteLine("c");
Action d = () => Console.WriteLine("d");

var ab = a + b;
var cd = c + d;
var ad = a + d;
var bc = b + c;

var abcd = ab + cd;
var adbc = ad + bc;
var abc = abcd - d;
var bcd = abcd - a;

bool test1 = (abcd == (a + b + c + d)); // returns true
bool test2 = abcd.GetInvocationList().Contains(a); // returns true;
bool test3 = abcd.GetInvocationList().Contains(ab); // returns **false**;
bool test4 = abc.GetInvocationList().Contains(d); // returns false

If you want to create a public static extension method to check to see whether your handler was added to an event, it should correctly handle the case of a multicast delegate being added. But, does that just mean that the event has all the delegates of the handler? Or do they need to be bound together in sequence, since Delegate.Combine does preserve order? The following extension method checks for the latter:

public static class EventHelper
{
/// <summary>
/// Return true if all the atomic delegates in the multicast delegate handler are bound into the
/// publisher, grouped together and in the same order.
/// </summary>
/// <param name="publisher"></param>
/// <param name="handler"></param>
/// <returns></returns>
public static bool HasBound(this Delegate publisher, Delegate handler)
{
if (publisher == null || handler == null)
return false;
if (publisher == handler)
return true;
var publisherList = publisher.GetInvocationList();
var handlerList = handler.GetInvocationList();
return (publisherList.SublistIndex(handlerList, 0) >= 0);
}

public static bool HasBound<TEventArgs>(this EventHandler<TEventArgs> publisher, EventHandler<TEventArgs> handler) where TEventArgs : EventArgs
{
return HasBound((Delegate)publisher, (Delegate)handler);
}

public static bool HasBound(this EventHandler publisher, EventHandler handler)
{
return HasBound((Delegate)publisher, (Delegate)handler);
}
}

public static class ListHelper
{
public static int SublistIndex<T>(this IList<T> list, IList<T> sublist, int start)
{
var comparer = EqualityComparer<T>.Default;
for (int listIndex = start, end = list.Count - sublist.Count + 1; listIndex < end; listIndex++)
{
int count = 0;
while (count < sublist.Count && comparer.Equals(sublist[count], list[listIndex + count]))
count++;
if (count == sublist.Count)
return listIndex;
}
return -1;
}
}

And here are the results of testing:

        bool test08 = a.HasBound(a); // returns true;
bool test09 = b.HasBound(a); // returns true;
bool test10 = abcd.HasBound(a + b + c + d); // returns true;
bool test11 = abcd.HasBound(adbc); // returns false - wrong order.
bool test12 = abcd.HasBound(a); // returns true;
bool test13 = cd.HasBound(a); // return false
bool test14 = bcd.HasBound(bc); // returns true despite the fact that bcd was not created directly from bc.
bool test15 = abcd.HasBound(ad); // returns false because the "ad" events are not adjacent in abcd.

Honestly, I would not do this outside of debugging however. Actually coding a check to see if a listener is bound to an event seems wrong.

Update 2 Is the real question here how to fetch all the event handlers from a Microsoft UIElement? It has to be done through some nasty reflection that isn't guaranteed to work in future .Net versions, as is shown here: How to remove all Click event handlers? and here: How would it be possible to remove all event handlers of the 'Click' event of a 'Button'?.

Get list of functions bound to element via parent ( via .on() )

There's two parts to your question which I'll try to answer. The first question:

How can get the name of the function bound to my element?

This one is fairly straight forward, and you're actually on the right path with your fiddle. When you bind an event handler, the handler's event type is added to jQuery's internal _data object. So, for instance, if you do:

$('div').on('click', function() { ... })

Then internally, jQuery will create an object {} and add a key, click to that. Like this:

{'click': []}

The key points to an array of objects, which are all the event handlers you've bound with the click type.

If you were to add a custom event like this:

$('div').on('myfancyhandler', function() {...})

And called $._data($('div')[0], 'events'), you would see an object returned like:

{'myfancyhandler': []}

So, when you call $._data( el , 'events'), you can access the handler property (which points to an array of your handlers) like this:

// Bind two event handlers - click and our custom myfancyhandler event
$('div').on('click myfancyhandler', function() { });

// Returns object {click: [], myfancyhandler: []}
var handlers = $._data($('div')[0], 'events');

// Access handler (function passed for callback)
var handler = handlers.click[0].handler; // dot notation, square bracket works too

Now, say you do something like this:

$('div').on('click', function(){ alert(1) });
$('div').on('click', function(){ alert(2) });

var handlers = $._data($('div')[0], 'events');

The _data object would have one click property with two items in the array, like this:

{click: [
{
handler: function() { alert(1) }
/* additional properties */
},
{
handler: function() { alert(2) }
/* additional properties */
}
]}

Visually (in our console), you could see that the first object is the function that alerts 1, and the second function alerts 2. But, what if you needed to programmatically check? For that, we would need to name our anonymous functions, like so:

$('div').on('click', function alert1(){ alert(1) });
$('div').on('click', function alert2(){ alert(2) });

var handlers = $._data($('div')[0], 'events');

Which would return us:

{click: [
{
handler: function alert1() { alert(1) }
/* additional properties */
},
{
handler: function alert2() { alert(2) }
/* additional properties */
}
]}

Which we could then check (and get the name) like this:

var handlers = $._data($('div')[0], 'events');
handlers.click.forEach(function(obj, idx){
console.log(obj.handler.name);
});

That would output:

console
> alert1
> alert2

In addition, you could pass a reference to the function like this:

function ourCoolHandler() { .. does something .. }

// Bind our cool handler
$('div').on('keyup', ourCoolHandler);

// Get event object
var evtObj = $._data($('div')[0], 'events')

// Get function name from handler object
console.log(evtObj.keyup[0].handler.name) // ourCoolHandler

As for your second question, which is:

Also, if the function is actually bound to a parent element, is there anyway to access the functions name if I only have the the child?

What I think you're referring to is jQuery's event delegation. Something like this:

$('#parentElement').on('click', '#childElement', function() { .. do something } );

And then somehow get the function named based on #childElement. The short answer is no, this can't be done. You're right when you say Surely these must be stored internally somewhere by jQuery.

In fact, let's walk through what happens when we do this:

$('#parentElement').on('click', '#childElement', function doSomething() { .. do something } );

jQuery finds the #parentElement and registers a click handler. But, it sees that you want to only handle the click event when the selector is #childElement. (If this is omitted, the event is always triggered.) Now, if we do something like this:

$._data($('#childElement')[0], 'events');

nothing will be returned. However, if we do something like this:

$._data($('#parentElement')[0], 'events');

an object is returned with our click handler. Something that looks like this:

{
click:
// 0 index array
0: {
handler: function doSomething,
selector: '#childElement'
}
delegateCount: 1
}

As you can see in the returned object, this is where the child element is stored by jQuery. The events are filtered when delegateCount is greater than 0, and it's filtered against the selector passed in.

Now earlier I said the short answer is no. The longer answer is you could - you could (in theory) traverse up the DOM and check each element to see if it has a event object, has a delegateCount, and if the selector matches the child element. That'd be a lot of logic to implement, though.

Finally, one last trick: You can see all event handlers bound to an element in Chrome Developer Tool. Open it up, click an element, and choose the "Event Listeners" tab on the right. If there's an event handler bound, it should show up there. Something like this:

Sample Image

Good luck!

How to find event listeners on a DOM node in JavaScript or in debugging?

If you just need to inspect what's happening on a page, you might try the Visual Event bookmarklet.

Update: Visual Event 2 available.

C# Know how many EventHandlers are set?

To find the number of event handlers, you can use this code:

InternetConnectionAvailableEvent.GetInvocationList().Length;

The following code demonstrates that MyEvent -= null does not clear the list of handlers.

public static event EventHandler MyEvent;

[STAThread]
static void Main()
{
MyEvent += (s,dea) => 1.ToString();
MyEvent -= null;

Console.WriteLine(MyEvent.GetInvocationList().Length);
// Prints 1
MyEvent = null;
Console.WriteLine(MyEvent == null);
// Prints true
}

To clear the list (which is probably never a good idea), you can set the event to null (as long as you are in the class that declared the event).

Can I find events bound on an element with jQuery?

In modern versions of jQuery, you would use the $._data method to find any events attached by jQuery to the element in question. Note, this is an internal-use only method:

// Bind up a couple of event handlers
$("#foo").on({
click: function(){ alert("Hello") },
mouseout: function(){ alert("World") }
});

// Lookup events for this particular Element
$._data( $("#foo")[0], "events" );

The result from $._data will be an object that contains both of the events we set (pictured below with the mouseout property expanded):

Console output for $._

Then in Chrome, you may right click the handler function and click "view function definition" to show you the exact spot where it is defined in your code.



Related Topics



Leave a reply



Submit