Windows Phone中的路由事件-以ListBox控件为例
今天我们来介绍一下Windows Phone中的路由事件,以ListBox控件为例。
首先我们来熟悉一下路由事件的概念。
路由事件是具有更强传播能力的事件,他们可以在元素树中向上冒泡和向下隧道传播,并且沿着传播路径被事件处理程序处理。路由事件经常以冒泡路由事件和隧道路由事件的形式出现,冒泡路由事件是在元素树中向上传播的一种事件,触发事件的源会把事件传递给他的父元素,他的父元素又会将事件继续向上传递,直到传递到元素树的顶端,或者有着特殊的逻辑处理。稍后会给大家详细讲述冒泡路由事件的工作方式。隧道路由事件的工作方式和冒泡路由事件相同,但方向相反。他是在元素树中向下传播的一种事件,触发事件的源的会寻找他的子元素,然后把事件传递给他。隧道路由事件通常比较容易辨认,因为他们都以单词Preview开头。隧道路由事件总是在冒泡路由事件之前被触发。今天我们的重点是冒泡路由事件。
由于是讲Windows phone中的路由事件,那就要讲一下触摸屏设备所特有的事件--触摸事件。在Windows phone中 触摸事件主要有3种,比较简单,分别是ManipulationStarted事件,他是在用户的手指触摸到屏幕时触发的事件。ManipulationDelta事件,他是用户的手指在屏幕上滑动式触发的事件。ManipulationCompleted事件,他是用户的手指离开屏幕时触发的事件。值得注意的是,以上三种触摸事件都是冒泡路由事件。
好,下面让我们来结合程序详细介绍一下Windows phone中的路由事件。
新建一个Windows Phone应用程序,在内容Grid中添加以下XAML代码。
1 <ListBox x:Name="listBox"
2 ManipulationStarted="listBox_ManipulationStarted"
3 ManipulationCompleted="listBox_ManipulationCompleted"
4 >
5 <ListBoxItem x:Name="listBoxItem1"
6 ManipulationStarted="listBoxItem1_ManipulationStarted"
7 ManipulationCompleted="listBoxItem1_ManipulationCompleted">
8 <TextBlock x:Name="textBlock1" FontSize="30"
9 Text="文本一文本一文本一"
10 ManipulationStarted="textBlock1_ManipulationStarted"
11 ManipulationCompleted ="textBlock1_ManipulationCompleted"/>
12 </ListBoxItem>
13 <ListBoxItem x:Name="listBoxItem2"
14 ManipulationStarted="listBoxItem2_ManipulationStarted"
15 ManipulationCompleted="listBoxItem2_ManipulationCompleted">
16 <TextBlock x:Name="textBlock2" FontSize="30"
17 Text="文本二文本二文本二"
18 ManipulationStarted="textBlock2_ManipulationStarted"
19 ManipulationCompleted="textBlock2_ManipulationCompleted"/>
20 </ListBoxItem>
21 </ListBox>
这段代码比较简单,包括一个listbox控件,和两个listboxitem,每个listboxitem的内容也比较简单,就是一行文本,我们给每个控件都分别注册了ManipulationStarted事件和ManipulationCompleted事件。
这是完成后的手机界面:
接下来,我们添加后台的事件处理程序,上代码。
首先添加一个名字空间:
1 using System.Diagnostics;
然后是事件处理程序的代码:
1 private void listBox_ManipulationStarted(object sender, ManipulationStartedEventArgs e)
2 {
3 Debug.WriteLine("OUT PUT: listBox_ManipulationStarted in {0}", DateTime.Now.ToLongTimeString());
4 }
5
6 private void listBox_ManipulationCompleted(object sender, ManipulationCompletedEventArgs e)
7 {
8 Debug.WriteLine("OUT PUT: listBox_ManipulationCompleted in {0}", DateTime.Now.ToLongTimeString());
9 }
10
11 private void listBoxItem1_ManipulationStarted(object sender, ManipulationStartedEventArgs e)
12 {
13 Debug.WriteLine("OUT PUT: listBoxItem1_ManipulationStarted in {0}", DateTime.Now.ToLongTimeString());
14 }
15
16 private void listBoxItem1_ManipulationCompleted(object sender, ManipulationCompletedEventArgs e)
17 {
18 Debug.WriteLine("OUT PUT: listBoxItem1_ManipulationCompleted in {0}", DateTime.Now.ToLongTimeString());
19 }
20
21 private void textBlock1_ManipulationStarted(object sender, ManipulationStartedEventArgs e)
22 {
23 Debug.WriteLine("OUT PUT: textBlock1_ManipulationStarted in {0}", DateTime.Now.ToLongTimeString());
24 }
25
26 private void textBlock1_ManipulationCompleted(object sender, ManipulationCompletedEventArgs e)
27 {
28 Debug.WriteLine("OUT PUT: textBlock1_ManipulationCompleted in {0}", DateTime.Now.ToLongTimeString());
29 }
30
31 private void listBoxItem2_ManipulationStarted(object sender, ManipulationStartedEventArgs e)
32 {
33 Debug.WriteLine("OUT PUT: listBoxItem2_ManipulationStarted in {0}", DateTime.Now.ToLongTimeString());
34 }
35
36 private void listBoxItem2_ManipulationCompleted(object sender, ManipulationCompletedEventArgs e)
37 {
38 Debug.WriteLine("OUT PUT: listBoxItem2_ManipulationCompleted in {0}", DateTime.Now.ToLongTimeString());
39 }
40
41 private void textBlock2_ManipulationStarted(object sender, ManipulationStartedEventArgs e)
42 {
43 Debug.WriteLine("OUT PUT: textBlock2_ManipulationStarted in {0}", DateTime.Now.ToLongTimeString());
44 }
45
46 private void textBlock2_ManipulationCompleted(object sender, ManipulationCompletedEventArgs e)
47 {
48 Debug.WriteLine("OUT PUT: textBlock2_ManipulationCompleted in {0}", DateTime.Now.ToLongTimeString());
49 }
每个事件处理程序都是类似的,他的功能是在调试时的输出窗口里打印一行文本,这样我们就可以清晰的看到每个事件处理的顺序。 www..2cto.com
运行程序,并单击第一个ListBoxItem,我们发现输出窗口会打印一下文字:
我们首先观察前3行文字,他是一个完整的冒泡路由过程,从触发事件的TextBlock,再到ListBoxItem,最后到元素树的*元素ListBox终止(其实ListBox并不是真正的*元素,真正的*元素应该是phone:PhoneApplicationPage控件,但由于没有对phone:PhoneApplicationPage控件的触摸事件进行处理,所以在这里是无法显示的,目前我们姑且认为ListBox控件就是元素树的*元素)。我们再来看最后一行文字,比较奇怪,ManipulationCompleted事件并没有完成一个完整的冒泡路由过程,这是怎么回事呢?我们在此留下一个悬念,稍后会给大家解释。
我们继续完善代码。
首先在ListBox中添加一个ListBoxItem。
1 <ListBoxItem x:Name="listBoxItem3"
2 ManipulationStarted="listBoxItem3_ManipulationStarted"
3 ManipulationCompleted="listBoxItem3_ManipulationCompleted">
4 <CheckBox x:Name="checkBox"
5 ManipulationStarted="checkBox_ManipulationStarted"
6 ManipulationCompleted="checkBox_ManipulationCompleted"
7 >
8 <TextBlock x:Name="textBlock3" Text="文本三文本三文本三文本三文本三"
9 ManipulationStarted="textBlock3_ManipulationStarted"
10 ManipulationCompleted="textBlock3_ManipulationCompleted"/>
11 </CheckBox>
12 </ListBoxItem>
这个ListBoxItem的内容是一个CheckBox控件,CheckBox控件中又包含了一行文本。
这是添加完成后的手机界面。
接下来是事件处理程序的代码。
1 private void listBoxItem3_ManipulationStarted(object sender, ManipulationStartedEventArgs e)
2 {
3 Debug.WriteLine("OUT PUT: listBoxItem3_ManipulationStarted in {0}", DateTime.Now.ToLongTimeString());
4 }
5
6 private void listBoxItem3_ManipulationCompleted(object sender, ManipulationCompletedEventArgs e)
7 {
8 Debug.WriteLine("OUT PUT: listBoxItem3_ManipulationCompleted in {0}", DateTime.Now.ToLongTimeString());
9 }
10
11 private void checkBox_ManipulationStarted(object sender, ManipulationStartedEventArgs e)
12 {
13 Debug.WriteLine("OUT PUT: checkBox_ManipulationStarted in {0}", DateTime.Now.ToLongTimeString());
14 }
15
16 private void checkBox_ManipulationCompleted(object sender, ManipulationCompletedEventArgs e)
17 {
18 Debug.WriteLine("OUT PUT: checkBox_ManipulationCompleted in {0}", DateTime.Now.ToLongTimeString());
19 }
20
21 private void textBlock3_ManipulationStarted(object sender, ManipulationStartedEventArgs e)
22 {
23 Debug.WriteLine("OUT PUT: textBlock3_ManipulationStarted in {0}", DateTime.Now.ToLongTimeString());
24 }
25
26 private void textBlock3_ManipulationCompleted(object sender, ManipulationCompletedEventArgs e)
27 {
28 Debug.WriteLine("OUT PUT: textBlock3_ManipulationCompleted in {0}", DateTime.Now.ToLongTimeString());
29
30 }
和以前也是一样的,也是在调试时的输出窗口里打印一行文本。
运行程序,并单击新添加的带有CheckBox的ListBoxItem,我们会看到输出窗口会发生变化。
由于ListBoxItem中包含了一个带有文本的CheckBox控件,所以元素树的层次增加了一层。我们可以清晰的看到,和上一次不一样的是,不论是ManipulationStarted事件还是ManipulationCompleted事件都完成了完整的冒泡路由传递,这又是为什么呢?
为了进一步解释这个问题,我们进一步完善代码。
首先给ListBox控件注册一个SelectionChanged事件。
1 <ListBox x:Name="listBox"
2 ManipulationStarted="listBox_ManipulationStarted"
3 ManipulationCompleted="listBox_ManipulationCompleted"
4 SelectionChanged="listBox_SelectionChanged"
5 > 然后给SelectionChanged事件添加事件处理程序。
1 private void listBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
2 {
3 Debug.WriteLine("OUT PUT: listBox_SelectionChanged in {0}", DateTime.Now.ToLongTimeString());
4 } 该事件处理程序功能和原来是类似的。
运行程序,先后点击只有文本的ListBoxItem和带有CheckBox控件的ListBoxItem,我们注意对比两者的不同。
点击只有文本的ListBoxItem。
单击带有CheckBox控件的ListBoxItem
我们发现当单击只有文本的ListBoxItem的时候,在TextBlock控件的ManipulationCompleted事件后,触发了ListBox的SelectionChanged事件,而单击带有CheckBox控件的ListBoxItem的时候并没有触发ListBox的SelectionChanged事件,事实上这就是问题的关键所在。
当ListBoxItem中包含着对单击或触摸有特殊处理的控件(Button、CheckBox、RatioButton)的时候,不会触发ListBox的SelectionChanged事件,会将事件继续向上传递。而ListBoxItem中仅仅有自身对单击或触摸没有特殊处理的控件(TextBlock Image),就会触发ListBox的SelectionChanged事件,而SelectionChanged就不会向上继续传递了。因为已经到了*元素ListBox那里。这就是冒泡路由事件的向上传递被中断的原因。
好了,到现在大家对应该windows phone中的路由事件应该已经有了一个大致的了解,希望大家能自己建立一个示例程序,试验一下其他控件在ListBox中的表现,这样能够更加深刻的理解路由事件。
摘自 DannyLittle
下一篇: 90后小伙创业记