今天我們來介紹一下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