本文章是由機器翻譯。

Windows Phone 7

您的第一個 Windows Phone 應用程式

Jesse Liberty

編寫首個 Windows Phone 應用程式的決竅是構建足夠有意義且足以真正實現的簡單內容。為此,我將指導您創建一個我日常使用的簡單實用程式: NoteToMe.創建思路是,您可以輸入一則消息,然後通過按一個按鈕將其發送給自己,如圖 1 所示。

The NoteToMe Interface圖 1 NoteToMe 介面

請注意,本文將涉及眾多主題,我將在後續文章中非常詳細地介紹其中每個主題。這些主題包括:

  • 創建應用程式佈局
  • 在獨立存儲中存儲和檢索資料
  • 事件和事件處理
  • 創建和運行任務(啟動器和選擇器)

在開始之前,您將需要從 http://xbox.create.msdn.com 中下載相關工具。如果您已解鎖您的手機,但尚未升級到 Windows Phone 7.5(“Mango”),那麼現在是升級到該系統的時候了;Mango 在 Windows Phone 作業系統中引入了數百個新功能。

開始使用

與許多 Windows Phone 開發人員一樣,我相信創建 Windows Phone 應用程式的最佳做法是結合使用 Visual Studio(用於編寫代碼)和 Expression Blend(用於完成其他工作)工具。因此,我們將首先打開 Expression Blend,然後基於 Windows Phone SDK 7.1 創建一個名為 NoteToMe 的新應用程式。

讓我們從更改應用程式標題開始。按一下該標題,然後在“屬性”視窗中,找到該控制項的“文本”屬性。Metro 設計準則(Windows Phone 的設計準則)要求標題全部為大寫字母,所以請將標題更改為 NOTE TO ME。

按一下頁面標題,然後點擊“刪除”將其刪除。

為創建佈局,您在頁面頂部附近需要一小行。按一下邊緣,將顯示一條可説明您直觀地選擇該行的放置位置的輔助線,如圖 2 所示。

Placing the Top Row of the Layout圖 2 定位佈局的頂端行

當然,您可以直接在 XAML 中手動設置行大小:

<Grid.RowDefinitions>
  <RowDefinition Height="1*"/>
  <RowDefinition Height="9*"/>
</Grid.RowDefinitions>

值後面的星號表示相對大小 - 在本例中為 1:9。 即,第一行的大小將是第二行的 1/9。

向 StackPanel 中添加三個控制項

頂端行中將有三個並排控制項:

  • 一個充當標籤的文字區塊
  • 一個用於容納電子郵寄地址的文字區塊
  • 一個用於發送消息的按鈕

此設計如圖 3 所示。

Three Controls in the Top Row
圖 3 頂端行中的三個控制項

如果未將三個控制項放在某種類型的組織容器中,則不能將它們放在單行的單列中。 我將使用其方向已設置為水準的 StackPanel - StackPanel 彼此堆疊或彼此相連。

若要創建 StackPanel,請按一下工具列上“佈局”控制項旁邊的白色小箭頭,如圖 4 所示。

Adding a StackPanel
圖 4 添加 StackPanel

按一下 StackPanel 以選擇控制項。 現在將一個 StackPanel 拖動到行中,並在“佈局”視窗中將其水準和垂直對齊方式設置為“拉伸”,將其邊距設置為零,如圖 5 所示。

Placing the StackPanel
圖 5 定位 StackPanel

添加文字區塊,並將其字型大小設置為 32,將其文本設置為 To(收件人)。 現在將一個文字方塊拖動到 StackPanel 上。 (注意用於顯示文本的文字區塊和用於文本輸入的文字方塊之間重要且細微的差別。)將此文字方塊命名為 Address(位址)。 最後,向 StackPanel 中添加一個按鈕,將其命名為 Send(發送),並將其 Content(內容)設置為 Send(發送)。

圖 6 顯示此生成的 XAML。

圖 6 使用 XAML 設計 StackPanel

