Windows Phone 學習筆記 (4) – 頁面切換及傳值

Windows Phone - Navigation

通常來說,App 不會只有一個頁面,可能會有兩個或多個頁面,所以我們要瞭解如何做到頁面的切換。當需要頁面切換時,有些時候需要將使用者所選擇的資料傳遞給下一個頁面,這時候就必須瞭解如何處理資料傳遞。
假設啟動頁面為 MainPage.xaml,我們新增了一個結果頁面叫ResultPage.xaml,那要如何從 MainPage.xaml 切換到 ResultPage.xaml 呢?

在 MainPage.xaml 中我新增了一個按鈕,當按下時就會切換到結果頁面,按鈕中只要寫下:
NavigationService.Navigate(new Uri("/ResultPage.xaml", UriKind.Relative));
NavigationService 是一個專門用來做導覽服務的類別,使用其中的 Navigate 方法即可將頁面導向我們要的頁面。Uri 類別中有兩個輸入參數,第 1 個是我們要前往的頁面檔名,第 2 個則是我們要對第 1 個參數使用絕對路徑或相對路徑。

通常在切換頁面時會使用相對路徑,也就是「/ + 檔案名稱」,斜線表示為同一目錄,所以假設頁面是在 view 目錄之下,那就必須寫成「/view/ResultPage.xaml」。

UriKind 還有另一個 Absolute 可選,表示路徑為絕對路徑,通常會用在取得網路上的資料,例如:http://abc.com/image.png 之類的檔案,這時候就要使用絕對路徑。

頁面切換事件

在做頁面切換時會觸發幾個事件:
  • Loaded
    在每次頁面載入完成時觸發。
  • Unloaded
    要從目前頁面切換到另一個頁面時觸發。
  • OnNavigatedFrom
    利用 NavigationService 從 A 頁面切換到 B 頁面時,在 A 頁面觸發。使用上必須覆寫此事件。
  • OnNavigatedTo
    利用 NavigationService 從 A 頁面切換到 B 頁面時,在 B 頁面觸發。使用上必須覆寫此事件。
我在兩個頁面(MainPage.xaml, ResultPage.xaml)中的這4個事件中分別輸出資料到Console中來看看事件的運作順序。

當 App 第 1 次開啟載入 MainPage.xaml 時,觸發 2 個事件:
MainPage: OnNavigatedTo
MainPage: Loaded

接著按下 MainPage.xaml 中的按鈕,前往 ResultPage.xaml,觸發的事件依序為:
MainPage: OnNavigatedFrom
ResultPage: OnNavigatedTo
MainPage: Unloaded
ResultPage: Loaded

再按下實體返回鍵,回到MainPage.xaml:
ResultPage: OnNavigatedFrom
MainPage: OnNavigatedTo
ResultPage: Unloaded
MainPage: Loaded

如此可以很清楚看到切換頁面的事件觸發順序。 但這裡有點要注意,有時候我們會在 OnNavigatedTo 事件取得前一頁面傳來的值。假設我們有 3 個頁面分別為 A, B, C。從 A 到 B 會觸發 B 的 OnNavigatedTo ,接著我們從 B 到 C,然後又返回 B ,會怎樣呢?會正常的觸發一次 B 的 OnNavigatedTo ,這在不傳遞資料的情況下不會有問題,但若 B 會接收 A 所傳來的資料,當使用者從 C 退回 B 時也會觸發 B 的 OnNavigatedTo ,可是因為是從 C 退回來的而不是 A ,所以若在 B 中接收資料,就可能產生錯誤,所以這部份記得要做好處理。或是利用別種傳遞資料的方式。

資料傳遞

傳資料的方式有很多種,適情況選擇適合的方式,這裡列出 4 種方式:
  • 全域變數
  • Url參數
  • PhoneApplicationSerivce 中的 State 屬性
  • Isolated storage(永久性儲存)

全域變數

利用 App 類別 (App.xaml.cs 檔),在該類別中新增 static 變數來做儲存,接著在呼叫 NavigationService.Navigate() 之前,先把資料存入這個 static 變數。然後在新頁面中 OnNavigatedTo 事件中取出這個 static 變數來使用。

利用全域變數的傳遞方式,該變數在整個App中的任何類別中都可以取用。

Url參數

這個方式的使用和網址後面常看到的一串query字串一樣,所以寫起來會像是:
ResultPage.xaml?msg=hello&count=1
在 ResultPage.xaml 中的 OnNavigatedTo 接收時這麼寫:
Debug.WriteLine(NavigationContext.QueryString["msg"]);
Debug.WriteLine(NavigationContext.QueryString["count"]);
很簡單吧~~傳值時,在頁面後的第一個參數用問號 (?) 之後每個都用 (&) 來分隔。

PhoneApplicationSerivce 中的 State 屬性

這個方式和全域方式有點像,因為整個 App 都可以取用,但我們不用 App 類別,而是利用 PhoneApplicationSerivce 的 State 屬性。

State 是一個實做 IDictionary 的類別,可以用來保存應用程式的相關資料。IDictionary是以一個鍵值對的方式來儲存資料,也就一個key會有一個對應的value。 使用時,必須先引用 Microsoft.Phone.Shell 的命名空間。
在當前頁面中這麼寫
PhoneApplicationService.Current.State["msg"] = "hello";
一樣在切換的頁面的 OnNavigatedTo 事件中寫:
object data = null; 
if (PhoneApplicationService.Current.State.TryGetValue("msg", out data))
     textBox1.Text = (string)data; 
else     
     textBox1.Text = "error";
使用TryGetValue是為了防止要取得的 key 不存在,若 key 不存在會發生錯誤。如果將資料保存在 State 中,那麼除非應用程式結束,不然在 Deactivated、Activated 事件中,這些資料都還會保留下來。

Isolated storage(永久性儲存)

Isolated storage 中文為隔離儲存區,每個 App 有自己的隔離儲存區,其他 App 無法取用,儲存在這裡的資料不會因為 App 關閉或手機關機而消失。 使用時必須引用 System.IO.IsolatedStorage 及 System.IO 命名空間。

寫入檔案:
IsolatedStorageFile isofile = IsolatedStorageFile.GetUserStoreForApplication(); 
if (isofile.FileExists("/data.txt"))
     isofile.DeleteFile("/data.txt"); 

StreamWriter sw = new StreamWriter(isofile.CreateFile("/data.txt"), System.Text.Encoding.UTF8); 
sw.WriteLine("要儲存的資料"); 
sw.Close(); 
sw.Dispose(); 
isofile.Dispose();
基本上就只是將資料寫入 data.txt 檔。

讀取檔案:
IsolatedStorageFile isofile = IsolatedStorageFile.GetUserStoreForApplication(); 
if (isofile.FileExists("/data.txt")) {
     StreamReader sr = new StreamReader(isofile.OpenFile("/data.txt", FileMode.Open), System.Text.Encoding.UTF8);
     string tmpString = sr.ReadLine();
     sr.Close();
     sr.Dispose();
     isofile.Dispose(); 
} 
所以在切換頁面前將資料寫入隔離區檔案儲存,在另一個頁面時讀取,就完成了資料傳遞。

總結

Isolated storage用來傳遞資料其實有點大材小用,因為它能做的事情太多了,通常會用來儲存像是圖片或檔案等必須永久性儲存的資料。全域性的資料儲存建議只有整個 App 會用到的才使用此方式,比較不容易因為某個地方改變了資料而產生錯誤。資料量少的話用url來傳是最方便的,也是大部份情況下會用到的方式。
本文網址:http://blog.tonycube.com/2012/06/windows-phone-4.html
Tony Blog 撰寫,轉載時請註明出處及文章連結,謝謝 😀

我要留言

留言小提醒:
1.回覆時間通常在晚上,如果太忙可能要等幾天。
2.請先瀏覽一下其他人的留言,也許有人問過同樣的問題。
3.程式碼請先將它編碼後再貼上。(線上編碼:http://bit.ly/1DL6yog)
4.文字請加上標點符號及斷行,難以閱讀者恕難回覆。
5.感謝您的留言,您的問題也可能幫助到其他有相同問題的人。