Custom event handling of Mouse events in programmatically created UI elements?

MSCollege3000 120 Reputation points
2024-01-11T08:16:16.4633333+00:00

WPF, Net Core 8.

How to implement this logic:

  1. create a set of UI elements in C# (not XAML)
  2. create an extension of MouseEventArgs with an added unique property
  3. create MouseEventHandler that uses the custom MouseEventArgs object with the unique value and assign this handler to each UI element's MouseEnter event programmatically
  4. when mouse enters a UI element, raise the MouseEnter event and send the unique value form assigned custom MouseEventArgs to ViewModel.

A good official documentation/article/book would be great. Thanks.

.NET
.NET
Microsoft Technologies based on the .NET software framework.
3,439 questions
Windows Presentation Foundation
Windows Presentation Foundation
A part of the .NET Framework that provides a unified programming model for building line-of-business desktop applications on Windows.
2,685 questions
C#
C#
An object-oriented and type-safe programming language that has its roots in the C family of languages and includes support for component-oriented programming.
10,360 questions
{count} votes

Accepted answer
  1. Peter Fleischer (former MVP) 19,231 Reputation points
    2024-01-15T17:50:54.0266667+00:00

    Hi,
    all UI controls derived from FrameworkElement have the Tag property. If you don't want to use the Tag property for additional values on events, you can use your own EventArgs in your own event, like in the following demo: XAML:

    <Window x:Class="WpfApp1.Window055"
            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:WpfApp055"
            mc:Ignorable="d"
            Title="MSCollege3000_240111" Height="450" Width="800"
            local:ViewModel.AttProp="True">
    </Window>
    
    
    

    And code:

    using System;
    using System.Windows;
    using System.Windows.Controls;
    using System.Windows.Input;
    using System.Windows.Media;
    
    namespace WpfApp055
    {
    	public class ViewModel
    	{
    		private void Window_Loaded(object sender, RoutedEventArgs e)
    		{
    			// 1. create a set of UI elements in C# (not XAML)
    			StackPanel stp = new StackPanel();
    			Wnd.Content = stp;
    			MyBorder b1 = new MyBorder()
    			{
    				BorderBrush = Brushes.Red,
    				BorderThickness = new Thickness(3),
    				Height = 100,
    				Margin = new Thickness(25),
    				BorderProp = "Border 1"     // UniqueValue in Custom UI element
    			};
    			// 3. assign event to each UI element's MyMouseEnter event programmatically
    			b1.MyMouseEnter += MouseEnter;
    			stp.Children.Add(b1);
    			MyBorder b2 = new MyBorder()
    			{
    				BorderBrush = Brushes.Green,
    				BorderThickness = new Thickness(3),
    				Height = 100,
    				Margin = new Thickness(25),
    				BorderProp = "Border 2"     // UniqueValue in Custom UI element
    			};
    			// 3. assign event to each UI element's MyMouseEnter event programmatically
    			b2.MyMouseEnter += MouseEnter;
    			stp.Children.Add(b2);
    		}
    
    		// 4. event handling
    		private void MouseEnter(object sender, EventArgs e)
    		{
    			// that uses the custom MouseEventArgs object with the unique value
    			MyEventArgs args = e as MyEventArgs;
    			if (args == null) return;
    			MessageBox.Show(args.Prop);
    		}
    
    		#region Attached property for attaching ViewModel with code
    		public static readonly DependencyProperty AttPropProperty = DependencyProperty.Register("AttProp",
    			typeof(bool), typeof(Window), new UIPropertyMetadata(false, OnAttProp));
    		public static bool GetAttProp(DependencyObject obj) => (bool)obj.GetValue(AttPropProperty);
    		public static void SetAttProp(DependencyObject obj, bool value) => obj.SetValue(AttPropProperty, value);
    		private static void OnAttProp(DependencyObject depObj, DependencyPropertyChangedEventArgs e)
    		{
    			var wnd = depObj as Window;
    			if (wnd == null) return;
    			if ((e.NewValue is bool) && (bool)(e.NewValue))
    			{
    				ViewModel vm = new ViewModel();
    				vm.Wnd = wnd;
    				vm.Wnd.Loaded += vm.Window_Loaded;
    			}
    		}
    		Window Wnd;
    		#endregion
    	}
    
    	/// <summary>
    	/// Custom UI element
    	/// </summary>
    	public class MyBorder : Border
    	{
    		// UniqueValue in Custom UI element
    		public string BorderProp { get; set; }
    
    		// original MouseEnter event raises Custom event
    		protected override void OnMouseEnter(MouseEventArgs e)
    		{
    			RaiseCustomRoutedEvent(BorderProp);
    			base.OnMouseEnter(e);
    		}
    
    		// custom routed event
    		public static readonly RoutedEvent MyMouseEnterEvent = EventManager.RegisterRoutedEvent("MyMouseEnter",
    			RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(MyBorder));
    
    		public event RoutedEventHandler MyMouseEnter
    		{
    			add { AddHandler(MyMouseEnterEvent, value); }
    			remove { RemoveHandler(MyMouseEnterEvent, value); }
    		}
    
    		// raise custom event args class
    		void RaiseCustomRoutedEvent(string prop)
    		{
    			// copy UniqueValue from Custom UI element into custom event args
    			MyEventArgs routedEventArgs = new(MyMouseEnterEvent) { Prop = prop };
    			RaiseEvent(routedEventArgs);
    		}
    	}
    
    	// custom event args class
    	// 2. create an extension of RoutedEventArgs with an added unique property
    	public class MyEventArgs : RoutedEventArgs
    	{
    		public MyEventArgs(RoutedEvent routedEvent) : base(routedEvent) { }
    		public string Prop { get; set; }
    	}
    
    }
    

    Result: x

    1 person found this answer helpful.