<StackPanel
  Margin="0"
  Orientation="Horizontal">
  <TextBlock
    Margin="0,8"
    TextWrapping="Wrap"
    Text="To"
    Width="42"
    HorizontalAlignment="Left"
    VerticalAlignment="Center"
    FontSize="32" />
  <TextBox
    x:Name="Address"
    Margin="0,0,0,-7"
    TextWrapping="Wrap"
    Text="foo@bar.com"
    Width="293" />
  <Button
    x:Name="Send"
    Content="Send"
    Margin="0,4,0,0"
    Width="124"
    Click="Send_Click" />
</StackPanel>

請注意,Send(發送)按鈕具有 Click=“Send_Click” 屬性。 按一下該按鈕可創建此屬性,接下來,在“屬性”視窗中按一下“事件”按鈕,如圖 7 所示。

The Events Button
圖 7“事件”按鈕

這將打開該按鈕的所有事件。 找到 click 事件並按兩下。 會使用該事件更新按鈕,並且您會轉到該事件處理常式的代碼編輯器(在 Blend 或 Visual Studio 中,具體取決於您設置 Blend 的方式)。 您暫時可將此事件處理常式保持原樣:

private void Send_Click( object sender, RoutedEventArgs e )
{
}

添加消息控制項

按一下工具列中的文字方塊控制項,然後將文字方塊拖出,以填充剩餘頁面的一半位置(我們將另一半位置留給鍵盤,當需要在文字方塊中輸入內容時,會顯示鍵盤。) 將“水準對齊方式”設置為“拉伸”,將“垂直對齊方式”設置為“頂端對齊”,將邊距設置為 0。 將寬度設置為“自動”,將高度設置為 244。 可在調整文字方塊的大小時通過目測完成所有這些操作,也可在適當的位置粗略繪製文字方塊,並在“屬性”視窗中設置相應的屬性,如圖 8 所示。

Adding the TextBox
圖 8 添加文字方塊

編寫代碼

控制項就緒後,您就可以處理常式的邏輯了。 您應在左上角看到一個名為“專案”的選項卡。 保存所有更改後,按一下“專案”選項卡,然後按右鍵 MainPage.xaml.cs 並選擇“在 Visual Studio 中編輯”,如圖 9 所示。

Getting Ready to Write the Code
圖 9 準備編寫代碼

規範

我的(自願設定的)規範指出,您應該不必在每次使用程式時都填寫“To”(收件人)欄位;“To”(收件人)欄位中應預先填充了上次使用“To”(收件人)欄位時填充的內容。

而且,當您按一下“Send”(發送)時,應該已為您的電子郵件程式準備了一封新的電子郵件,其所有欄位均已預先填充,這樣您便只需按“Send”(發送),也可以選擇編輯該郵件,然後按“Send”(發送)。

使用獨立存儲

若要在使用應用程式時保留“To”(收件人)欄位的內容,您需要在手機的某個位置存儲這些內容。 此處是獨立存儲的用途:在關閉應用程式時保留資料。 正如其名稱所示,獨立存儲允許您的應用程式獨立存儲資料,並防止其他應用程式存儲資料。 使用獨立存儲非常簡單。

首先,添加 using 語句:

using System.IO.IsolatedStorage;

聲明一個類型為 IsolatedStorageSettings 的成員變數和一個充當獨立存儲詞典中的關鍵字的常量字串,以便可以存儲和檢索電子郵寄地址:

private IsolatedStorageSettings _isoSettings;
const string IsoKey = "EmailAddress";
Initialize the _isoSettings member in the constructor:
 _isoSettings = IsolatedStorageSettings.ApplicationSettings;

存儲和檢索電子郵寄地址

與獨立存儲相關的兩個任務是存儲和檢索字串。 最好在您離開頁面時存儲它。 當您離開任何 Windows Phone 頁面時,都會調用 OnNavigatedFrom 方法。 您可以隨時重寫該方法,而這樣做的一個充分理由是需要在獨立存儲中存儲資料,如下所示:

protected override void OnNavigatedFrom(
  System.Windows.Navigation.NavigationEventArgs e )
{
  _isoSettings[IsoKey] = Address.Text;
  base.OnNavigatedFrom( e );
}

