0

WPF 自定义附加事件 - 唐宋元明清2188

 1 year ago
source link: https://www.cnblogs.com/kybs0/p/17012536.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

我们都知道路由事件,而附加路由事件用的比较少。

但如果是通用的场景,类似附加属性,附加事件就很有必要的。

举个例子,输入设备有很多种,WPF中输入事件主要分为鼠标、触摸、触笔:WPF 屏幕点击的设备类型 - 唐宋元明清2188 - 博客园 (cnblogs.com)

有这么多输入事件Mouse、Touch、Stylus,另外按钮Click还处理了冒泡事件,事件类型就更多了。

但绝大部分业务其实并不关心事件类型,只需要一个触发事件就行了。

所以我们有这样的需求:设计并提供一个通用的输入事件,大家只需要拿到事件进行业务操作。另外一些小场景,如果需要具体事件如触摸点集,可以从事件源参数内部去获取。

具体的通用输入事件,我们另外讨论,这里主要描述如何自定义附加事件

附加事件定义

WPF官方对附加事件的描述 - 附加事件概述 - WPF .NET Framework | Microsoft Learn

所以我们先定义一个附加事件类:

 1     public class DeviceEvents
 2     {
 3         /// <summary>
 4         /// 按压事件
 5         /// </summary>
 6         public static readonly RoutedEvent PreviewDeviceDownEvent = EventManager.RegisterRoutedEvent("PreviewDeviceDown", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(DeviceEvents));
 7         public static void AddPreviewDeviceDownHandler(DependencyObject d, RoutedEventHandler handler)
 8         {
 9             (d as UIElement)?.AddHandler(DeviceEvents.PreviewDeviceDownEvent, handler);
10         }
11         public static void RemovePreviewDeviceDownHandler(DependencyObject d, RoutedEventHandler handler)
12         {
13             (d as UIElement)?.RemoveHandler(DeviceEvents.PreviewDeviceDownEvent, handler);
14         }
15     }

然后,我们看下如何封装原有事件PreviewMouseDown、PreviewStylusDown,并转换成此PreviewDeviceDown事件

在界面上添加下事件:

1     <Button x:Name="TestButton"  
2             Content="测试" Height="30" Width="120"
3             local:DeviceEvents.PreviewDeviceDown="TestButton_OnPreviewDeviceDown"/>

我们会发现界面加载时,AddPreviewDeviceDownHandler不会触发调用。附加事件,与附加属性原理不一样,附加事件相当于在编译时注入委托(事件处理程序)AddPreviewDeviceDownHandler,界面加载时不会执行这个委托注册。

所以,我们可以通过另一个附加属性来开关附加事件,并且内部集成事件的触发。具体设计如下:

 1     public class DeviceEvents
 2     {
 3         private static readonly List<DeviceEventTransformer> EventTransformers = new List<DeviceEventTransformer>();
 4 
 5         /// <summary>
 6         /// 是否开启附加事件
 7         /// </summary>
 8         public static readonly DependencyProperty IsOpenProperty = DependencyProperty.RegisterAttached(
 9             "IsOpen", typeof(bool), typeof(DeviceEvents), new PropertyMetadata(default(bool), OnIsOpenPropertyChanged));
10         private static void OnIsOpenPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
11         {
12             if (d is UIElement element && e.NewValue is bool isOpen)
13             {
14                 var eventTransformer = EventTransformers.FirstOrDefault(i => i.Target == element);
15                 if (isOpen)
16                 {
17                     if (eventTransformer == null)
18                     {
19                         eventTransformer = new DeviceEventTransformer(element);
20                         EventTransformers.Add(eventTransformer);
21                     }
22                     eventTransformer.Register();
23                 }
24                 else
25                 {
26                     eventTransformer?.UnRegister();
27                 }
28             }
29         }
30 
31         public static void SetIsOpen(DependencyObject element, bool value)
32         {
33             element.SetValue(IsOpenProperty, value);
34         }
35         public static bool GetIsOpen(DependencyObject element)
36         {
37             return (bool)element.GetValue(IsOpenProperty);
38         }
39 
40         /// <summary>
41         /// 按压事件
42         /// </summary>
43         public static readonly RoutedEvent PreviewDeviceDownEvent = EventManager.RegisterRoutedEvent("PreviewDeviceDown", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(DeviceEvents));
44         public static void AddPreviewDeviceDownHandler(DependencyObject d, RoutedEventHandler handler)
45         {
46             (d as UIElement)?.AddHandler(DeviceEvents.PreviewDeviceDownEvent, handler);
47         }
48         public static void RemovePreviewDeviceDownHandler(DependencyObject d, RoutedEventHandler handler)
49         {
50             (d as UIElement)?.RemoveHandler(DeviceEvents.PreviewDeviceDownEvent, handler);
51         }
52     }
53     /// <summary>
54     /// 事件转换器
55     /// </summary>
56     public class DeviceEventTransformer
57     {
58         private readonly UIElement _uiElement;
59 
60         public DeviceEventTransformer(UIElement uiElement)
61         {
62             _uiElement = uiElement;
63         }
64         public UIElement Target => _uiElement;
65 
66         public void Register()
67         {
68             _uiElement.PreviewMouseDown += UiElement_PreviewMouseDown;
69             _uiElement.PreviewStylusDown += UiElement_PreviewStylusDown;
70         }
71 
72         public void UnRegister()
73         {
74             _uiElement.PreviewMouseDown -= UiElement_PreviewMouseDown;
75             _uiElement.PreviewStylusDown -= UiElement_PreviewStylusDown;
76         }
77 
78         private void UiElement_PreviewMouseDown(object sender, System.Windows.Input.MouseButtonEventArgs e)
79         {
80             OnPreviewDeviceDown(e);
81         }
82         private void UiElement_PreviewStylusDown(object sender, StylusDownEventArgs e)
83         {
84             OnPreviewDeviceDown(e);
85         }
86 
87         private void OnPreviewDeviceDown(InputEventArgs e)
88         {
89             RoutedEventArgs reArgs = new RoutedEventArgs(DeviceEvents.PreviewDeviceDownEvent, e);
90             _uiElement.RaiseEvent(reArgs);
91         }
92     }

DeviceEventTransformer是一个事件封装器,如果只是后台代码,也可以直接使用DeviceEventTransformer订阅相关事件。

上面事件实现是一个案例,这里的代码比较粗糙,没有对鼠标、触摸做互斥处理。另外其它的事件类型如冒泡、隧道、Down、Up等,后续可以将DeviceEvents封装完整。

附加事件使用

在界面上,我们可以直接在任意控件上添加此事件:

685541-20221229144423696-1443809971.png

先添加DeviceEvents.IsOpen="True"开启设备附加事件,
然后订阅相应的附加事件DeviceEvents.PreviewDeviceDown="TestGrid_OnPreviewDeviceDown"

Demo:DeviceEventsDemo: WPF附加事件DEMO (gitee.com)


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK