﻿# WP7 修練 DAY 09：使用巡覽框架的開發技巧與注意事項

在開發以頁面為基礎的 WP7 應用程式時，最重要的就是瞭解所謂的**巡覽框架** (Navigation Framework)，而這類應用程式其實與 Silverlight 開發時大同小異，但也許還更簡單一些，我們只要瞭解 20% 的重點，就可以應用在 80% 的使用情境上，在此除了介紹一些比較常用的情境外，最後還是會依照我撰寫文章的慣例，列出詳細的相關連結與學習資源供各位參考。

記得我在撰寫【 [WP7 修練 DAY 02：如何在頁面顯示前自動轉向到其他頁面](/post/2011/10/28/Windows-Phone-7-Xiu-Lian-DAY-02-Redirect-Before-Page-Show-up.aspx) 】時曾經在 Page 建構子使用 [NavigationService](http://msdn.microsoft.com/zh-tw/library/system.windows.navigation.navigationservice.aspx) 卻無法執行的問題，如下圖示：

![](https://stwillblogassets.blob.core.windows.net/files/images/external/stwillblogassets.blob.core.windows.net/b29e1b2cc47b04939ab8-image_1206.png)

會遇到這個問題，主要還是因為不瞭解頁面生命週期的關係，前陣子在 [AppHub](https://developer.microsoft.com/en-us/windows) 發現一篇非常棒的文章： Windows Phone Silverlight Application Life Cycle，而這篇文章裡有另一張示意圖更加清楚明瞭的指出 WP7 應用程式的執行生命週期，以及每個事件被觸發的順序，我真的愛死這張圖啦！如下圖：

![](https://stwillblogassets.blob.core.windows.net/files/images/external/stwillblogassets.blob.core.windows.net/57bf2d51a0617455e3df-image_1241.png)

而 Windows Phone Silverlight Application Life Cycle 這份 19 頁的文件裡，還清清楚楚、明明白白的提到每一個階段的事件「應該」與「不應該」撰寫的程式碼，我認為任何一位英文閱讀程度還行的人，都應該仔細閱讀這份文件，對你絕對有極大幫助。

因此在學習 Navigation Framework 開發技巧之前，首先還是必須先瞭解應用程式生命週期才能事半功倍，以下幾篇是我推薦的文章：

-   [邊做邊學 Silverlight for windows phone 7 Application life cycle & Navigation](http://msdn.microsoft.com/zh-tw/windowsphone/gg546046.aspx)
-   Windows Phone Silverlight Application Life Cycle
-   [Windows Phone 頁面導覽和控制項](http://msdn.microsoft.com/zh-tw/windowsphone/gg441226.aspx) ( Silverlight 實驗室 )
-   [NavigationService 類別](http://msdn.microsoft.com/zh-tw/library/system.windows.navigation.navigationservice\(VS.95\).aspx)

最後我整理一些常用的程式碼片段與說明如下：

**1\. 切換至不同頁面的基本語法**

在 WP7 應用程式裡，一般都會只用「相對連結」來巡覽網頁，所以如下範例在建立 [Uri](http://msdn.microsoft.com/zh-tw/library/ms131565\(VS.95\).aspx) 物件時，都會傳入第二參數為 [UriKind.Relative](http://msdn.microsoft.com/zh-tw/library/system.urikind\(VS.95\).aspx)。

```csharp
NavigationService.Navigate(new Uri("/Page1.xaml", UriKind.Relative));
```

請注意：最早能使用 [NavigationService 屬性](http://msdn.microsoft.com/zh-tw/library/system.windows.controls.page.navigationservice\(VS.95\).aspx)的事件是在 [Page 類別](http://msdn.microsoft.com/zh-tw/library/system.windows.controls.page\(VS.95\).aspx)的 [OnNavigatedTo 方法](http://msdn.microsoft.com/zh-tw/library/system.windows.controls.page.onnavigatedto\(VS.95\).aspx)裡。

**2011/11/23 補充**

除了透過程式碼轉向到另一個頁面外，也有另外一個完全無須定義撰寫程式的方法，只要透過 XAML 的設定即可，以下是設定的方式：

‧先在專案中新增以下兩個組件參考

![](https://stwillblogassets.blob.core.windows.net/files/images/external/stwillblogassets.blob.core.windows.net/9e1eebcd5d2958840059-image_1250.png)

‧再加上以下兩行命名空間到 XAML 的根元素

```csharp
xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
xmlns:ic="clr-namespace:Microsoft.Expression.Interactivity.Core;assembly=Microsoft.Expression.Interactions"
```

![](https://stwillblogassets.blob.core.windows.net/files/images/external/stwillblogassets.blob.core.windows.net/02b682bb99587de94ad6-image_1248.png)

‧最後參考如下 XAML 設定，替控制項定義一個 **<i:Interaction.Triggers>** 觸發事件，並透過 **<ic:NavigateToPageAction>** 直接指向事件觸發時直接導向到另一個頁面。

```xml
<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>
```

![](https://stwillblogassets.blob.core.windows.net/files/images/external/stwillblogassets.blob.core.windows.net/2f69a6d74b1174667f1b-image_1249.png)

**2\. 取得從上頁傳來的查詢字串 (QueryString)**

讀取從上頁傳來的 QueryString 應該會從 [Page 類別](http://msdn.microsoft.com/zh-tw/library/system.windows.controls.page\(VS.95\).aspx)的 [OnNavigatedTo 方法](http://msdn.microsoft.com/zh-tw/library/system.windows.controls.page.onnavigatedto\(VS.95\).aspx)裡來取得，且須要特別注意的地方是：取得鍵值時不能「直接取得」，要先判斷在不在，否則就會發生以下錯誤：

![](https://stwillblogassets.blob.core.windows.net/files/images/external/stwillblogassets.blob.core.windows.net/ce0de74bd78fbeef2990-image_1242.png)

正確的撰寫語法如下：

```csharp
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](http://msdn.microsoft.com/zh-tw/library/system.windows.navigation.navigationservice.cangoback\(VS.95\).aspx) 與 [CanGoForward](http://msdn.microsoft.com/zh-tw/library/system.windows.navigation.navigationservice.cangoforward\(VS.95\).aspx) 屬性，否則會導致 [InvalidOperationException](http://msdn.microsoft.com/zh-tw/library/system.invalidoperationexception\(VS.95\).aspx) 例外發生，如下範例：

```csharp
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 屬性](http://msdn.microsoft.com/zh-tw/library/system.windows.controls.page.navigationservice\(VS.95\).aspx)**[](http://msdn.microsoft.com/zh-tw/library/system.windows.navigation.navigationservice\(VS.95\).aspx) **的** [**Navigating**](http://msdn.microsoft.com/zh-tw/library/system.windows.navigation.navigationservice.navigating\(VS.95\).aspx) **與** [**Navigated**](http://msdn.microsoft.com/zh-tw/library/system.windows.navigation.navigationservice.navigated\(VS.95\).aspx) **事件**

如果你想實作 [Navigating](http://msdn.microsoft.com/zh-tw/library/system.windows.navigation.navigationservice.navigating\(VS.95\).aspx) 事件，應該改用 [Page 類別](http://msdn.microsoft.com/zh-tw/library/system.windows.controls.page\(VS.95\).aspx)的 [OnNavigatingFrom](http://msdn.microsoft.com/zh-tw/library/system.windows.controls.page.onnavigatingfrom\(VS.95\).aspx) 方法。  
如果你想實作 [Navigated](http://msdn.microsoft.com/zh-tw/library/system.windows.navigation.navigationservice.navigated\(VS.95\).aspx) 事件，應該改用 [Page 類別](http://msdn.microsoft.com/zh-tw/library/system.windows.controls.page\(VS.95\).aspx)的 [OnNavigatedFrom](http://msdn.microsoft.com/zh-tw/library/system.windows.controls.page.onnavigatedfrom\(VS.95\).aspx) 方法。

之所以為有這樣的建議，主要是因為當你實作與註冊這些事件，可能會導致 Page 物件狀態亂掉的情況，舉個例子來說，你註冊了一個事件，進入下一頁，然後再回到上一頁，就有可能導致重複註冊事件，同樣一段 Code 執行兩遍的情況，因此還是建議照著 MSDN 的建議去覆寫 (override) [Page 類別](http://msdn.microsoft.com/zh-tw/library/system.windows.controls.page\(VS.95\).aspx)的 [OnNavigatingFrom](http://msdn.microsoft.com/zh-tw/library/system.windows.controls.page.onnavigatingfrom\(VS.95\).aspx) 與 [OnNavigatedFrom](http://msdn.microsoft.com/zh-tw/library/system.windows.controls.page.onnavigatedfrom\(VS.95\).aspx) 方法才是王道。  

**5\. 如何清除所有巡覽過的歷史紀錄**

還記得我在【[WP7 修練 DAY 01：如何設計啟動畫面 (Splash screen)](/post/2011/10/25/Windows-Phone-7-Xiu-Lian-DAY-01-Splash-Screen.aspx) 】文章裡提到的其中一個注意事項嗎？其描述如下：

> 請注意：這是為了測試方便才這樣寫的，此寫法有 2 個嚴重的缺點：一個是頁面會先讓你看到 MainPage.xaml 然後才會進入到 SplashScreen.xaml 頁面裡；另一個問題是進入該頁面後，若按下手機的**上一頁**按鈕，又會繼續跳到 SplashScreen.xaml 這頁，這並非常規的設計法則，小朋友請不要亂學。

然而，修練到 **巡覽框架** (Navigation Framework) 時讓我學會了，只要能**清除所有巡覽過的歷史紀錄**，就不用擔心回上一頁之後又再次進入下一頁的情況，我們可以手動將所有巡覽過的歷史紀錄給清除乾淨，就不會讓我們再次進入第一頁了。以下是程式碼範例：

```csharp
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 應用程式的執行生命週期極其重要，以下的相關連結記得都要看完、看懂才行。

**相關連結**

-   Windows Phone Silverlight Application Life Cycle
-   [邊做邊學 Silverlight for windows phone 7 Application life cycle & Navigation](http://msdn.microsoft.com/zh-tw/windowsphone/gg546046.aspx)
-   [Windows Phone 頁面導覽和控制項](http://msdn.microsoft.com/zh-tw/windowsphone/gg441226.aspx) (Silverlight 實驗室)
-   [Frame and Page Navigation for Windows Phone](http://msdn.microsoft.com/en-us/library/ff941091\(VS.92\).aspx)
-   [NavigationService 類別](http://msdn.microsoft.com/zh-tw/library/system.windows.navigation.navigationservice\(VS.95\).aspx) ( Silverlight )
-   [巡覽概觀](http://msdn.microsoft.com/zh-tw/library/cc838245\(VS.95\).aspx)
-   [Windows Phone 7 : Working with Controls and Themes - Understanding Frame and Page Navigation](http://programming4.us/mobile/2712.aspx)
-   [Navigation Framework](https://learn.microsoft.com/en-us/previous-versions/windows/silverlight/dotnet-windows-silverlight/cc838245\(v=vs.95\)) (影片教學)
-   [The Silverlight Navigation Framework](http://www.silverlightshow.net/items/The-Silverlight-3-Navigation-Framework.aspx)
-   [Silverlight 3 Navigation API](http://www.silverlightshow.net/items/Silverlight-3-Navigation-API.aspx)