3 additional answers

Sort by: Most helpful
  1. Hui Liu-MSFT 40,866 Reputation points Microsoft Vendor
    2024-01-11T09:57:59.71+00:00

    Hi,@MSCollege3000. Welcome to Microsoft Q&A.

    Based on the logical steps you described, you could try the following code to see if it meets your needs.

    
    <Border x:Name="border1" Background="LightGray" Margin="20" Width="300" Height="300" MouseEnter="Border_MouseEnter"/>
            <Border x:Name="border2" Background="LightBlue" Width="300" Height="300"  MouseEnter="Border_MouseEnter"/>
    
    

    codebedhind:

    
    public partial class MainWindow : Window
      {
        private MainViewModel viewModel;
    
        public MainWindow()
        {
          InitializeComponent();
          viewModel = new MainViewModel();
        }
    
        private void Border_MouseEnter(object sender, System.Windows.Input.MouseEventArgs e)
        {
          if (sender is FrameworkElement element)
          {
            string uniqueValue = element.Name; // Use a property or tag to store a unique value
    
            // Raise the custom mouse enter event
            viewModel.OnBorderMouseEnter(uniqueValue);
          }
        }
      }
      public class MainViewModel  
      {
        public event EventHandler<BorderMouseEnterEventArgs> BorderMouseEnter;
      
    
        public void OnBorderMouseEnter(string uniqueValue)
        {
          // Raise the event with the unique value
          BorderMouseEnter?.Invoke(this, new BorderMouseEnterEventArgs(uniqueValue));
          
        }
       
    
      }
    
      // Custom event args class
      public class BorderMouseEnterEventArgs : EventArgs
      {
        public string UniqueValue { get; }
    
        public BorderMouseEnterEventArgs(string uniqueValue)
        {
          UniqueValue = uniqueValue;
        }
      }
    
    

    If the answer is the right solution, please click "Accept Answer" and kindly upvote it. If you have extra questions about this answer, please click "Comment". Note: Please follow the steps in our documentation to enable e-mail notifications if you want to receive the related email notification for this thread.


  2. Peter Fleischer (former MVP) 19,231 Reputation points
    2024-01-11T15:31:39.24+00:00

    Hi,
    try following demo:

    <Window x:Class="WpfApp1.Window051"
            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:WpfApp051"
            mc:Ignorable="d"
            Title="MSCollege3000_240111" Height="450" Width="800"
            local:ViewModel.AttProp="True">
    </Window>
    
    
    

    And ViewModel:

    using System.Windows;
    using System.Windows.Controls;
    using System.Windows.Input;
    using System.Windows.Media;
    
    namespace WpfApp051
    {
    	public class ViewModel
    	{
    		private void Window_Loaded(object sender, RoutedEventArgs e)
    		{
    			StackPanel stp = new StackPanel();
    			Wnd.Content = stp;
    			Border b1 = new Border() { BorderBrush = Brushes.Red, BorderThickness = new Thickness(3), Height = 100, Margin = new Thickness(5), Tag = "Border 1" };
    			b1.MouseEnter += MouseEnter;
    			stp.Children.Add(b1);
    			Border b2 = new Border() { BorderBrush = Brushes.Green, BorderThickness = new Thickness(3), Height = 100, Margin = new Thickness(5), Tag = "Border 2" };
    			b2.MouseEnter += MouseEnter;
    			stp.Children.Add(b2);
    		}
    
    		private void MouseEnter(object sender, MouseEventArgs e)
    		{
    			Border b = sender as Border; if (b == null) return;
    			MessageBox.Show(b.Tag.ToString());
    		}
    
    		#region Attached property
    		public static readonly DependencyProperty AttPropProperty = DependencyProperty.Register("AttProp",
    			typeof(bool), typeof(Window), new UIPropertyMetadata(false, OnAttProp));
    		public static bool GetAttProp(DependencyObject obj) => (bool)obj.GetValue(AttPropProperty);
    		public static void SetAttProp(DependencyObject obj, bool value) => obj.SetValue(AttPropProperty, value);
    		private static void OnAttProp(DependencyObject depObj, DependencyPropertyChangedEventArgs e)
    		{
    			var wnd = depObj as Window;
    			if (wnd == null) return;
    			if ((e.NewValue is bool) && (bool)(e.NewValue))
    			{
    				ViewModel vm = new ViewModel();
    				vm.Wnd = wnd;
    				vm.Wnd.Loaded += vm.Window_Loaded;
    			}
    		}
    		Window Wnd;
    		#endregion
    	}
    }
    

    Result: x


  3. MSCollege3000 120 Reputation points
    2024-01-20T12:58:16.6933333+00:00

    Regarding good articles on the subject:

    1. Routed events theory
    2. Custom implementation in derived classes
    3. Creating routed events walkthrough

    And there is more useful stuff in the Events chapter.

    0 comments No comments