今天我們來介紹一下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 }
每個事件處理程序都是類似的,他的功能是在調試時的輸出窗口里打印一行文本,這樣我們就可以清晰的看到每個事件處理的順序。
運行程序,並單擊第一個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中的表現,這樣能夠更加深刻的理解路由事件。
相關視頻請參考:http://v.youku.com/v_show/id_XMzc4NTc2ODIw.html