今天我们来介绍一下Windows Phone中的路由事件,以ListBox控件为例。
首先我们来熟悉一下路由事件的概念。
路由事件是具有更强传播能力的事件,他们可以在元素树中向上冒泡和向下隧道传播,并且沿着传播路径被事件处理程序处理。路由事件经常以冒泡路由事件和隧道路由事件的形式出现,冒泡路由事件是在元素树中向上传播的一种事件,触发事件的源会把事件传递给他的父元素,他的父元素又会将事件继续向上传递,直到传递到元素树的顶端,或者有着特殊的逻辑处理。稍后会给大家详细讲述冒泡路由事件的工作方式。隧道路由事件的工作方式和冒泡路由事件相同,但方向相反。他是在元素树中向下传播的一种事件,触发事件的源的会寻找他的子元素,然后把事件传递给他。隧道路由事件通常比较容易辨认,因为他们都以单词Preview开头。隧道路由事件总是在冒泡路由事件之前被触发。今天我们的重点是冒泡路由事件。
由于是讲Windows phone中的路由事件,那就要讲一下触摸屏设备所特有的事件--触摸事件。在Windows phone中 触摸事件主要有3种,比较简单,分别是ManipulationStarted事件,他是在用户的手指触摸到屏幕时触发的事件。ManipulationDelta事件,他是用户的手指在屏幕上滑动式触发的事件。ManipulationCompleted事件,他是用户的手指离开屏幕时触发的事件。值得注意的是,以上三种触摸事件都是冒泡路由事件。
好,下面让我们来结合程序详细介绍一下Windows phone中的路由事件。
新建一个Windows Phone应用程序,在内容Grid中添加以下XAML代码。
<ListBox x:Name="listBox"
ManipulationStarted="listBox_ManipulationStarted"
ManipulationCompleted="listBox_ManipulationCompleted"
>
<ListBoxItem x:Name="listBoxItem1"
ManipulationStarted="listBoxItem1_ManipulationStarted"
ManipulationCompleted="listBoxItem1_ManipulationCompleted">
<TextBlock x:Name="textBlock1" FontSize="30"
Text="文本一文本一文本一"
ManipulationStarted="textBlock1_ManipulationStarted"
ManipulationCompleted ="textBlock1_ManipulationCompleted"/>
ListBoxItem>
<ListBoxItem x:Name="listBoxItem2"
ManipulationStarted="listBoxItem2_ManipulationStarted"
ManipulationCompleted="listBoxItem2_ManipulationCompleted">
<TextBlock x:Name="textBlock2" FontSize="30"
Text="文本二文本二文本二"
ManipulationStarted="textBlock2_ManipulationStarted"
ManipulationCompleted="textBlock2_ManipulationCompleted"/>
ListBoxItem>
ListBox>
这段代码比较简单,包括一个listbox控件,和两个listboxitem,每个listboxitem的内容也比较简单,就是一行文本,我们给每个控件都分别注册了ManipulationStarted事件和ManipulationCompleted事件。
这是完成后的手机界面:
接下来,我们添加后台的事件处理程序,上代码。
首先添加一个名字空间:
using System.Diagnostics;
然后是事件处理程序的代码:
private void listBox_ManipulationStarted(object sender, ManipulationStartedEventArgs e)
{
Debug.WriteLine("OUT PUT: listBox_ManipulationStarted in {0}", DateTime.Now.ToLongTimeString());
}
private void listBox_ManipulationCompleted(object sender, ManipulationCompletedEventArgs e)
{
Debug.WriteLine("OUT PUT: listBox_ManipulationCompleted in {0}", DateTime.Now.ToLongTimeString());
}
private void listBoxItem1_ManipulationStarted(object sender, ManipulationStartedEventArgs e)
{
Debug.WriteLine("OUT PUT: listBoxItem1_ManipulationStarted in {0}", DateTime.Now.ToLongTimeString());
}
private void listBoxItem1_ManipulationCompleted(object sender, ManipulationCompletedEventArgs e)
{
Debug.WriteLine("OUT PUT: listBoxItem1_ManipulationCompleted in {0}", DateTime.Now.ToLongTimeString());
}
private void textBlock1_ManipulationStarted(object sender, ManipulationStartedEventArgs e)
{
Debug.WriteLine("OUT PUT: textBlock1_ManipulationStarted in {0}", DateTime.Now.ToLongTimeString());
}
private void textBlock1_ManipulationCompleted(object sender, ManipulationCompletedEventArgs e)
{
Debug.WriteLine("OUT PUT: textBlock1_ManipulationCompleted in {0}", DateTime.Now.ToLongTimeString());
}
private void listBoxItem2_ManipulationStarted(object sender, ManipulationStartedEventArgs e)
{
Debug.WriteLine("OUT PUT: listBoxItem2_ManipulationStarted in {0}", DateTime.Now.ToLongTimeString());
}
private void listBoxItem2_ManipulationCompleted(object sender, ManipulationCompletedEventArgs e)
{
Debug.WriteLine("OUT PUT: listBoxItem2_ManipulationCompleted in {0}", DateTime.Now.ToLongTimeString());
}
private void textBlock2_ManipulationStarted(object sender, ManipulationStartedEventArgs e)
{
Debug.WriteLine("OUT PUT: textBlock2_ManipulationStarted in {0}", DateTime.Now.ToLongTimeString());
}
private void textBlock2_ManipulationCompleted(object sender, ManipulationCompletedEventArgs e)
{
Debug.WriteLine("OUT PUT: textBlock2_ManipulationCompleted in {0}", DateTime.Now.ToLongTimeString());
}
每个事件处理程序都是类似的,他的功能是在调试时的输出窗口里打印一行文本,这样我们就可以清晰的看到每个事件处理的顺序。
运行程序,并单击第一个ListBoxItem,我们发现输出窗口会打印一下文字:
我们首先观察前3行文字,他是一个完整的冒泡路由过程,从触发事件的TextBlock,再到ListBoxItem,最后到元素树的顶级元素
ListBox终止(其实ListBox并不是真正的顶级元素,真正的顶级元素应该是phone:PhoneApplicationPage控件,但由于
没有对phone:PhoneApplicationPage控件的触摸事件进行处理,所以在这里是无法显示的,目前我们姑且认为ListBox控件就是
元素树的顶级元素)。我们再来看最后一行文字,比较奇怪,ManipulationCompleted事件并没有完成一个完整的冒泡路由过程,这是怎么回
事呢?我们在此留下一个悬念,稍后会给大家解释。
我们继续完善代码。
首先在ListBox中添加一个ListBoxItem。
<ListBoxItem x:Name="listBoxItem3"
ManipulationStarted="listBoxItem3_ManipulationStarted"
ManipulationCompleted="listBoxItem3_ManipulationCompleted">
<CheckBox x:Name="checkBox"
ManipulationStarted="checkBox_ManipulationStarted"
ManipulationCompleted="checkBox_ManipulationCompleted"
>
<TextBlock x:Name="textBlock3" Text="文本三文本三文本三文本三文本三"
ManipulationStarted="textBlock3_ManipulationStarted"
ManipulationCompleted="textBlock3_ManipulationCompleted"/>
CheckBox>
ListBoxItem>
这个ListBoxItem的内容是一个CheckBox控件,CheckBox控件中又包含了一行文本。
这是添加完成后的手机界面。
接下来是事件处理程序的代码。
private void listBoxItem3_ManipulationStarted(object sender, ManipulationStartedEventArgs e)
{
Debug.WriteLine("OUT PUT: listBoxItem3_ManipulationStarted in {0}", DateTime.Now.ToLongTimeString());
}
private void listBoxItem3_ManipulationCompleted(object sender, ManipulationCompletedEventArgs e)
{
Debug.WriteLine("OUT PUT: listBoxItem3_ManipulationCompleted in {0}", DateTime.Now.ToLongTimeString());
}
private void checkBox_ManipulationStarted(object sender, ManipulationStartedEventArgs e)
{
Debug.WriteLine("OUT PUT: checkBox_ManipulationStarted in {0}", DateTime.Now.ToLongTimeString());
}
private void checkBox_ManipulationCompleted(object sender, ManipulationCompletedEventArgs e)
{
Debug.WriteLine("OUT PUT: checkBox_ManipulationCompleted in {0}", DateTime.Now.ToLongTimeString());
}
private void textBlock3_ManipulationStarted(object sender, ManipulationStartedEventArgs e)
{
Debug.WriteLine("OUT PUT: textBlock3_ManipulationStarted in {0}", DateTime.Now.ToLongTimeString());
}
private void textBlock3_ManipulationCompleted(object sender, ManipulationCompletedEventArgs e)
{
Debug.WriteLine("OUT PUT: textBlock3_ManipulationCompleted in {0}", DateTime.Now.ToLongTimeString());
}
和以前也是一样的,也是在调试时的输出窗口里打印一行文本。
运行程序,并单击新添加的带有CheckBox的ListBoxItem,我们会看到输出窗口会发生变化。
由于ListBoxItem中包含了一个带有文本的CheckBox控件,所以元素树的层次增加了一层。我们可以清晰的看到,和上一次不一
样的是,不论是ManipulationStarted事件还是ManipulationCompleted事件都完成了完整的冒泡路由传递,这又是为什
么呢?
为了进一步解释这个问题,我们进一步完善代码。
首先给ListBox控件注册一个SelectionChanged事件。
<ListBox x:Name="listBox"
ManipulationStarted="listBox_ManipulationStarted"
ManipulationCompleted="listBox_ManipulationCompleted"
SelectionChanged="listBox_SelectionChanged"
>
然后给SelectionChanged事件添加事件处理程序。
private void listBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
Debug.WriteLine("OUT PUT: listBox_SelectionChanged in {0}", DateTime.Now.ToLongTimeString());
}
该事件处理程序功能和原来是类似的。
运行程序,先后点击只有文本的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中的表现,这样能够更加深刻的理解路由事件。
相关视频请参考:
阅读(1472) | 评论(0) | 转发(0) |