原文: WPF 自定義路由事件 WPF中的路由事件 as U know,和以前Windows消息事件差別不再多講,這篇博文中,将首先回顧下WPF内置的路由事件的用法,然後在此基礎上自定義一個路由事件。
1.WPF内置路由事件
WPF中的大多數事件都是路由事件,WPF有3中路由政策:

具體不多講,單需要注意的是WPF路由事件是沿着VIsualTree傳遞的。VisualTree與LogicalTree的差別在于:LogicalTree的葉子節點是構成使用者界面的控件(xaml緊密相關),而VisualTree要連控件中的細微結構也算上。VisualTree是LogicalTree的擴充。
reference:
Understanding the Visual Tree and Logical Tree in WPF下面給出一個使用WPF内置路由事件的例子:
<Window x:Class="WPFRoutedEvent.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525" >
<Grid x:Name="Grid1" Margin="10" Background="AliceBlue" MouseLeftButtonDown="Grid1_MouseLeftButtonDown">
<StackPanel Background="BurlyWood" Height="200" x:Name="StackPanel1" Button.Click="ButtonInStackPanel_Click" MouseLeftButtonDown="StackPanel1_MouseLeftButtonDown">
<Button x:Name="Button1" Content="RoutedEvent" Click="Button1_Click" />
</StackPanel>
</Grid>
</Window>
View Code
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
namespace WPFRoutedEvent
{
/// <summary>
/// MainWindow.xaml 的互動邏輯
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
//Grid訂閱Button的Click事件
Grid1.AddHandler(Button.ClickEvent, new RoutedEventHandler(ButtonInGrid_Click));
}
private void Button1_Click(object sender, RoutedEventArgs e)
{
MessageBox.Show("Button Clicked.");
//
//e.Handled = true;
}
private void ButtonInStackPanel_Click(object sender, RoutedEventArgs e)
{
MessageBox.Show("StackPanel Clicked.");
}
private void ButtonInGrid_Click(object sender, RoutedEventArgs e)
{
MessageBox.Show("Grid Clicked.");
}
private void Grid1_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
MessageBox.Show("Grid Mouse Left button down.");
}
private void StackPanel1_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
MessageBox.Show("StackPanel Mouse Left button down.");
}
}
}
Button的Click事件是一個路由事件,分别在StackPanel中和Grid中訂閱這個事件并進行相應的處理,分别用xaml代碼和C#代碼如下:
Click="Button1_Click"
Button.Click="ButtonInStackPanel_Click"
Grid1.AddHandler(Button.ClickEvent, new RoutedEventHandler(ButtonInGrid_Click));
StackPanel的MouseLeftButtonDown也是一個路由事件,也可以叫“附加事件”。其實“附加事件”也是路由事件,隻是個文字遊戲,為什麼還要另外起個名字呢?原來路由事件的宿主都是那些擁有可視化實體的界面元素;而附加事件則不具備顯示在使用者界面上的能力。
常見的附加事件有:
Binding類:SourceUpdated事件、TargetUpdated事件。
Mouse類:MouseEnter事件、MouseLeave事件、MouseDown事件、MouseUp事件等。
Keyboard類:KeyDown事件、KeyUp事件等。
Grid和StackPanel中均如下訂閱:
MouseLeftButtonDown="StackPanel1_MouseLeftButtonDown"
程式運作如下:
2.自定義路由事件
前面DebugLZQ寫過一篇博文,内容是關于自定義CLR事件的,參考:
http://www.cnblogs.com/DebugLZQ/archive/2012/11/04/2753076.html .NET自定義事件小結。下面來自定義一個WPF路由事件,各位博友可以比較下兩者的異同。
建立自定義路由事件大體可以分為三個步驟:
(1)聲明并注冊路由事件
(2)為路由事件添加CLR事件包裝
(3)建立可以激發路由事件的方法
下面我們自定義一個WPF路由事件,我們給事件攜帶個參數,為此需要建立一個RoutedEventArgs類的派生類。如下:
using System;
using System.Windows;
namespace MyRoutedEvent
{
//事件參數
class ReportTimeRoutedEventArgs:RoutedEventArgs
{
public ReportTimeRoutedEventArgs(RoutedEvent routedEvent, object source) : base(routedEvent, source) { }
public DateTime ClickTime { get; set; }
}
}
然後,建立一個Button類的派生類并按前面的步驟為其添加路由事件:
using System;
using System.Windows.Controls;
using System.Windows;
namespace MyRoutedEvent
{
class TimeButton:Button
{
//聲明和注冊路由事件\
public static readonly RoutedEvent ReportTimeRoutedEvent =
EventManager.RegisterRoutedEvent("ReportTime", RoutingStrategy.Bubble, typeof(EventHandler<ReportTimeRoutedEventArgs>), typeof(TimeButton));
//CLR事件包裝
public event RoutedEventHandler ReportTime
{
add { this.AddHandler(ReportTimeRoutedEvent, value); }
remove { this.RemoveHandler(ReportTimeRoutedEvent, value); }
}
//激發路由事件,借用Click事件的激發方法
protected override void OnClick()
{
base.OnClick();//保證Button原有功能正常使用,Click事件被激發
ReportTimeRoutedEventArgs args = new ReportTimeRoutedEventArgs(ReportTimeRoutedEvent, this);
args.ClickTime = DateTime.Now;
this.RaiseEvent(args);//UIElement及其派生類
}
}
}
下面是程式界面的XAML代碼,看下如何消費這個路由事件:
<Window x:Class="MyRoutedEvent.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:MyRoutedEvent"
Title="MainWindow" Height="350" Width="525">
<Grid x:Name="grid1" local:TimeButton.ReportTime="TimeButton_ReportTime"><!---->
<Grid x:Name="grid2">
<Grid x:Name="grid3">
<StackPanel x:Name="stackPanel1">
<ListBox x:Name="listBox1"/>
<local:TimeButton Width="200" Height="200" Background="Aquamarine" ReportTime="TimeButton_ReportTime" /><!---->
</StackPanel>
</Grid>
</Grid>
</Grid>
</Window>
事件處理的背景代碼如下:
using System.Windows;
namespace MyRoutedEvent
{
/// <summary>
/// MainWindow.xaml 的互動邏輯
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void TimeButton_ReportTime(object sender, ReportTimeRoutedEventArgs e)//注意參數
{
listBox1.Items.Add(e.ClickTime.ToLongTimeString()+"DebugLZQ");
}
}
}
程式運作效果如下:
小結:UIElement類是路由事件和附加事件的分水嶺,因為從UIElement類開始才具備了再界面上顯示的能力,也因為RaiseEvent、AddHandler和RemoveHandler這些方法也定義在UIElement類中。附加事件也隻能算是路由事件的一種用法而不是一個新的概念,其本質還是路由事件。