Wpf 简明教程

WPF - Routed Events

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

A routed event is a type of event that can invoke handlers on multiple listeners in an element tree rather than just the object that raised the event. It is basically a CLR event that is supported by an instance of the Routed Event class. It is registered with the WPF event system. RoutedEvents have three main routing strategies which are as follows −

  1. Direct Event

  2. Bubbling Event

  3. Tunnel Event

Direct Event

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

A direct event is similar to events in Windows forms which are raised by the element in which the event is originated.

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

Unlike a standard CLR event, direct routed events support class handling and they can be used in Event Setters and Event Triggers within your style of your Custom Control.

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

A good example of a direct event would be the MouseEnter event.

Bubbling Event

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

A bubbling event begins with the element where the event is originated. Then it travels up the visual tree to the topmost element in the visual tree. So, in WPF, the topmost element is most likely a window.

Tunnel Event

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

Event handlers on the element tree root are invoked and then the event travels down the visual tree to all the children nodes until it reaches the element in which the event originated.

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

The difference between a bubbling and a tunneling event is that a tunneling event will always start with a preview.

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

In a WPF application, events are often implemented as a tunneling/bubbling pair. So, you’ll have a preview MouseDown and then a MouseDown event.

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

Given below is a simple example of a Routed event in which a button and three text blocks are created with some properties and events.

<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# 代码。

Here is the C# code for the Click events implementation for Button, StackPanel, and Window.

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";
      }

   }
}

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

When you compile and execute the above code, it will produce the following window −

routed event

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

When you click on the button, the text blocks will get updated, as shown below.

click on button

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

If you want to stop the routed event at any particular level, then you will need to set the e.Handled = true;

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

Let’s change the StackPanel_Click event as shown below −

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

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

When you click on the button, you will observe that the click event will not be routed to the window and will stop at the stackpanel and the 3rd text block will not be updated.

click event

Custom Routed Events

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

In .NET framework, custom routed event can also be defined. You need to follow the steps given below to define a custom routed event in C#.

  1. Declare and register your routed event with system call RegisterRoutedEvent.

  2. Specify the Routing Strategy, i.e. Bubble, Tunnel, or Direct.

  3. Provide the event handler.

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

Let’s take an example to understand more about custom routed events. Follow the steps given below −

  1. Create a new WPF project with WPFCustomRoutedEvent

  2. Right click on your solution and select Add > New Item…​

  3. The following dialog will open, now select Custom Control (WPF) and name it MyCustomControl.

custom routed events
  1. Click the Add button and you will see that two new files (Themes/Generic.xaml and MyCustomControl.cs) will be added in your solution.

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

The following XAML code sets the style for the custom control in Generic.xaml file.

<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。

Given below is the C# code for the MyCustomControl class which inherits from the Control class in which a custom routed event Click is created for the custom control.

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# 中的自定义路由事件实现,当用户单击该事件时,将会显示一个消息框。

Here is the custom routed event implementation in C# which will display a message box when the user clicks it.

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 的自定义控件。

Here is the implementation in MainWindow.xaml to add the custom control with a routed event 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>

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

When the above code is compiled and executed, it will produce the following window which contains a custom control.

custom control

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

When you click on the custom control, it will produce the following message.

click on custom control