您現在已將電子郵寄地址存儲在 _isoSettings 詞典中的 IsoKey 關鍵字下。 當您返回到該頁面時,可還原此設置。 我通過從構造函數中調用專用 Helper 方法 RestoreEmailAddress 來實現這一目的:

private void RestoreEmailAddress()
  {
    if (_isoSettings.Contains( IsoKey ))
      Address.Text = _isoSettings[IsoKey].ToString();
  }

請注意,我在嘗試還原此設置之前,執行了該關鍵字在獨立存儲中是否存在的測試操作 - 這可防止首次運行該程式時出現 KeyNotFound 異常。 請記住,在您首次運行程式時,尚未在獨立存儲中存儲任何內容。

首次啟動該程式時,“Address”(位址)欄位中沒有任何內容。 使用者在位址欄位中輸入電子郵寄地址後,該位址會存儲在獨立存儲中,並在下次運行該程式時還原。 如果使用者更改位址,該新位址即為還原後的位址。

任務

Windows Phone 7.5 支援大量可與內置手機應用程式(郵件、連絡人清單、相機等)交互的任務。 有兩種類型的任務:啟動器和選擇器。 選擇器用於選擇資訊並將其返回到程式(例如,為了從連絡人清單中獲取電子郵寄地址)。 啟動器用於啟動不返回資料的程式。

在本例中,您已完成發送郵件所需的全部工作,所以可調用電子郵件啟動器並提供所需的欄位。 在電子郵件啟動器上調用“Show”(顯示)時,將使用您的資料啟動電子郵件應用程式,但您不會獲得任何資料(這樣很好;您不需要任何資料)。

發送電子郵件後,如果您需要發送其他郵件,將返回到該程式。

創建啟動器的所有工作都封裝在“Send”(發送)按鈕的 click 事件處理常式中。 讓我們先創建 EmailComposeTask 的一個實例(啟動器)。 填充相關欄位並調用“Show”(顯示)。 就這麼簡單:

private void Send_Click( object sender, RoutedEventArgs e )
{
  EmailComposeTask emailComposeTask = new EmailComposeTask();
  emailComposeTask.Subject = "Send To Me";
  emailComposeTask.To = Address.Text;
  emailComposeTask.Body = Message.Text;
  Message.Text = String.Empty;
  emailComposeTask.Show();
}

調用“Show”(顯示)時,郵件的主題、位址和正文會傳遞到您的電子郵件應用程式中。 如果您有多個電子郵件應用程式,會詢問您要使用哪一個。 將創建一封位址和格式都正確的電子郵件,便於您準備發送。

應用程式生命週期

如果確信使用者在發送郵件前從不會中斷其對應用程式的使用,則您已經大功告成。 但在現實中,使用者會在撰寫郵件的過程中停下來,並啟動其他應用程式。 當他們返回時,如果他們所做的工作丟失,他們會感到不高興。

若要瞭解如何防止出現這種情況,您需要瞭解一些關於應用程式生命週期的資訊,以及如何在保持相應狀態的同時仍支援 Mango 的一項最強大的功能:快速應用程式切換。

當啟動應用程式(假定從“開始”功能表啟動)時,會引發 Application Launching 事件。 應用程式啟動後,當使用者每次導航到您的頁面時,都會調用 OnNavigatedTo 方法,隨後頁面會進入“正在運行”狀態。 如果使用者啟動新應用程式,您的應用程式會收到 Application Deactivated 事件,並進入休眠狀態。 如果手機的記憶體不足,則您的應用程式可能處於邏輯刪除狀態。

在邏輯刪除或休眠狀態下,可終止或還原您的應用程式。 目前我們關心的是還原應用程式時所發生的情況。

如果您的應用程式處於休眠狀態,那麼在還原時,您不必執行任何操作,而且也不需要執行任何操作;當應用程式處於休眠狀態時,會保持該狀態,隨時待命。

但如果應用程式處於邏輯刪除狀態,那麼當應用程式返回時,您確實需要還原頁面狀態,以便向使用者表明,該應用程式在切換出來時處於運行(或至少處於休眠)狀態。

