Wpf 简明教程

WPF - Routed Events

routed event 是一种事件类型,可以在元素树中的多个侦听器上调用处理程序,而不仅仅是引发该事件的对象。它基本上是一个由 Routed Event 类的实例支持的 CLR 事件。它在 WPF 事件系统中注册。RoutedEvents 有三种主要的路由策略,如下所示:

  1. Direct Event

  2. Bubbling Event

  3. Tunnel Event

Direct Event

直接事件类似于 Windows 窗体中的事件,它们是由事件发源所在的元素触发的。

与标准 CLR 事件不同,直接路由事件支持类处理,并且可以在自定义控件样式中的事件设置器和事件触发器中使用。

直接事件的一个好例子是 MouseEnter 事件。

Bubbling Event

冒泡事件始于事件发源的元素。然后它沿着可视化树向上移动到可视化树中的最顶级元素。因此,在 WPF 中,最顶级元素很可能是窗口。

Tunnel Event

元素树根上的事件处理程序被调用,然后该事件沿着可视化树向下移动到所有子节点,直到到达事件发源的元素。

冒泡事件和隧道事件之间的区别在于隧道事件总是从预览开始。

在 WPF 应用程序中,事件通常被实现为隧道/冒泡对。因此,你将有一个预览 MouseDown,然后是 MouseDown 事件。

下面给出了 Routed 事件的一个简单示例,其中创建了一个按钮和三个文本块,并具有一些属性和事件。

<Window x:Class = "WPFRoutedEvents.MainWindow"
   xmlns = "http://schemas.microsoft.com/winfx/2006/xaml/presentation"
   xmlns:x = "http://schemas.microsoft.com/winfx/2006/xaml"
   Title = "MainWindow" Height = "450" Width = "604" ButtonBase.Click  = "Window_Click" >

   <Grid>
      <StackPanel Margin = "20" ButtonBase.Click = "StackPanel_Click">

         <StackPanel Margin = "10">
            <TextBlock Name = "txt1" FontSize = "18" Margin = "5" Text = "This is a TextBlock 1" />
            <TextBlock Name = "txt2" FontSize = "18" Margin = "5" Text = "This is a TextBlock 2" />
            <TextBlock Name = "txt3" FontSize = "18" Margin = "5" Text = "This is a TextBlock 3" />
         </StackPanel>

         <Button Margin = "10" Content = "Click me" Click = "Button_Click" Width = "80"/>
      </StackPanel>
   </Grid>

</Window>

这是为 Button、StackPanel 和 Window 实现 Click 事件的 C# 代码。

using System.Windows;

namespace WPFRoutedEvents {
   /// <summary>
      /// Interaction logic for MainWindow.xaml
   /// </summary>

   public partial class MainWindow : Window {

      public MainWindow() {
         InitializeComponent();
      }

      private void Button_Click(object sender, RoutedEventArgs e) {
         txt1.Text = "Button is Clicked";
      }

      private void StackPanel_Click(object sender, RoutedEventArgs e) {
         txt2.Text = "Click event is bubbled to Stack Panel";
      }

      private void Window_Click(object sender, RoutedEventArgs e) {
         txt3.Text = "Click event is bubbled to Window";
      }

   }
}

当你编译并执行以上代码时,它将产生以下窗口:

routed event

当你单击按钮时,文本块将更新,如下所示。

click on button

如果你想在任何特定级别停止路由事件,那么你需要设置 e.Handled = true;

让我们将 StackPanel_Click 事件更改为如下所示:

private void StackPanel_Click(object sender, RoutedEventArgs e) {
   txt2.Text = "Click event is bubbled to Stack Panel";
   e.Handled = true;
}

当你单击按钮时,你会观察到单击事件不会传递到窗口,并且会停止在堆栈面板上,并且第 3 个文本块不会更新。

click event

Custom Routed Events

在 .NET 框架中,也可以定义自定义路由事件。你需要按照下面给出的步骤在 C# 中定义一个自定义路由事件。

  1. 使用系统调用 RegisterRoutedEvent 声明并注册您的路由事件。

  2. 指定路由策略,即冒泡、隧道或直接。

  3. Provide the event handler.

