逻辑树
<Window>
<Grid>
<Button>
<StackPanel>
<Image/>
<TextBlock/>
</StackPanel>
</Button>
</Grid>
</Window>
但是实际上这些元素在运行时会扩展为可是树
事件路由
对逻辑树和可视树有所了解很有必要,因为路由事件主要是根据可视树进行路由。路由事件支持三种路由策略:气泡、隧道和直接。
气泡事件最为常见,它表示事件从源元素扩散(传播)到可视树,直到它被处理或到达根元素。这样您就可以针对源元素的上方层级对象处理事件。例如,您可向嵌入的 Grid 元素附加一个 Button.Click 处理程序,而不是直接将其附加到按钮本身。气泡事件有指示其操作的名称(例如,MouseDown)。
隧道事件采用另一种方式,从根元素开始,向下遍历元素树,直到被处理或到达事件的源元素。这样上游元素就可以在事件到达源元素之前先行截取并进行处理。根据命名惯例,隧道事件带有前缀 Preview(例如 PreviewMouseDown)。
直接事件类似 .NET Framework 中的正常事件。该事件唯一可能的处理程序是与其挂接的委托。
通常,如果为特殊事件定义了隧道事件,就会有相应的气泡事件。在这种情况下,隧道事件先触发,从根元素开始,下行至源元素,查找处理程序。一旦它被处理或到达源元素,即会触发气泡事件,从源元素上行,查找处理程序。气泡或隧道事件不会仅因调用事件处理程序而停止路由。如果您想中止隧道或气泡进程,可使用您传递的事件参数在事件处理程序中将事件标记为已处理。
示例(我们在Grid上加了一个Button.Click的附加事件:
01 |
< Window x:Class = "DeepXAML.MainWindow" |
04 |
xmlns:local = "clr-namespace:DeepXAML" |
05 |
xmlns:sys = "clr-namespace:System;assembly=mscorlib" |
06 |
Title = "MainWindow" Height = "250" Width = "450" > |
07 |
< Grid x:Name = "rootGrid" Button.Click = "rootGrid_Click" > |
08 |
< Button x:Name = "btnOK" Margin = "30" >OK</ Button > |
后台代码:
1 |
private void rootGrid_Click(object sender, RoutedEventArgs e) |
3 |
MessageBox.Show((e.Source as FrameworkElement).Name); //btnOk |
4 |
MessageBox.Show((e.OriginalSource as FrameworkElement).Name); //btnOk |
source是指LogicTree的源途,orginalSource指的是VisualTree上的源
自定义路由事件示例
01 |
< Window x:Class = "DeepXAML.MainWindow" |
04 |
xmlns:local = "clr-namespace:DeepXAML" |
05 |
xmlns:sys = "clr-namespace:System;assembly=mscorlib" |
06 |
Title = "MainWindow" Height = "250" Width = "450" > |
07 |
< Grid x:Name = "rootGrid" > |
08 |
< StackPanel x:Name = "stp1" local:TestButton.ClickTimeEvent = "TimeHanler" > |
09 |
< StackPanel x:Name = "stp2" local:TestButton.ClickTimeEvent = "TimeHanler" > |
10 |
< StackPanel x:Name = "stp3" local:TestButton.ClickTimeEvent = "TimeHanler" > |
11 |
< ListBox x:Name = "listBox" ></ ListBox > |
12 |
< local:TestButton local:TestButton.ClickTimeEvent = "TimeHanler" Height = "50" Margin = "30" >OK</ local:TestButton > |
02 |
using System.Collections.Generic; |
04 |
using System.Windows.Data; |
05 |
using System.Windows.Documents; |
06 |
using System.Windows.Controls; |
10 |
public partial class MainWindow : Window |
14 |
InitializeComponent(); |
18 |
private void TimeHanler( object sender, TimeEventArgs e) |
20 |
FrameworkElement element = sender as FrameworkElement; |
21 |
string strTime = e.ClickTime.ToLongTimeString(); |
22 |
this .listBox.Items.Add( element.Name+ ":" + strTime); |
26 |
public class TimeEventArgs : RoutedEventArgs |
28 |
public TimeEventArgs(RoutedEvent routedEvent, object source): base (routedEvent,source) |
31 |
public DateTime ClickTime { get ; set ; } |
34 |
public class TestButton : Button |
36 |
public static RoutedEvent timeEvent= |
37 |
EventManager.RegisterRoutedEvent( "ClickTimeEvent" , RoutingStrategy.Bubble, typeof (EventHandler<TimeEventArgs>), typeof (TestButton)); |
39 |
public event RoutedEventHandler ClickTimeEvent |
41 |
add { this .AddHandler(timeEvent,value);} |
42 |
remove{ this .RemoveHandler(timeEvent,value);} |
45 |
protected override void OnClick() |
48 |
TimeEventArgs args= new TimeEventArgs(timeEvent, this ); |
49 |
args.ClickTime=DateTime.UtcNow; |
50 |
this .RaiseEvent(args); |