因此,您需要完成兩個任務:

  1. 在調用頁面的 OnNavigatedFrom 方法時保存相應狀態。
  2. 在調用頁面的 OnNavigatedTo 方法時可能需要還原相應狀態,即,如果應用程式處於邏輯刪除狀態,則還原狀態;如果處於休眠狀態,則不需要還原。

在離開頁面時保存相應狀態

因為您無法知道,頁面什麼時候會收到 OnNavigatedFrom、還原時頁面可能處於何種狀態,所以您必須存儲狀態,以備不時之需。 這很容易實現:您可以使用其語法與獨立存儲詞典非常類似的 State 詞典,但您必須知道編寫 State 詞典的目的不是為了永久存儲,並且在退出程式或關閉手機時,實際上會損壞該詞典。

讓我們先創建一個常量字串 StateKey,您將在 State 詞典中將其用作偏移量:

const string StateKey = "MessageState";

在 OnNavigatedFrom 方法中,您將在 State 詞典中存儲狀態(在本例中,為訊息方塊中的內容):

protected override void OnNavigatedFrom(
  System.Windows.Navigation.NavigationEventArgs e )
{
  _isoSettings[IsoKey] = Address.Text;
  State[StateKey] = Message.Text;
  base.OnNavigatedFrom( e );
}

在創建頁面時還原相應狀態

當調用 OnNavigatedTo 方法時,如果應用程式處於休眠狀態,那麼您不需要執行任何操作來還原狀態,但如果應用程式處於邏輯刪除狀態,則確實需要執行一些操作。

您可以通過在構造函數中將某標記設置為 false,然後將其設置為 true 來區分休眠或邏輯刪除狀態。 如果應用程式處於休眠狀態,則不會調用構造函數;如果它處於邏輯刪除狀態,則會調用構造函數(因為將首次構造該程式),如下所示:

bool isNew = false;
  public MainPage()
  {
    InitializeComponent();
    isNew = true;

可在 OnNavigatedTo 中檢查該標記:

protected override void OnNavigatedTo(
  System.Windows.Navigation.NavigationEventArgs e )
{
  if (isNew)
  {
    if (State.ContainsKey( StateKey ))
    {
      Message.Text = State[StateKey].ToString();
    }
  }
  isNew = false;
  base.OnNavigatedTo( e );
}

此測試會節省從 State 詞典中還原值所需的時間。 可通過先正常運行程式(在這種情況下,當您切換到其他應用程式時,您的程式會進入休眠狀態),然後強制該程式進入邏輯刪除狀態來執行這一測試。 可通過如下方法強制您的程式進入邏輯刪除狀態:按右鍵專案,選擇“屬性”,選擇“調試”選項卡,並選中“Tombstone upon deactivation while debugging”(調試期間在停用後進入邏輯刪除狀態)核取方塊。

在選中此核取方塊的情形下運行程式時,您會在返回頁面時注意到明顯的暫停,這是因為必須還原該狀態。

最後概述

在這篇簡短的文章中,我簡要介紹了如何編寫您的第一個重要的 Windows Phone 應用程式。 我先在 Expression Blend 中創建該應用程式,在此過程中我創建了一個行,並使用 StackPanel 來設計控制項佈局。

然後,我切換到 Visual Studio,以編寫按鈕的事件處理常式的邏輯,並使用獨立存儲來保存電子郵寄地址。 我使用了 State 記憶體,以確保應用程式能夠在進入邏輯刪除狀態後正常地重新開機。

正如前面所說,對於其中每個主題,還有許多相關內容要介紹,我將在後續文章中詳細介紹它們。

Jesse Liberty 是 Windows Phone 團隊的高級開發人員-社區推廣專家。Liberty 主辦了受歡迎的 Yet Another 播客 (jesseliberty.com/podcast),並且他的博客 (jesseliberty.com/) 也值得一讀。他是許多熱銷書的作者,其中包括“Programming Reactive Extensions and LINQ”(Apress,2011 年)和“Migrating to Windows Phone”(Apress,2011 年)。請在 Twitter 上關注 Liberty:twitter.com/JesseLiberty

衷心感謝以下技術專家對本文的審閱:Drew BatchelorCheryl Simmons