在開發以頁面為基礎的 WP7 應用程式時,最重要的就是瞭解所謂的巡覽框架 (Navigation Framework),而這類應用程式其實與 Silverlight 開發時大同小異,但也許還更簡單一些,我們只要瞭解 20% 的重點,就可以應用在 80% 的使用情境上,在此除了介紹一些比較常用的情境外,最後還是會依照我撰寫文章的慣例,列出詳細的相關連結與學習資源供各位參考。
記得我在撰寫【 WP7 修練 DAY 02:如何在頁面顯示前自動轉向到其他頁面 】時曾經在 Page 建構子使用 NavigationService 卻無法執行的問題,如下圖示:
會遇到這個問題,主要還是因為不瞭解頁面生命週期的關係,前陣子在 AppHub 發現一篇非常棒的文章: Windows Phone Silverlight Application Life Cycle,而這篇文章裡有另一張示意圖更加清楚明瞭的指出 WP7 應用程式的執行生命週期,以及每個事件被觸發的順序,我真的愛死這張圖啦!如下圖:
而 Windows Phone Silverlight Application Life Cycle 這份 19 頁的文件裡,還清清楚楚、明明白白的提到每一個階段的事件「應該」與「不應該」撰寫的程式碼,我認為任何一位英文閱讀程度還行的人,都應該仔細閱讀這份文件,對你絕對有極大幫助。
因此在學習 Navigation Framework 開發技巧之前,首先還是必須先瞭解應用程式生命週期才能事半功倍,以下幾篇是我推薦的文章:
最後我整理一些常用的程式碼片段與說明如下:
1. 切換至不同頁面的基本語法
在 WP7 應用程式裡,一般都會只用「相對連結」來巡覽網頁,所以如下範例在建立 Uri 物件時,都會傳入第二參數為 UriKind.Relative。
NavigationService.Navigate(new Uri("/Page1.xaml", UriKind.Relative));
請注意:最早能使用 NavigationService 屬性的事件是在 Page 類別的 OnNavigatedTo 方法裡。
2011/11/23 補充
除了透過程式碼轉向到另一個頁面外,也有另外一個完全無須定義撰寫程式的方法,只要透過 XAML 的設定即可,以下是設定的方式:
‧先在專案中新增以下兩個組件參考
‧再加上以下兩行命名空間到 XAML 的根元素
xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
xmlns:ic="clr-namespace:Microsoft.Expression.Interactivity.Core;assembly=Microsoft.Expression.Interactions"
‧最後參考如下 XAML 設定,替控制項定義一個 <i:Interaction.Triggers> 觸發事件,並透過 <ic:NavigateToPageAction> 直接指向事件觸發時直接導向到另一個頁面。
<Button Content="Button" Height="72" Width="160">
<i:Interaction.Triggers>
<i:EventTrigger EventName="Click">
<ic:NavigateToPageAction TargetPage="/Page1.xaml"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</Button>
2. 取得從上頁傳來的查詢字串 (QueryString)
讀取從上頁傳來的 QueryString 應該會從 Page 類別的 OnNavigatedTo 方法裡來取得,且須要特別注意的地方是:取得鍵值時不能「直接取得」,要先判斷在不在,否則就會發生以下錯誤:
正確的撰寫語法如下:
protected override void OnNavigatedTo(System.Windows.Navigation.NavigationEventArgs e)
{
base.OnNavigatedTo(e);
if (this.NavigationContext.QueryString.ContainsKey("Name"))
{
PageTitle.Text = this.NavigationContext.QueryString["Name"];
}
}
3. 自訂按鈕實作上一頁、下一頁功能
自行實作上一頁、下一頁功能,必須搭配 CanGoBack 與 CanGoForward 屬性,否則會導致 InvalidOperationException 例外發生,如下範例:
private void btnBack_Click(object sender, RoutedEventArgs e)
{
if (NavigationService.CanGoBack)
{
NavigationService.GoBack();
}
}
private void btnForward_Click(object sender, RoutedEventArgs e)
{
if (NavigationService.CanGoForward)
{
NavigationService.GoForward();
}
}
2011/11/23 補充:在目前的 Windows Phone 7.1 版本,其 NavigationService.CanGoForward 永遠會回傳 False,所以可以不用寫這段程式碼。不過,日後的 Windows Phone 版本也許會改變這個行為也說不定,留著供參考即可。
4. 不要宣告 NavigationService 屬性 的 Navigating 與 Navigated 事件
如果你想實作 Navigating 事件,應該改用 Page 類別的 OnNavigatingFrom 方法。
如果你想實作 Navigated 事件,應該改用 Page 類別的 OnNavigatedFrom 方法。
之所以為有這樣的建議,主要是因為當你實作與註冊這些事件,可能會導致 Page 物件狀態亂掉的情況,舉個例子來說,你註冊了一個事件,進入下一頁,然後再回到上一頁,就有可能導致重複註冊事件,同樣一段 Code 執行兩遍的情況,因此還是建議照著 MSDN 的建議去覆寫 (override) Page 類別的 OnNavigatingFrom 與 OnNavigatedFrom 方法才是王道。
5. 如何清除所有巡覽過的歷史紀錄
還記得我在【WP7 修練 DAY 01:如何設計啟動畫面 (Splash screen) 】文章裡提到的其中一個注意事項嗎?其描述如下:
請注意:這是為了測試方便才這樣寫的,此寫法有 2 個嚴重的缺點:一個是頁面會先讓你看到 MainPage.xaml 然後才會進入到 SplashScreen.xaml 頁面裡;另一個問題是進入該頁面後,若按下手機的上一頁按鈕,又會繼續跳到 SplashScreen.xaml 這頁,這並非常規的設計法則,小朋友請不要亂學。
然而,修練到 巡覽框架 (Navigation Framework) 時讓我學會了,只要能清除所有巡覽過的歷史紀錄,就不用擔心回上一頁之後又再次進入下一頁的情況,我們可以手動將所有巡覽過的歷史紀錄給清除乾淨,就不會讓我們再次進入第一頁了。以下是程式碼範例:
int numOfHistory = NavigationService.BackStack.Count();
for (int i = 0; i < numOfHistory; i++)
{
NavigationService.RemoveBackEntry();
}
這也代表著,設計啟動畫面又多了一種實作的方法,也就是直接用一個簡單的 MainPage 頁面,設計特殊的啟動畫面或動畫效果,然後在進入下一頁後直接將歷史紀錄 (BackEntry) 給清除,此時就再也不會進入你的啟動畫面了! ^_^
請注意:在 Windows Phone 7 手機裡,應用程式在執行時有個跟一般 Silverlight 應用程式很不一樣的特性,那就是應用程式剛開啟後,如果你按下手機的 Back 硬體按鍵,就會迫使應用程式自動結束。然而,如果你用了這個技巧把巡覽歷史紀錄給清楚乾淨,此時使用者按下手機的 Back 硬體按鍵就會導致應用程式自動結束。這也是我在修練過程中的意外發現!
2011/11/23 補充:打個比方說,你先進入 MainPage.xaml 再進入 PageB.xaml,此時按下 Back 鍵預設會回到 MainPage,假設你希望使用者從 MainPage.xaml 進入 PageB 之後不要再回到 MainPage 而是直接關閉應用程式,那麼清除歷史紀錄這方法就可以輕鬆做到這點需求。
今日修練總結
Silverlight 應用程式的巡覽框架 (Navigation Framework) 不是一個小主題,要寫的話可以寫很多,但是還好 Windows Phone 7 提供了一個簡化版的巡覽框架 ,你只要知道一些基本觀念,就能夠自由自在的遊走在 XAML 頁面之間。
當然,不免嘮叨的再耳提面命一次,瞭解 WP7 應用程式的執行生命週期極其重要,以下的相關連結記得都要看完、看懂才行。
相關連結