【问题标题】:How to obtain the invocation list of any event如何获取任意事件的调用列表
【发布时间】:2012-01-09 14:38:40
【问题描述】:

如何在WPF中获取控件的委托列表表单事件。

我已尝试以下代码,但它会将字段信息返回为 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);

【问题讨论】:

标签: c# .net wpf reflection .net-4.0


【解决方案1】:

我不知道为什么人们说不可能:

假设你想暂时禁用任何事件,你可以创建一个这样的方法:

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;

}|

你可以这样称呼它:

var events = textbox1.DisableEvents("GotFocus")

如果你想再次添加它们,你只需要浏览事件列表。

【讨论】:

  • 你在最后一句“只需浏览事件列表”是什么意思?
  • 我的意思是您可以迭代您在 DisableEvents 方法中获得的列表并再次添加这些事件。但实际上现在有一个最简单的方法可以做到这一点,就是调用 GetInvocationList 来检索所有的委托。
  • 为我工作!使用简单的 Control.Click 事件进行测试。 +1
  • 这不是通用方式,它只适用于 Windows.Form.Controls(但这是 OP 想要的)
【解决方案2】:

上一个答案中描述的方法非常有效。 但是,要使用它,您需要确保事件不是作为字段实现,而是作为属性实现。同时,对委托的引用存储在 Dictionary 类的内部对象中。 你可以在这里阅读更多:How to: Use a Dictionary to Store Event Instances (C# Programming Guide)

【讨论】:

    【解决方案3】:

    Kenneth J. Sanchez Venegas 推荐的反射是获取类外事件调用列表的唯一方法。但是当性能很重要时,我们可以像这样使用已编译的 lambdas 创建一个助手:

    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;
        }
    }
    

    假设我们将有一个这个助手的实例并且每次都使用它:

    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;
    
    ...
    

    *我正在使用此代码从 Json.NET JsonSerializer 中提取设置,因此示例取自该上下文

    **使用 C# 9.0 语法特性的代码示例,在 .NET 5 和 .NET 6 上测试。

    【讨论】:

      猜你喜欢
      • 2011-07-09
      • 1970-01-01
      • 2010-11-19
      • 2022-10-05
      • 1970-01-01
      • 2015-03-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多