Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Copy all event handlers from one control to another at runtime

Tags:

c#

reflection

wpf

I'm trying to copy every event handlers from one control to another (same type). I found several examples to do it with Winform but nothing for 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 ??
        }
    }
}

The thing is that I don't have access to any handlers method name, as i'm suppose to copy the event handlers from another class...

like image 984
Sugz Avatar asked Dec 05 '25 10:12

Sugz


1 Answers

I finally found something that works here:

Here is the solution I'm using:

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

    }
}

With this, I'm assigning both handlers for the Click event and the one for MouseEntered event to the second button.

like image 189
Sugz Avatar answered Dec 08 '25 00:12

Sugz



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!