G
Gnarlito
I have an application in which I need to fire an event from some class
other than the one that has defined the event. I am trying to do this
by finding the delegate associated with the event in question, and then
by calling GetInvocationList() and invoking each EventHandler in turn.
And although I've found some nice links to back-door techniques on how
to retrieve a delegate for a given event, those techniques don't seem
to work for me. I've figured out an alternative method, but it's not
quite working the way I want it to...yet.
Alex Feinman
(http://www.opennetcf.org/PermaLink.aspx?guid=4cf118ee-5668-452e-af2c-f3a0c6d43a7f)
and others have suggested a technique where you cycle through the
private fields of a type looking for one that has the same name as the
event of interest. For example, to find the delegate for the Click
event for button1, and then emulate the button-click event on that
button:
Type t = typeof(Control);
FieldInfo fi = t.GetField("Click", BindingFlags.NonPublic |
BindingFlags.Instance);
Delegate handler = fi.GetValue(button1) as Delegate;
foreach (Delegate d in handler.GetInvocationList())
{ d.Invoke(button1, new EventArgs()); }
Only trouble is, this doesn't work for me. I can't find this private
field. What "almost" works instead is something like the following, but
it returns *all* delegates, and by, extension, all event handlers, for
the associated control, not just the ones associated with the Click
event or some other event of interest. So my question is, given an
instance of an EventHandler or Delegate, is it possible to determine
which Event that EventHandler or Delegate is associated with?
// button1 is defined as a Button, and it has added an EventHandler
// to the chain of handlers that handle the Click event. It has also
// defined some handlers to handle other events and has added those
// handlers to the chain of handlers associated with those other
// events. Every non-null delegate for the control is returned by
// findDelegates(), not just the Button.Click event delegate.
ArrayList allDelegates = findDelegates(button1);
// The following will cycle through ALL delegates. not just the ones
// associated with the Click event. This is not good....
foreach (Delegate del in allDelegates)
{
Delegate[] theseDelegates = del.GetInvocationList();
foreach (EventHandler thisHandler in theseDelegates)
{
thisHandler.Invoke(buttonDemo, new EventArgs());
}
}
// What I would like to do:
// Delegate buttonClickDelegate = findDelegateFor(button1, "Click");
// and then invoke this delegate's handlers as above.
....
protected ArrayList findDelegates(object o)
{
ArrayList rc = new ArrayList();
// Event-handler information for a given instance
// of an object should be accessible through a private field
// of type EventHandlerList. And this field probably won't
// be defined for the current class type of object o.
// The field will probably be defined as part of a base
// class that is buried several classes deep in the
// inheritance chain.
// findFieldType will recurse through the type tree for
// the caller-specified object until it finds a field of type
// EventHandlerList
FieldInfo f = findFieldType(o.GetType(),
typeof(EventHandlerList));
if (null == f)
{ return rc; }
EventHandlerList ehl = (EventHandlerList)f.GetValue(o);
if (null == ehl)
{ return rc; }
// The EventHandlerList class doesn't expose what we need.
// We need to get to the private fields...to the contents of the
// linked list that is contained within the class. Each entry in
// the linked list is of type ListEntry (a private nested class
// apparently defined within EventHandlerList) and represents
// one delegate. The event handlers for that delegate can then
// be accessed using the publicly visible GetInvocationList()
// method.
FieldInfo headFieldInfo = ehl.GetType().GetField("head",
BindingFlags.NonPublic | BindingFlags.Instance);
if (null == headFieldInfo)
{ return rc; }
object headField = headFieldInfo.GetValue(ehl);
// findDelegate will populate the caller-supplied ArrayList with
// all delegates in the linked listed headed by headField.
// What we really need is a way to get only those delegates
// associated with a specific event.
findOneDelegate(headField, ref rc);
return rc;
}
/// <summary>
/// Walk through the linked list represented by the caller-supplied
/// ListEntry object and extract delegates from that list.
/// Each event exposed by a given control is represented by one linked
/// list entry. There may be multiple delegates to handle the event,
/// but those delegates are retrieved using the GetInvocationList()
/// method and not by using this back-door stuff.
/// </summary>
/// <param name="o">A linked-list entry of type ListEntry</param>
/// <param name="delegateList">A reference to a delegate list ArrayList
/// that is recursively populated</param>
protected void findOneDelegate(object o, ref ArrayList delegateList)
{
if (null == o)
{ return; }
object next = null;
foreach (FieldInfo fx in o.GetType().GetFields(
BindingFlags.Public |
BindingFlags.NonPublic |
BindingFlags.Instance))
{
object thisObj = fx.GetValue(o);
// thisObj is of type ListEntry. It has 3 fields:
// next -- the next ListEntry in the linked list,
// key -- an object. Obviously this identifies the event, but how??
// delegate -- a delegate
if (fx.Name.ToLower() == "next")
{ next = thisObj; }
if (fx.Name.ToLower() == "key")
{
Trace.WriteLine("Key type: " + thisObj.GetType().Name +
"; hashcode: " + thisObj.GetHashCode().ToString());
}
if (fx.FieldType.Name.ToLower() == "delegate")
{ delegateList.Add((Delegate)thisObj); }
}
if (next != null)
{ findOneDelegate(next, ref delegateList); }
}
other than the one that has defined the event. I am trying to do this
by finding the delegate associated with the event in question, and then
by calling GetInvocationList() and invoking each EventHandler in turn.
And although I've found some nice links to back-door techniques on how
to retrieve a delegate for a given event, those techniques don't seem
to work for me. I've figured out an alternative method, but it's not
quite working the way I want it to...yet.
Alex Feinman
(http://www.opennetcf.org/PermaLink.aspx?guid=4cf118ee-5668-452e-af2c-f3a0c6d43a7f)
and others have suggested a technique where you cycle through the
private fields of a type looking for one that has the same name as the
event of interest. For example, to find the delegate for the Click
event for button1, and then emulate the button-click event on that
button:
Type t = typeof(Control);
FieldInfo fi = t.GetField("Click", BindingFlags.NonPublic |
BindingFlags.Instance);
Delegate handler = fi.GetValue(button1) as Delegate;
foreach (Delegate d in handler.GetInvocationList())
{ d.Invoke(button1, new EventArgs()); }
Only trouble is, this doesn't work for me. I can't find this private
field. What "almost" works instead is something like the following, but
it returns *all* delegates, and by, extension, all event handlers, for
the associated control, not just the ones associated with the Click
event or some other event of interest. So my question is, given an
instance of an EventHandler or Delegate, is it possible to determine
which Event that EventHandler or Delegate is associated with?
// button1 is defined as a Button, and it has added an EventHandler
// to the chain of handlers that handle the Click event. It has also
// defined some handlers to handle other events and has added those
// handlers to the chain of handlers associated with those other
// events. Every non-null delegate for the control is returned by
// findDelegates(), not just the Button.Click event delegate.
ArrayList allDelegates = findDelegates(button1);
// The following will cycle through ALL delegates. not just the ones
// associated with the Click event. This is not good....
foreach (Delegate del in allDelegates)
{
Delegate[] theseDelegates = del.GetInvocationList();
foreach (EventHandler thisHandler in theseDelegates)
{
thisHandler.Invoke(buttonDemo, new EventArgs());
}
}
// What I would like to do:
// Delegate buttonClickDelegate = findDelegateFor(button1, "Click");
// and then invoke this delegate's handlers as above.
....
protected ArrayList findDelegates(object o)
{
ArrayList rc = new ArrayList();
// Event-handler information for a given instance
// of an object should be accessible through a private field
// of type EventHandlerList. And this field probably won't
// be defined for the current class type of object o.
// The field will probably be defined as part of a base
// class that is buried several classes deep in the
// inheritance chain.
// findFieldType will recurse through the type tree for
// the caller-specified object until it finds a field of type
// EventHandlerList
FieldInfo f = findFieldType(o.GetType(),
typeof(EventHandlerList));
if (null == f)
{ return rc; }
EventHandlerList ehl = (EventHandlerList)f.GetValue(o);
if (null == ehl)
{ return rc; }
// The EventHandlerList class doesn't expose what we need.
// We need to get to the private fields...to the contents of the
// linked list that is contained within the class. Each entry in
// the linked list is of type ListEntry (a private nested class
// apparently defined within EventHandlerList) and represents
// one delegate. The event handlers for that delegate can then
// be accessed using the publicly visible GetInvocationList()
// method.
FieldInfo headFieldInfo = ehl.GetType().GetField("head",
BindingFlags.NonPublic | BindingFlags.Instance);
if (null == headFieldInfo)
{ return rc; }
object headField = headFieldInfo.GetValue(ehl);
// findDelegate will populate the caller-supplied ArrayList with
// all delegates in the linked listed headed by headField.
// What we really need is a way to get only those delegates
// associated with a specific event.
findOneDelegate(headField, ref rc);
return rc;
}
/// <summary>
/// Walk through the linked list represented by the caller-supplied
/// ListEntry object and extract delegates from that list.
/// Each event exposed by a given control is represented by one linked
/// list entry. There may be multiple delegates to handle the event,
/// but those delegates are retrieved using the GetInvocationList()
/// method and not by using this back-door stuff.
/// </summary>
/// <param name="o">A linked-list entry of type ListEntry</param>
/// <param name="delegateList">A reference to a delegate list ArrayList
/// that is recursively populated</param>
protected void findOneDelegate(object o, ref ArrayList delegateList)
{
if (null == o)
{ return; }
object next = null;
foreach (FieldInfo fx in o.GetType().GetFields(
BindingFlags.Public |
BindingFlags.NonPublic |
BindingFlags.Instance))
{
object thisObj = fx.GetValue(o);
// thisObj is of type ListEntry. It has 3 fields:
// next -- the next ListEntry in the linked list,
// key -- an object. Obviously this identifies the event, but how??
// delegate -- a delegate
if (fx.Name.ToLower() == "next")
{ next = thisObj; }
if (fx.Name.ToLower() == "key")
{
Trace.WriteLine("Key type: " + thisObj.GetType().Name +
"; hashcode: " + thisObj.GetHashCode().ToString());
}
if (fx.FieldType.Name.ToLower() == "delegate")
{ delegateList.Add((Delegate)thisObj); }
}
if (next != null)
{ findOneDelegate(next, ref delegateList); }
}