WPF路由事件

  • WPF路由事件已关闭评论
  • 194 次浏览
  • A+
所属分类:.NET技术
摘要

事件路由允许源自某个元素的事件由另一个元素引发。定义、注册和包装路由事件


理解路由事件

事件路由允许源自某个元素的事件由另一个元素引发。

定义、注册和包装路由事件

public class MyWindow : Window { 	/// <summary> 	/// 定义和注册路由事件 	/// </summary> 	public static readonly RoutedEvent MyRoutedEvent = EventManager.RegisterRoutedEvent("MyEvent", 		RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(MyWindow));  	/// <summary> 	/// 包装路由事件 	/// </summary> 	public event RoutedEventHandler MyRouted 	{ 		add 		{ 			base.AddHandler(MyRoutedEvent, value); 		} 		remove 		{ 			base.RemoveHandler(MyRoutedEvent, value); 		} 	} } 

共享路由事件

​ 与依赖项属性一样,可以在类之间共享路由事件的定义。例如,两个基类UIElementContentElement类,都使用了MouseUp事件。MouseUp事件是在Windows.Input.Mouse类中定义,通过RoutedEvent.AddOwer()方法重用MouseUp事件。

	UIElement.MouseUpEvent = Mouse.MouseUpEvent.AddOwner(Typeof(UIElement)); 

触发路由事件

路由事件的引发不是传统的.NET事件包装器引发,而是使用RaiseEvent()方法引发事件,说有UIElement类继承了该方法。

	RoutedEventArgs e = new RoutedEventArgs(MyRoutedEvent, this); 	base.RaiseEvent(e); 

处理路由事件

XAML标记添加事件特性

<local:MyWindow ...                  MyRouted="MyWindow_MyRouted"                 ... /> 

通过代码连接事件

     this.MyRouted += MainWindow_MyRouted;  	///WPF事件参数类都是继承自RoutedEventArgs类,可自定义事件参数类来传递更多的信息 	private void MainWindow_MyRouted(object sender, RoutedEventArgs e) 	{ 		//do something... 	} 

还可以通过调用AddHandler()方法链接事件处理器。

