Reputation: 570
How to get the delegate list form event of the control in WPF.
I have tried the following code but it will return the field info as null
TextBox cont = new TextBox();
cont.TextChanged += new TextChangedEventHandler(cont_TextChanged);
FieldInfo fi = cont.GetType().GetField("TextChanged", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.GetField | BindingFlags.Public | BindingFlags.Static);
Delegate del = (Delegate)fi.GetValue(cont);
Upvotes: 10
Views: 18706
Reputation: 266
Reflection, as recommended by Kenneth J. Sanchez Venegas, is the only way to get an invocation list of event outside of a class. But when performance is important, we can make a helper with compiled lambdas like this:
public class EventHelper<TClass, TDelegate> where TDelegate : Delegate
{
public EventHelper(string eventName)
{
var fieldInfo = typeof(TClass).GetField(eventName, BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.Instance) ??
throw new ArgumentException("Event was not found", nameof(eventName));
var thisArg = Expression.Parameter(typeof(TClass));
var body = Expression.Convert(Expression.Field(thisArg, fieldInfo), typeof(TDelegate));
Get = Expression.Lambda<Func<TClass, TDelegate?>>(body, thisArg).Compile();
}
public Func<TClass, TDelegate?> Get { get; }
public IEnumerable<TDelegate> GetInvocationList(TClass forInstance)
{
var eventDelegate = Get(forInstance);
if (eventDelegate is null)
yield break;
foreach (var d in eventDelegate.GetInvocationList())
yield return (TDelegate)d;
}
}
Assuming we will have one instance of this helper and use it every time:
private static readonly EventHelper<JsonSerializer, EventHandler<ErrorEventArgs>> _errorHelper = new(nameof(JsonSerializer.Error));
...
JsonSerializerSettings settings = new();
foreach (var errorHandler in _errorHelper.GetInvocationList(serializer))
settings.Error += errorHandler;
...
*I'm using this code to extract settings from Json.NET JsonSerializer so examples are taken from that context
**Code examples using C# 9.0 syntax features, tested on .NET 5 and .NET 6.
Upvotes: 2
Reputation: 55
The approach described in the previous answer works perfectly. However, to use it, you need to be sure that the event is implemented not as a field, but as a property. At the same time, the reference to the delegate is stored in the internal object of the Dictionary class. You can read more here: How to: Use a Dictionary to Store Event Instances (C# Programming Guide)
Upvotes: 2
Reputation: 101
I dont know why the people say it's not possible:
Lets say you want to disable any event temporary, you can create a method like this:
static Delegate[] DisableEvents(this Control ctrl, string eventName)
{
PropertyInfo propertyInfo = ctrl.GetType().GetProperty("Events", BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.Instance);
EventHandlerList eventHandlerList = propertyInfo.GetValue(ctrl, new object[] { }) as EventHandlerList;
FieldInfo fieldInfo = typeof(Control).GetField("Event"+eventName, BindingFlags.NonPublic | BindingFlags.Static);
object eventKey = fieldInfo.GetValue(ctrl);
var eventHandler = eventHandlerList[eventKey] as Delegate;
Delegate[] invocationList = eventHandler.GetInvocationList();
foreach (EventHandler item in invocationList)
{
ctrl.GetType().GetEvent(eventName).RemoveEventHandler(ctrl, item);
}
return invocationList;
}|
You can call it like this:
var events = textbox1.DisableEvents("GotFocus")
If you want to add them again you just need to go through the events list.
Upvotes: 10