Windows Presentation Foundation (WPF) 应用程序开发人员和组件作者可以创建自定义路由事件,用于扩展公共语言运行时 (CLR) 事件的功能。 本文介绍创建自定义路由事件的基本知识。
本文假定你对路由事件有基本的了解,并且已阅读
路由事件概述。 若要遵循本文中的示例,如果熟悉 Extensible Application Markup Language (XAML) 并知道如何编写 Windows Presentation Foundation (WPF) 应用程序,将会很有帮助。
创建路由事件的基本步骤如下:
使用?RegisterRoutedEvent?方法注册?RoutedEvent。
注册调用返回一个?RoutedEvent
?实例,称为路由事件标识符,该标识符包含已注册的事件名、路由策略
和其他事件详细信息。 将该标识符分配给静态只读字段。 按照惯例:
<event name>Event
。 例如,如果事件名为?Tap
,则标识符应命名为?TapEvent
。Preview<event name>Event
。 例如,如果事件名为?Tap
,则标识符应命名为?PreviewTapEvent
。定义 CLR?add?和?remove?事件访问器。 如果没有 CLR 事件访问器,你就只能通过直接调用?UIElement.AddHandler?和?UIElement.RemoveHandler?方法来添加或删除事件处理程序。 使用 CLR 事件访问器时,你会获得以下事件处理程序分配机制:
+=
?和?-=
?运算符来添加或删除事件处理程序。添加用于触发路由事件的自定义逻辑。 例如,你的逻辑可能会基于用户输入和应用程序状态触发事件。
以下示例在自定义控件库中实现?CustomButton
?类。 派生自?Button?的?CustomButton
?类:
ConditionalClick
?的?RoutedEvent,并在注册期间指定浮升RoutedEvent
?实例分配给名为?ConditionalClickEvent
?的静态只读字段。CustomButton
?并应用外部条件时引发自定义路由事件。 虽然示例代码从重写的?OnClick
?虚拟方法内引发?ConditionalClick
?路由事件,但你可选用任何方式来引发事件。public class CustomButton : Button
{
// Register a custom routed event using the Bubble routing strategy.
public static readonly RoutedEvent ConditionalClickEvent = EventManager.RegisterRoutedEvent(
name: "ConditionalClick",
routingStrategy: RoutingStrategy.Bubble,
handlerType: typeof(RoutedEventHandler),
ownerType: typeof(CustomButton));
// Provide CLR accessors for assigning an event handler.
public event RoutedEventHandler ConditionalClick
{
add { AddHandler(ConditionalClickEvent, value); }
remove { RemoveHandler(ConditionalClickEvent, value); }
}
void RaiseCustomRoutedEvent()
{
// Create a RoutedEventArgs instance.
RoutedEventArgs routedEventArgs = new(routedEvent: ConditionalClickEvent);
// Raise the event, which will bubble up through the element tree.
RaiseEvent(routedEventArgs);
}
// For demo purposes, we use the Click event as a trigger.
protected override void OnClick()
{
// Some condition combined with the Click event will trigger the ConditionalClick event.
if (DateTime.Now > new DateTime())
RaiseCustomRoutedEvent();
// Call the base class OnClick() method so Click event subscribers are notified.
base.OnClick();
}
}
该示例包含一个单独的 WPF 应用程序,该应用程序使用 XAML 标记将?CustomButton
?实例添加到?StackPanel,并将?Handler_ConditionalClick
?方法分配为?CustomButton
?和?StackPanel1
?元素的?ConditionalClick
?事件处理程序。
<Window x:Class="CodeSample.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:custom="clr-namespace:WpfControl;assembly=WpfControlLibrary"
Title="How to create a custom routed event" Height="100" Width="300">
<StackPanel Name="StackPanel1" custom:CustomButton.ConditionalClick="Handler_ConditionalClick">
<custom:CustomButton
Name="customButton"
ConditionalClick="Handler_ConditionalClick"
Content="Click to trigger a custom routed event"
Background="LightGray">
</custom:CustomButton>
</StackPanel>
</Window>
WPF 应用程序在代码隐藏中定义?Handler_ConditionalClick
?事件处理程序方法。 事件处理程序方法只能在代码隐藏中实现。
// The ConditionalClick event handler.
private void Handler_ConditionalClick(object sender, RoutedEventArgs e)
{
string senderName = ((FrameworkElement)sender).Name;
string sourceName = ((FrameworkElement)e.Source).Name;
Debug.WriteLine($"Routed event handler attached to {senderName}, " +
$"triggered by the ConditionalClick routed event raised on {sourceName}.");
}
// Debug output when CustomButton is clicked:
// Routed event handler attached to CustomButton,
// triggered by the ConditionalClick routed event raised on CustomButton.
// Routed event handler attached to StackPanel1,
// triggered by the ConditionalClick routed event raised on CustomButton.
单击?CustomButton
?时:
ConditionalClick
?路由事件在?CustomButton
?上引发。CustomButton
?的?Handler_ConditionalClick
?事件处理程序。ConditionalClick
?路由事件在元素树中向上遍历到?StackPanel1
。StackPanel1
?的?Handler_ConditionalClick
?事件处理程序。ConditionalClick
?路由事件继续向上遍历元素树,可能会触发附加到其他已遍历元素的其他?ConditionalClick
?事件处理程序。Handler_ConditionalClick
?事件处理程序获取有关触发它的事件的以下信息:
sender
?为?CustomButton
,第二次运行时则为?StackPanel1
。Source
?始终为?CustomButton
。?备注
路由事件和 CLR 事件之间的一个主要区别是,路由事件遍历元素树来查找处理程序,而 CLR 事件不遍历元素树,处理程序只能附加到引发事件的源对象。 因此,路由事件?sender
?可以是元素树中的任何已遍历的元素。
你可以像创建浮升事件一样创建隧道事件,但将在?Tunnel?事件注册调用中设置路由策略。