	AddHandler(MyRoutedEvent, new RoutedEventHandler((s,e)=> 	{         //do something...                                                  	}));	 	或 	AddHandler(MyRoutedEvent, new RoutedEventHandler(Eventhandler));  	private void Eventhandler(object o, RoutedEventArgs e) 	{ 		//do something... 	} 	//使用RemoveHandler()方法来移除事件处理器 注:匿名委托不适用 	//当程序多次附加事件处理器时,执行RemoveHandler()方法只删除一次连接。例如,连接了两次事件处理程序,删除了一次连接,触发事件时事件处理程序执行一次。		连接了三次事件处理程序,删除了一次连接,触发事件时事件处理程序执行两次。 	RemoveHandler(MyRoutedEvent, new RoutedEventHandler(eventhandler)); 	或      MyRouted -= Eventhandler; 

事件路由

WPF窗体中的所有要素都一定程度上继承自UIElement类,WPF中的许多空间都是内容控件,继承自ContentControl,可以在其中多次重复嵌套。

WPF路由事件模型的三种方式:

  • 直接路由事件(Direct Event):与.NET事件类似,它起源于一个元素,并且不传递给其他元素。例如,MouseEnter事件是一个直接路由事件。
  • 冒泡路由事件(Bubbling Event):在包含层次中向上传递。例如,MouseDown事件就是冒泡路由事件。冒泡顺序首先由被单击的元素引发,接下来被该元素的父元素引发,依次类推,直到WPF元素树的顶部为止。
  • 隧道路由事件(Tunneling Event):在包含层次中向下传递。隧道路由事件在事件到达恰当的控件之前为预览事件(或者可能终止事件)提供机会。例如,通过PreviewKeyDown事件可以截获是否按下了一个键,首先在窗口级别上,然后是更具体的容器,直到到达按下键时具有焦点的元素。

路由事件的行为在注册路由事件的EventManager.RegisterEvent()中定义,传递为RoutingStrategy枚举值。

RoutedEventArgs

WPF事件参数类都是继承自RoutedEventArgs类,可自定义事件参数类来传递更多的信息。RoutedEventArgs类的属性:

名称 说明
Source 指示引发事件的对象。对于键盘事件,是当事件发生时具有焦点的控件。对于鼠标事件,是当事件发生时鼠标指针下面所有元素中最上面的元素。
OriginalSource 指示最初是什么对象引发事件。通常OriginalSource属性和Source属性值是相同的。但是在某些情况下,OriginalSource属性指向对象树种更深得层次,以获得作为更高一级元素一部分的后台元素,事件最原始的源为Border元素,组成在控件模板中。
RoutedEvent 通过事件处理程序为触发的事件提供RoutedEvent对象。如果使用同一事件处理程序处理不同的事件,这一信息是非常有用得。
Handled 该属性允许终止事件的冒泡或隧道过程。如果一个控件将Handled属性设置为true,那么事件将不会继续传播。并且也不会再为任何其他元素引发该事件。

冒泡路由事件

<Label MouseUp="Label_MouseUp"> 	<Border MouseUp="Border_MouseUp"> 		<StackPanel MouseUp="StackPanel_MouseUp"> 			<Button Width="100" Height="30"  MouseUp="Button_MouseUp"/> 		</StackPanel> 	</Border> </Label> 
MessageBox.Show("btn"+"n"+sender.ToString()+ "n" + e.Source.ToString()+ "n" + e.OriginalSource.ToString()); 

在一个容器中放置下列元素,分别给每一个元素的MouseUp事件处理器添加以上代码。窗体打开后右键点击按钮就会依次弹出消息框,通过消息框信息可以看到事件的触发顺序依次是ButtonStackPanelBorderLabel。若在StackPanelMouseUp事件处理器中将e.Handled设置为true时,事件将不会继续传播,程序将不会在进入BoderLabel中的MouseUp事件处理器中。

e.Handled = true; 

当事件处于挂起时,还可以通过AddHandler()方法,AddHandler()方法提供一个重载版本,在之前的基础上在传递一个bool类型的参数,将参数设置为true时,即使Handled属性被设置为true也可以接收到事件。

lab.AddHandler(UIElement.MouseUpEvent, new MouseButtonEventHandler(Label_MouseUp), true); 

在设计器中删除Label元素的MouseUp事件特性,在初始化时通过以上代码连接事件处理器。当触发MouseUp时,Label消息框就会出现。

附加事件

<StackPanel Name="stpBtn" Grid.Row="2" ButtonBase.Click="DoSomething"> 	<Button Width="100" Height="30" /> 	<Button Width="100" Height="30" Margin="0,10" /> 	<Button Width="100" Height="30" /> </StackPanel> 

以上代码在StackPanel元素中关联Button元素的点击事件,当点击其中按钮时就会进图DoSomething()方法,通过类名.事件命关联事件。Click事件是在ButtonBase中定义,Button继承自ButtonBase。附加事件还可以通过以下方式连接:

//关联附加事件只能用UIElement.AddHandler()方法 不能用 += 运算符 stpBtn.AddHandler(Button.ClickEvent, new RoutedEventHandler(DoSomething)); 

隧道事件

隧道路由事件的工作方式和冒泡路由事件相同,但方向相反。隧道路由事件一般有Preview开头,WPF通常成对的定义隧道路由事件通常和冒泡事件。如果将隧道路由事件标记为已处理过(e.Handled=true),那么就不会发生冒泡路由事件,这是因为两个事件共享RoutedEventArgs的同一个实例。

WPF事件

WPF元素提供了许多事件,但最重要的事件通常包括5类,生命周期事件、鼠标事件、键盘事件、手写笔事件、多点触控事件。