附加事件的作用让一个非UI元素的静态类(如Mouse),能够定义一种"可以借给任何UI元素使用"的路由事件

1、定义附加事件的静态类
1)定义附加事件
下面的代码中 EventManager.RegisterRoutedEvent方法注册路由事件,该方法第一个参数是"事件名称";第二个参数是路由策略,路由策略分为冒泡、隧道、直接三种;第3个参数是委托类型,这个委托类型不是固定的,RoutedEventHandler是标准的写法;第4个参数是事件所在的类。
2)定义附加属性
这里定义一个附加属性IsEnabled,附加属性的作用就是启动附加事件,在附加属性的回调函数中,动态订阅/取消订阅附加事件,本文代码中当IsEnabled为true时,监听MouseDoubleClick 事件,并在监听方法Element_MouseDoubleClick中触发附加事件。
csharp
public static class DoubleClickHelper
{
// ========== 第一部分:定义附加事件 ==========
public static readonly RoutedEvent DoubleClickEvent =
EventManager.RegisterRoutedEvent("DoubleClick", RoutingStrategy.Bubble,
typeof(RoutedEventHandler), typeof(DoubleClickHelper));
public static void AddDoubleClickHandler(DependencyObject obj, RoutedEventHandler handler)
{
(obj as UIElement)?.AddHandler(DoubleClickEvent, handler);
}
public static void RemoveDoubleClickHandler(DependencyObject obj, RoutedEventHandler handler)
{
(obj as UIElement)?.RemoveHandler(DoubleClickEvent, handler);
}
// ========== 第二部分:附加属性来启用功能 ==========
public static readonly DependencyProperty IsEnabledProperty =
DependencyProperty.RegisterAttached(
"IsEnabled", // 属性名
typeof(bool), // 类型
typeof(DoubleClickHelper), // 所属类
new PropertyMetadata(false, OnIsEnabledChanged)); // 值改变时的回调
// 设置 IsEnabled 属性
public static void SetIsEnabled(DependencyObject obj, bool value)
{
obj.SetValue(IsEnabledProperty, value);
}
// 获取 IsEnabled 属性
public static bool GetIsEnabled(DependencyObject obj)
{
return (bool)obj.GetValue(IsEnabledProperty);
}
// ========== 第三部分:当启用时,监听鼠标双击 ==========
private static void OnIsEnabledChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
{
Control element = obj as Control;
if (element == null) return;
if ((bool)e.NewValue == true)
{
// 启用:监听鼠标双击
element.MouseDoubleClick += Element_MouseDoubleClick;
}
else
{
// 禁用:取消监听
element.MouseDoubleClick -= Element_MouseDoubleClick;
}
}
// ========== 第四部分:双击发生时,触发我们的附加事件 ==========
private static void Element_MouseDoubleClick(object sender, MouseButtonEventArgs e)
{
UIElement element = sender as UIElement;
if (element == null) return;
// 触发附加事件!
RoutedEventArgs args = new RoutedEventArgs(DoubleClickEvent, element);
element.RaiseEvent(args);
}
}
2、xaml代码
为控件添加附加事件的监控,在xaml使用类名.附加事件名
csharp
<StackPanel Margin="20" local:DoubleClickHelper.IsEnabled="True"
local:DoubleClickHelper.DoubleClick="OnDoubleClick">
<!-- 按钮1:启用双击功能 -->
<Button Content="按钮1 - 双击我"
local:DoubleClickHelper.IsEnabled="True"
local:DoubleClickHelper.DoubleClick="OnDoubleClick"
Height="50" Margin="0,5"/>
<!-- 按钮2:也启用双击功能 -->
<Button Content="按钮2 - 双击我"
local:DoubleClickHelper.IsEnabled="True"
local:DoubleClickHelper.DoubleClick="OnDoubleClick"
Height="50" Margin="0,5"/>
<!-- 文本框:也支持双击 -->
<TextBox Text="文本框 - 双击我"
local:DoubleClickHelper.IsEnabled="True"
local:DoubleClickHelper.DoubleClick="OnDoubleClick"
Height="30" Margin="0,5"/>
<!-- 状态显示 -->
<RichTextBox Name="StatusText" Margin="0,20"
Foreground="Red" FontSize="12"/>
</StackPanel>
3、逻辑层代码
只要附加事件被触发,下面的代码就会执行,如果判断被双击的是Button则获取Button的Content属性,如果被双击的是TextBox则获取它的Text属性。
csharp
private void OnDoubleClick(object sender, RoutedEventArgs e)
{
// sender 是被双击的那个元素
FrameworkElement element = sender as FrameworkElement;
string elementType = element?.GetType().Name ?? "未知";
string content = "";
// 获取显示文本
if (element is Button btn)
content = btn.Content.ToString();
else if (element is TextBox txt)
content = txt.Text;
// 显示提示
StatusText.AppendText ( $"双击了 {elementType}:{content},时间:{System.DateTime.Now:T}"+Environment.NewLine );
}
4、测试结果
1)双击按钮1
你会发现有richTextbox有两条记录,一个是按钮1被双击了,一个是StackPanel,这是因为附加是冒泡事件,当按钮1被双击以后,冒泡基制也会导致StackPanel作为按钮1的父控件也可以监听到双击事件。

2)双击文本框
