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