0
点赞
收藏
分享

微信扫一扫

WPF 通过反射获取控件(自身及style)注册的路由事件

boomwu 2022-04-04 阅读 23
wpf

文章目录


前言

通过反射获取到控件的路由事件时,如何获取到控件本身的事件,和通过Style设置的事件,这个问题似乎没有优美的解法。


stackoverflow中常见的方法通常是查询UIElement.EventHandlersStore获取控件对应的事件
https://stackoverflow.com/questions/44283395/copy-all-event-handlers-from-one-control-to-another-at-runtime

/// <summary>
        /// Get a list of RoutedEventHandlers
        /// Credit: Douglas : https://stackoverflow.com/a/12618521/3971575
        /// </summary>
        /// <param name="element"></param>
        /// <param name="routedEvent"></param>
        /// <returns></returns>
        public RoutedEventHandlerInfo[] GetRoutedEventHandlers(UIElement element, RoutedEvent routedEvent)
        {
            // Get the EventHandlersStore instance which holds event handlers for the specified element.
            // The EventHandlersStore class is declared as internal.
            PropertyInfo eventHandlersStoreProperty = typeof(UIElement).GetProperty("EventHandlersStore", BF.Instance | BF.NonPublic);
            object eventHandlersStore = eventHandlersStoreProperty.GetValue(element, null);

            // If no event handlers are subscribed, eventHandlersStore will be null.
            // Credit: https://stackoverflow.com/a/16392387/1149773
            if (eventHandlersStore == null)
                return null;

            // Invoke the GetRoutedEventHandlers method on the EventHandlersStore instance 
            // for getting an array of the subscribed event handlers.
            MethodInfo getRoutedEventHandlers = eventHandlersStore.GetType().GetMethod("GetRoutedEventHandlers", BF.Instance | BF.Public | BF.NonPublic);

            return (RoutedEventHandlerInfo[])getRoutedEventHandlers.Invoke(eventHandlersStore, new object[] { routedEvent });
        }

遇到的问题

但我在使用中发现,这种方法只能查询到直接在控件中注册的事件

//在xaml中定义的
<Button Style="{StaticResource menuButton}" MouseLeave="xxx">

//在c#代码中增加的
xxButton.MouseLeave += xxx

//其他直接作用到控件中的方式
....

如果在控件style中通过<EventSetter>设置的事件,是无法通过上述的反射代码获取到的,在网上搜索了下相关问题,但似乎没找到,正当我一筹莫展时,发现了微软官方早就开源了.netframework源码,这倒是给了我一点动力去探索了
浏览了下Style的源码,发现源码里也是没有把style中eventSetter设置的事件合并到tartgetType中的,Style中也有一个单独的EventHandlersStore用于存储事件(事件路由的时候,也是直接从EventHandlersStore查询并添加的FrameworkElement.AddStyleHandlersToEventRoute

小改进

于是就是对上述反射代码的改进了

public static RoutedEventHandlerInfo[] GetRoutedEventHandlers(UIElement element, RoutedEvent routedEvent)
        {
            var routedEventHandlers = default(RoutedEventHandlerInfo[]);
            // Get the EventHandlersStore instance which holds event handlers for the specified element.
            // The EventHandlersStore class is declared as internal.
            var eventHandlersStoreProperty = typeof(UIElement).GetProperty("EventHandlersStore", BindingFlags.Instance | BindingFlags.NonPublic);
            object eventHandlersStore = eventHandlersStoreProperty.GetValue(element, null);

            if (eventHandlersStore != null)
            {
                // Invoke the GetRoutedEventHandlers method on the EventHandlersStore instance 
                // for getting an array of the subscribed event handlers.
                var field = eventHandlersStore.GetType().GetField("_entries", BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Public | BindingFlags.FlattenHierarchy);
                dynamic sss = field.GetValue(eventHandlersStore);
                var getRoutedEventHandlers = eventHandlersStore.GetType().GetMethod("GetRoutedEventHandlers", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
                routedEventHandlers = (RoutedEventHandlerInfo[])getRoutedEventHandlers.Invoke(eventHandlersStore, new object[] { routedEvent });
            }
            //get style events
            var frElement = element as FrameworkElement;
            if (frElement != null && frElement.Style != null)
            {
                var styleRoutedEventHandlers = GetStyleRoutedEventHandlers(frElement.Style, routedEvent);
                if (styleRoutedEventHandlers != null)
                {
                    var tempHandlers = new List<RoutedEventHandlerInfo>();
                    if (routedEventHandlers != null)
                    {
                        foreach (var handler in routedEventHandlers)
                        {
                            tempHandlers.Add(handler);
                        }
                    }
                    if (styleRoutedEventHandlers != null)
                    {
                        foreach (var handler in styleRoutedEventHandlers)
                        {
                            tempHandlers.Add(handler);
                        }
                    }

                    routedEventHandlers = tempHandlers.ToArray();
                }
            }

            return routedEventHandlers;
        }

        public static RoutedEventHandlerInfo[] GetStyleRoutedEventHandlers(Style style, RoutedEvent routedEvent)
        {
            var routedEventHandlers = default(RoutedEventHandlerInfo[]);
            // Get the EventHandlersStore instance which holds event handlers for the specified element.
            // The EventHandlersStore class is declared as internal.
            var eventHandlersStoreProperty = typeof(Style).GetProperty("EventHandlersStore", BindingFlags.Instance | BindingFlags.NonPublic);
            object eventHandlersStore = eventHandlersStoreProperty.GetValue(style, null);

            if (eventHandlersStore != null)
            {
                // Invoke the GetRoutedEventHandlers method on the EventHandlersStore instance 
                // for getting an array of the subscribed event handlers.
                var field = eventHandlersStore.GetType().GetField("_entries", BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Public | BindingFlags.FlattenHierarchy);
                dynamic sss = field.GetValue(eventHandlersStore);
                var getRoutedEventHandlers = eventHandlersStore.GetType().GetMethod("GetRoutedEventHandlers", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
                routedEventHandlers = (RoutedEventHandlerInfo[])getRoutedEventHandlers.Invoke(eventHandlersStore, new object[] { routedEvent });
            }
            return routedEventHandlers;
        }

总结

并不算优美,但好歹解决了我遇到的问题了

举报

相关推荐

0 条评论