c# - 在运行时将所有事件处理程序从一个控件复制到另一个控件

标签 c# wpf reflection

我正在尝试将每个事件处理程序从一个控件复制到另一个控件(相同类型)。 我找到了几个用 Winform 做的例子,但没有找到 WPF 的例子......

<Window x:Class="WpfApp1.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:local="clr-namespace:WpfApp1"
    mc:Ignorable="d"
    Title="MainWindow" Height="350" Width="525">

    <StackPanel x:Name="panel">
        <Button x:Name="btn1" 
                Content="Button 01"/>
    </StackPanel>
</Window>


using System;
using System.Windows;
using System.Windows.Controls;

namespace WpfApp1
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
            btn1.Click += (s, e) => Console.WriteLine("Button pressed");

            AddButton();
        }

        private void AddButton()
        {
            Button btn2 = new Button() { Content = "Button 02" };
            panel.Children.Add(btn2);

            // Copy all event handler from btn1 to btn2 ??
        }
    }
}

问题是我无权访问任何处理程序方法名称,因为我想从另一个类复制事件处理程序...

最佳答案

我终于找到了有用的东西 here :

这是我正在使用的解决方案:

using System;
using System.Linq;
using System.Reflection;
using System.Windows;
using System.Windows.Controls;
using BF = System.Reflection.BindingFlags;

namespace WpfApp1
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
            btn1.Click += (s, e) => Console.WriteLine($"{((Button)s).Content}a pressed");
            btn1.Click += Btn1_Click;
            btn1.MouseEnter += (s, e) => Console.WriteLine($"{((Button)s).Content} mouse entered");

            AddButton();
        }

        private void Btn1_Click(object sender, RoutedEventArgs e)
        {
            Console.WriteLine($"{((Button)sender).Content}b pressed");
        }


        private void AddButton()
        {
            Button btn2 = new Button() { Content = "Button 02" };
            panel.Children.Add(btn2);

            // Copy all event handler from btn1 to btn2 ??
            FieldInfo[] fields = btn1.GetType().GetFields(BF.Static | BF.NonPublic | BF.Instance | BF.Public | BF.FlattenHierarchy);
            foreach (FieldInfo field in fields.Where(x => x.FieldType == typeof(RoutedEvent)))
            {
                RoutedEventHandlerInfo[] routedEventHandlerInfos = GetRoutedEventHandlers(btn1, (RoutedEvent)field.GetValue(btn1));
                if (routedEventHandlerInfos != null)
                {
                    foreach (RoutedEventHandlerInfo routedEventHandlerInfo in routedEventHandlerInfos)
                        btn2.AddHandler((RoutedEvent)field.GetValue(btn1), routedEventHandlerInfo.Handler);
                }
            }
        }


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

    }
}

有了这个,我将 Click 事件的处理程序和 MouseEntered 事件的处理程序分配给第二个按钮。

关于c# - 在运行时将所有事件处理程序从一个控件复制到另一个控件,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/44283395/

相关文章:

C#、反射和原始类型

python - 在 Python 中检查 A 是否是 B 的父类(super class)

c# - 如何反序列化当前类

c# - 无法识别 System.Web.HttpContext

c# - 如何从解决方案中获取 64 位 exe 文件?

c# - WebClient 超时花费的时间比预期的要长(使用 : Rx, Try Catch,任务)

c# - WPF + Windows 窗体错误处理

c# - 如何使 Microsoft.VisualStudio.Diagnostics.UI.Controls.MultiSelectComboBox 工作

c# - WPF MeasureOverride 不适用于我的自定义控件

scala - scala 的 ClassTag 和 TypeTag 的区别