清除事件所有委托方法

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了清除事件所有委托方法相关的知识,希望对你有一定的参考价值。

问题背景

在做winform报表开发时,FastReport是一个很好的工具,它提供了一些封装好的控件,可以很方便快速的开发打印报表。其中有一个控件是用于预览报表的,所有功能按钮事件方法都是封装好的。

技术分享图片

一般情况下这些功能按钮封装好的事件能满足基本的使用,不需要写多余的代码,但是在实际使用过程中,经常会遇到一些需要保存打印记录等操作情景,原有的按钮事件无法满足,就有了需要替换掉原按钮事件调用的方法的需求。

解决方案

因为控件是封装好的,无法直接知道事件具体调用哪些方法,通过 -= 的方式取消订阅事件,于是便想到了反射,通过反射将该事件的调用列表获取出来,然后全部清除掉,再通过 += 的方式订阅自定义方法。经过一番资料查找,整理出了一个帮助类:

 public static class EventHelper
 {
        private const BindingFlags BINDINGFLAGS = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.IgnoreCase | BindingFlags.Static;

        /// <summary>
        /// 获取事件委托调用列表
        /// </summary>
        /// <typeparam name="T">对象类型</typeparam>
        /// <param name="obj">事件所属对象</param>
        /// <param name="eventName">事件名称</param>
        /// <returns></returns>
        public static Delegate[] GetInvocationList<T>(this T obj, string eventName) where T : class
        {
            if (obj == null || string.IsNullOrEmpty(eventName))
            {
                return null;
            }
            var eventInfo = obj.GetType().GetEvent(eventName);
            if (eventInfo == null)
            {
                return null;
            }
            Delegate d = null;
            var fieldInfo = eventInfo.DeclaringType.GetField(eventName, BINDINGFLAGS);
            if (fieldInfo != null)
            {
                d = (Delegate)fieldInfo.GetValue(obj);
                return d == null ? null : d.GetInvocationList();
            }
            fieldInfo = eventInfo.DeclaringType.GetField("Event" + eventName, BINDINGFLAGS);
            if (fieldInfo == null)
            {
                fieldInfo = eventInfo.DeclaringType.GetField("EVENT_" + eventName.ToUpper(), BINDINGFLAGS);
            }
            if (fieldInfo == null)
            {
                return null;
            }
            PropertyInfo propertyInfo = obj.GetType().GetProperty("Events", BINDINGFLAGS);
            EventHandlerList eventHandlerList = (EventHandlerList)propertyInfo.GetValue(obj, null);
            d = eventHandlerList[fieldInfo.GetValue(obj)];
            return d == null ? null : d.GetInvocationList();
        }

        /// <summary>
        /// 清除事件委托调用列表
        /// </summary>
        /// <typeparam name="T">对象类型</typeparam>
        /// <param name="obj">事件所属对象</param>
        /// <param name="eventName">事件名称</param>
        public static void ClearInvocationList<T>(this T obj, string eventName) where T : class
        {
            var handlers = obj.GetInvocationList(eventName);
            if (handlers == null)
            {
                return;
            }
            var eventInfo = obj.GetType().GetEvent(eventName);
            foreach (var handler in handlers)
            {
                eventInfo.RemoveEventHandler(obj, handler);//移除已订阅的eventName类型事件
            }
        }
   }

通过这个帮助类,可以方便的获取或清除事件的调用列表,使用方式如下:

button.GetInvocationList("Click");
button.ClearInvocationList("Click");

 

PS:学业不精,有些专业知识理解的不是很透彻,术语maybe不正确导致阅读理解错误,大神不理赐教。

以上是关于清除事件所有委托方法的主要内容,如果未能解决你的问题,请参考以下文章

原生js如何绑定a连接点击事件?

对“xxx”类型的已垃圾回收委托进行了回调。这可能会导致应用程序崩溃损坏和数据丢失。向非托管代码传递委托时,托管应用程序必须让这些委托保持活动状态,直到确信不会再次调用它们。 错误解决一例。(代码片段

委托事件

帮你理清 C# 委托事件ActionFunc

帮你理清 C# 委托事件ActionFunc

泛型委托事件详解示例