本文内容

  1. 先决条件
  2. 路由事件步骤
  3. 示例

Windows Presentation Foundation (WPF) 应用程序开发人员和组件作者可以创建自定义路由事件,用于扩展公共语言运行时 (CLR) 事件的功能。 本文介绍创建自定义路由事件的基本知识。

1、先决条件

本文假定你对路由事件有基本的了解,并且已阅读
路由事件概述。 若要遵循本文中的示例,如果熟悉 Extensible Application Markup Language (XAML) 并知道如何编写 Windows Presentation Foundation (WPF) 应用程序,将会很有帮助。

2、路由事件步骤

创建路由事件的基本步骤如下:

  1. 使用RegisterRoutedEvent方法注册RoutedEvent。

  2. 注册调用返回一个RoutedEvent实例,称为路由事件标识符,该标识符包含已注册的事件名、路由策略
    和其他事件详细信息。 将该标识符分配给静态只读字段。 按照惯例:

    • 具有浮升
      策略的路由事件的标识符命名为Event。 例如,如果事件名为Tap,则标识符应命名为TapEvent
    • 具有
      隧道策略的路由事件的标识符命名为PreviewEvent。 例如,如果事件名为Tap,则标识符应命名为PreviewTapEvent
  3. 定义 CLRadd和remove事件访问器。 如果没有 CLR 事件访问器,你就只能通过直接调用UIElement.AddHandler和UIElement.RemoveHandler方法来添加或删除事件处理程序。 使用 CLR 事件访问器时,你会获得以下事件处理程序分配机制:

    • 对于 Extensible Application Markup Language (XAML),可以使用属性语法来添加事件处理程序。
    • 对于 C#,可以使用+=-=运算符来添加或删除事件处理程序。
    • 对于 VB,可以使用AddHandler和RemoveHandler语句来添加或删除事件处理程序。
  4. 添加用于触发路由事件的自定义逻辑。 例如,你的逻辑可能会基于用户输入和应用程序状态触发事件。

3、示例

以下示例在自定义控件库中实现CustomButton类。 派生自Button的CustomButton类:

  1. 使用RegisterRoutedEvent方法注册一个名为ConditionalClick的RoutedEvent,并在注册期间指定浮升
    策略。
  2. 将从注册调用返回的RoutedEvent实例分配给名为ConditionalClickEvent的静态只读字段。
  3. 定义 CLRadd和remove事件访问器。
  4. 添加自定义逻辑,以在单击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方法分配为CustomButtonStackPanel1元素的ConditionalClick事件处理程序。

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时:

  1. ConditionalClick路由事件在CustomButton上引发。
  2. 触发了附加到CustomButtonHandler_ConditionalClick事件处理程序。
  3. ConditionalClick路由事件在元素树中向上遍历到StackPanel1
  4. 触发了附加到StackPanel1Handler_ConditionalClick事件处理程序。
  5. ConditionalClick路由事件继续向上遍历元素树,可能会触发附加到其他已遍历元素的其他ConditionalClick事件处理程序。

Handler_ConditionalClick事件处理程序获取有关触发它的事件的以下信息:

  • sender对象,它是事件处理程序附加到的元素。 处理程序首次运行时,senderCustomButton,第二次运行时则为StackPanel1
  • RoutedEventArgs.Source对象,它是最初引发事件的元素。 在本示例中,Source始终为CustomButton

备注

路由事件和 CLR 事件之间的一个主要区别是,路由事件遍历元素树来查找处理程序,而 CLR 事件不遍历元素树,处理程序只能附加到引发事件的源对象。 因此,路由事件sender可以是元素树中的任何已遍历的元素。

你可以像创建浮升事件一样创建隧道事件,但将在Tunnel事件注册调用中设置路由策略。