让我们通过一个示例更深入地了解自定义路由事件。按照以下步骤操作:

  1. 使用 WPFCustomRoutedEvent 创建一个新的 WPF 项目

  2. 右键单击您的解决方案,然后选择“添加”>“新项…​”

  3. 将打开以下对话框,现在选择 Custom Control (WPF) 并将其命名为 MyCustomControl

custom routed events
  1. 单击 Add 按钮后,您会看到有两个新文件(Themes/Generic.xaml 和 MyCustomControl.cs)添加到您的解决方案中。

以下 XAML 代码在 Generic.xaml 文件中设置自定义控件的样式。

<ResourceDictionary
   xmlns = "http://schemas.microsoft.com/winfx/2006/xaml/presentation"
   xmlns:x = "http://schemas.microsoft.com/winfx/2006/xaml"
   xmlns:local = "clr-namespace:WPFCustomRoutedEvent">

   <Style TargetType = "{x:Type local:MyCustomControl}">
      <Setter Property = "Margin" Value = "50"/>
      <Setter Property = "Template">
         <Setter.Value>
            <ControlTemplate TargetType = "{x:Type local:MyCustomControl}">

               <Border Background = "{TemplateBinding Background}"
                  BorderBrush = "{TemplateBinding BorderBrush}"
                  BorderThickness = "{TemplateBinding BorderThickness}">
                  <Button x:Name = "PART_Button" Content = "Click Me" />
               </Border>

            </ControlTemplate>
         </Setter.Value>
      </Setter>
   </Style>

</ResourceDictionary>

以下是 MyCustomControl class 的 C# 代码,它继承自 Control class ,其中为自定义控件创建了一个自定义路由事件 Click。

using System.Windows;
using System.Windows.Controls;

namespace WPFCustomRoutedEvent {

   public class MyCustomControl : Control {

      static MyCustomControl() {
         DefaultStyleKeyProperty.OverrideMetadata(typeof(MyCustomControl),
            new FrameworkPropertyMetadata(typeof(MyCustomControl)));
      }

      public override void OnApplyTemplate() {
         base.OnApplyTemplate();

         //demo purpose only, check for previous instances and remove the handler first
         var button  =  GetTemplateChild("PART_Button") as Button;
         if (button ! =  null)
         button.Click + =  Button_Click;
      }

      void Button_Click(object sender, RoutedEventArgs e) {
         RaiseClickEvent();
      }

      public static readonly RoutedEvent ClickEvent  =
         EventManager.RegisterRoutedEvent("Click", RoutingStrategy.Bubble,
         typeof(RoutedEventHandler), typeof(MyCustomControl));

      public event RoutedEventHandler Click {
         add { AddHandler(ClickEvent, value); }
         remove { RemoveHandler(ClickEvent, value); }
      }

      protected virtual void RaiseClickEvent() {
         RoutedEventArgs args = new RoutedEventArgs(MyCustomControl.ClickEvent);
         RaiseEvent(args);
      }

   }
}

以下是 C# 中的自定义路由事件实现,当用户单击该事件时,将会显示一个消息框。

using System.Windows;

namespace WPFCustomRoutedEvent {
   // <summary>
      // Interaction logic for MainWindow.xaml
   // </summary>

   public partial class MainWindow : Window {

      public MainWindow() {
         InitializeComponent();
      }

      private void MyCustomControl_Click(object sender, RoutedEventArgs e) {
         MessageBox.Show("It is the custom routed event of your custom control");
      }

   }
}

以下是 MainWindow.xaml 中的实现,以添加具有路由事件 Click 的自定义控件。

<Window x:Class = "WPFCustomRoutedEvent.MainWindow"
   xmlns = "http://schemas.microsoft.com/winfx/2006/xaml/presentation"
   xmlns:x = "http://schemas.microsoft.com/winfx/2006/xaml"
   xmlns:local = "clr-namespace:WPFCustomRoutedEvent"
   Title = "MainWindow" Height = "350" Width = "604">

   <Grid>
      <local:MyCustomControl Click = "MyCustomControl_Click" />
   </Grid>

</Window>

当编译并执行上述代码时,它将生成以下包含自定义控件的窗口。

custom control

当您单击自定义控件时,它将生成以下消息。

click on custom control