WP7的Push Notifications

来源:互联网 发布:百度软件中心手机 编辑:程序博客网 时间:2024/05/09 18:20

概觀

Windows Phone 中的 Microsoft Push Notification Service 向協力廠商開發人員提供了一個彈性,專注,而且持續的管道,使得開發人員可以從 web service 向移動應用程式發送資訊和更新。

過去移動應用程式需要經常主動去調查其相應的 Web 服務,以瞭解是否有任何等待處理的通知。這樣做雖然有效,但是會導致手機的無線設備頻繁打開,從而對電池續航時間帶來負面影響。使用 Push Notification 的方式取代主動調查,web service 能夠提醒應用程式獲取所需要的重要更新。


圖1
Push Notifications

當一個 Web service 有資訊要發送到應用程式,它先發送一個通知到 Push Notification Service,該服務隨後將通知路由到應用程式。根據 Push Notification 的格式和裝載量,資訊作為原始資料傳遞到應用程式,應用程式的標題明顯地更新或顯示一個 Toast 通知。然後如果需要的話應用程式可以使用自己的協定聯繫 web service 以獲取更新。

Push Notification Service 在 Push Notification 發送後向你的 web service 發送一個回應碼。然而,Push Notification Service 不能為你的推送提醒是否確實成功傳遞到應用程式提供端的確認。瞭解更多資訊,請參考Push Notification Service Response Codes for Windows Phone。

 

本實驗包括了 Push Notification,並介紹了 Silverlight 中的 HTTP 服務的使用。在這個實驗中,您將建立透過 Push Notification ServicePush Notification 服務發送消息必需的伺服器端的邏輯,你也將建立一個簡單的 Windows Phone 7 應用程式,作為用戶端接收這種通知。用戶端應用接收天氣狀況更新,伺服器端業務應用 (簡單的WPF 應用程式) 會透過 Push Notification Services 發送天氣提醒到已註冊的用戶端應用程式,一旦用戶端應用程式接收到這種提醒,它會顯示接收到的資訊。


目標

完成本實驗你將:

  • 熟悉 Windows Phone 7 應用程式的通訊能力。
  • 熟悉 Push Notification 的概念和他們在手機上啟用後的行為。
  • 理解 Push Notification 在手機端和雲端如何工作。
  • 使用手機的 Push Notification 服務建立一個訂閱 Tokens (tiles),Toasts 和 RAW Push Notification。
  • 使用 web 用戶端來申請 Push Notifications。
  • 使用網路狀態來顯示當前的手機的網路狀態。
  • 建立一個 SL 應用程式訂閱 Push Notification 服務 (包括 token 和 toast)
    • 執行過程中處理 push 事件 (token,toast,和 raw) 。
    • 在 shell 上顯示 token 和 toast。

前置條件

以下是完成本次實驗所必須的條件:

  • 用於 Windows Phone 的 Microsoft Visual Studio 2010 Express 或者 Microsoft Visual Studio 2010。
  • Windows Phone 開發工具。

說明:所有的 Windows Phone 開發工具都可以從以下網址打包下載 http://developer.windowsphone.com


安裝程式碼片段

為了方便起見,本次實驗所使用的大部分程式碼都封裝為 Visual Studio 的程式碼片段提供。

若要安裝程式碼片段:

  1. 執行本實驗 Source \ Setup 資料夾下的 .vsi 安裝程式。

注釋:如果你在執行程式碼片段的安裝程式時遇到問題,你可以透過拷貝Source \ Setup \ CodeSnippets 資料夾下的所有 .snippet 檔到目錄:

My Documents \ Visual Studio 2010 \ Code Snippets \ Visual C# \ My Code Snippets


使用程式碼片段

透過程式碼片段,你可以隨時獲取所有你想要的程式碼。本實驗手冊將會準確的告訴你什麼時候使用它們。例如:


圖2
使用 Visual Studio 程式碼片段把程式碼插入到你的專案裡

為了把程式碼片段添加到 Visual Studio 中,你只要把游標放在你想插入的程式碼上,開始輸入段落的名字 (沒有空格和連字型大小),看到智慧感知的段落名稱,然後當你想要的段落名稱被選中時,敲擊 Tab 鍵兩次,程式碼將會被插入到游標的位置。


圖3
開始輸入程式碼片段落名稱


圖4
按 Tab 鍵來選取已選擇的段落


圖5
再次敲擊 Tab 鍵來展開程式碼片段

如果想使用滑鼠而不是鍵盤來插入程式碼片段,在你想插入程式碼片段的地方按右鍵,選擇 My Code Snippets 下面的InsertSnippet,然後從列表中挑選相關的程式碼片段。

想學習更多的 Visual Studio 程式碼片段,包括如何建立自己的程式碼片段,請參考

http://msdn.microsoft.com/en-us/library/ms165392.aspx。


練習

本實驗包括以下練習:

  1. 用於更新的 Windows Phone RAW Notifications 介紹。
  2. 用於提醒的 Toast 和 Tile Notifications 介紹。

完成實驗估計耗時:90分鐘


Before you begin…


Push Notification 很像是目前手機電信業者所提供的簡訊服務,由微軟在雲端上提供一個 Microsoft Push Notification Services (MPN Services) 的雲端服務 (這個服務建構在 Windows Azure Platform 上),而企業在自己的應用程式內實作一個服務,登錄並將訊息提交給 Push Notification Services,而位於 Windows Phone 內的用戶端應用程式在執行時開放一個雙向的 HTTP Channel 給 Push Notification Services,當 Push Notification Services 接到要發送給用戶端應用程式的訊息時,就會透過這個雙向的 HTTP Channel 發送訊息用戶端給應用程式。

在 Windows Phone Blog 上有數篇文章,有更多在 MPN Services 架構上使用上的探討:

  • Understanding Microsoft Push Notifications for Windows Phones
  • Understanding How Microsoft Push Notification Works – Part 2
  • Using Push Notification from Your Windows Phone Application

練習 1:用於更新的 Windows Phone RAW Notifications 介紹

本部分我們將打開 starter solution 並:

  • 實現伺服器端通知和註冊服務。
  • 建立Windows Phone 7 用戶端應用程式。
  • 建立notification 通道和訂閱通道事件。
  • 接收並處理來自 Push Notification Services 的事件。

我們將使用 Windows Phone 開發環境的 Microsoft Visual Studio 2010 Express,並部署到 Windows Phone 模擬器來除錯,我們要使用的解決方案是以 Silverlight for Windows Phone Application 範本發展的。開發過程中,我們將增加一個 Windows Phone 的 Silverlight 專案項目 - Windows Phone Portrait Page (直向頁)。

注意:本實驗的步驟示範使用 Microsoft Visual Studio 2010 Express for Windows Phone 的過程,但它們同樣適用於 Microsoft Visual Studio 2010 for Windows Phone。一般涉及到 Visual Studio 的用法說明同樣適用於這兩種產品。

任務 1 – 建立 Weather Service Solution

在此任務中,您將使用本實驗提供的 Microsoft Visual Studio 2010 Express for Windows Phone 或 Microsoft Visual Studio 2010 啟動解決方案。它包括簡單的 WPF 用戶端應用程式,該程式透過 Microsoft Push Notification Service 發送消息到Windows Phone 7 應用程式並承載 WCF 註冊服務,這個服務將在本任務過程中建立。這裡提供的 WPF 應用程式會自我裝載 (self-hosting) RESTful WCF 服務。為此本專案已經擁有所有的需要的配置。

1. 從開始|所有程式| Microsoft Visual Studio 2010 Express | Microsoft Visual Studio 2010 Express for Windows Phone 中打開 Microsoft Visual Studio 2010 Express for Windows Phone 。

Visual Studio 2010:從開始|所有程式| Microsoft Visual Studio 2010打開 Visual Studio 2010。

重要提醒:為了執行自我裝載 WCF 服務,Visual Studio 2010 Express for Windows Phone 或者Microsoft Visual Studio 2010 必須在管理模式下打開。 關於建立和提供自我裝載 WCF 服務請參考MSDN 文章 (http://msdn.microsoft.com/en-us/library/ms731758.aspx)。在管理模式下打開Visual Studio 2010 Express for Windows Phone or Visual Studio 2010,找到Microsoft Visual Studio 2010 Express for Windows Phone 快捷方式Start | All Programs | Microsoft Visual Studio 2010 Express 或者 Microsoft Visual Studio 2010 快捷方式Start | All Programs | Microsoft Visual Studio 2010,在圖示上按一下右鍵,然後從快顯功能表中選擇 “Run as administrator”。可能會彈出 UAC 提示 ,點按“Yes” 來允許 Visual Studio 2010 Express for Windows Phone 或者Visual Studio 2010 使用管理員許可權。

2. 在 File 功能表中,選擇 Open Project

Visual Studio 2010:在 File 功能表中, 指向 open 然後選擇Project / Solution

3. 找到位於本實驗 Source \ Ex1- RawNotifications \ Begin 資料夾中的 starter 專案,選擇Begin.sln,然後點擊 Open


圖 6
打開 starting 專案

4. 檢查打開的專案:

a. 這是一個標準的 WPF 應用程式。

注意:為了支援自我裝載 RESTful WCF 服務,此應用程式執行於 .NET Framework 4 而不是 .NET Framework 4 Client Profile。

NOTE

在 Visual Studio 2010 內,只要是主控台、Windows Forms 或 WPF 應用程式專案範本,在一開始建立專案時的類別庫是使用 .NET Framework 4 Client Profile,這個類別庫是 .NET Framework 4 類別庫的子集,擁有基本的 .NET Framework 4 應用程式所需的功能,但部份特殊功能沒有,本實驗中所需要的 WCF 服務類別庫在 .NET Framework 4 Client Profile 內沒有,所以必須要用 .NET Framework 4。

b. WPF 應用程式由 MainWindow 螢幕,PushNotificationsLogViewer 使用者控制項和 StatusToBrush 值轉換器 (Value Converter) 組成。

c. 在“Service” 專案資料夾中包括了 WCF RESTful 服務需要的介面定義。

d. 除了標準 WPF 應用程式引用,該應用程式還引用 system.serviceModel 和 System.ServiceModel.Web 元件來支援 RESTful 的 WCF 服務。

5. 按 F5 編譯並執行應用程式。熟悉一下它然後關閉應用程式並回到 Visual Studio,應用程式允許你發送Tile,Toast 和 Raw HTTP 通知。


圖 7
Push Notification 用戶端應用程式

6. 在接下來的幾個步驟,您將建立一個 WCF 服務實體並初始化它。為了能與 Push Notification Service 通信,叫用者必須提供註冊服務時的 URI。你將在本實驗的下面幾個任務中建立通道並註冊服務。在Service 專案資料夾中添加新類別。為此,在專案資料夾上點擊右鍵並選擇 AddàNew Class


圖 8
在項目中添加新類別

7. 命名為 RegistrationService,然後點擊 Add 按鈕。


圖 9
命名新類別

8. 打開新建立的類別。

9. 將建立的類別設為 public 並實作 IRegistrationService 介面。這個介面定義了一套方法來註冊手機的 URI。

(Code Snippet – Using Push Notifications – Registration Service – Implementing the interface)

C#
  1. public class RegistrationService : IRegistrationService
  2. {
  3. }

10. 點擊 IRegistrationService,透過在 “_” 標誌上停駐以打開快顯功能表。


圖 11
實作介面

12. 類別程式碼如以下程式碼片段所示。你可以添加 IRegistrationService Members 到 region 包圍的地方。

C#
  1. public class RegistrationService : IRegistrationService
  2. {
  3. #region IRegistrationService Members
  4. public void Register(string uri)
  5. {
  6. throw new NotImplementedException();
  7. }
  8. public void Unregister(string uri)
  9. {
  10. throw new NotImplementedException();
  11. }
  12. #endregion
  13. }

13. 註冊服務在內部清單中保存所有註冊客戶,並提醒用戶端是否註冊。另外它會檢查用戶端請求是否已經註冊以避免重複註冊。為支援此功能,添加以下類別變數(public 和private):

(Code Snippet – Using Push Notifications – Registration Service – Class variables)

C#
  1. public static event EventHandler<SubscriptionEventArgs> Subscribed;
  2. private static List<Uri> subscribers = new List<Uri>();
  3. private static object obj = new object();

14. 使用以下程式碼片段替換 Register 函數內容:

(Code Snippet – Using Push Notifications – Registration Service – Register function body)

C#
  1. Uri channelUri = new Uri(uri, UriKind.Absolute);
  2. Subscribe(channelUri);

15. 使用以下程式碼片段替換 Unregister 函數內容:

(Code Snippet – Using Push Notifications – Registration Service – Unregister function body)

C#
  1. Uri channelUri = new Uri(uri, UriKind.Absolute);
  2. Unsubscribe(channelUri);

16. 使用以下程式碼片段建立一個新region,包括 SubscribeUnsubscribe 輔助函數:

(Code Snippet – Using Push Notifications – Registration Service – Subscription and Unsubscription region)

C#
  1. #region Subscription/Unsubscribing logic
  2. private void Subscribe(Uri channelUri)
  3. {
  4. lock (obj)
  5. {
  6. if (!subscribers.Exists((u) => u == channelUri))
  7. {
  8. subscribers.Add(channelUri);
  9. }
  10. }
  11. OnSubscribed(channelUri, true);
  12. }
  13. public static void Unsubscribe(Uri channelUri)
  14. {
  15. lock (obj)
  16. {
  17. subscribers.Remove(channelUri);
  18. }
  19. OnSubscribed(channelUri, false);
  20. }
  21. #endregion

17. 使用以下程式碼片段建立 OnSubscribed 函數:

(Code Snippet – Using Push Notifications – Registration Service – OnSubscribed function region)

C#
  1. #region Helper private functionality
  2. private static void OnSubscribed(Uri channelUri, bool isActive)
  3. {
  4. EventHandler<SubscriptionEventArgs> handler = Subscribed;
  5. if (handler != null)
  6. {
  7. handler(null,
  8. new SubscriptionEventArgs(channelUri, isActive));
  9. }
  10. }
  11. #endregion

18. 為保存 Subscribed 事件參數,使用以下程式碼片段建立 SubscriptionEventArgs 內部類別 (在RegistrationService 類別中):

(Code Snippet – Using Push Notifications – Registration Service – SubscriptionEventArgs internal class region)

C#
  1. #region Internal SubscriptionEventArgs class definition
  2. public class SubscriptionEventArgs : EventArgs
  3. {
  4. public SubscriptionEventArgs(Uri channelUri, bool isActive)
  5. {
  6. this.ChannelUri = channelUri;
  7. this.IsActive = isActive;
  8. }
  9. public Uri ChannelUri { get; private set; }
  10. public bool IsActive { get; private set; }
  11. }
  12. #endregion

19. 最後在類別中建立一個公用函數,返回所有訂閱者列表-後面的 WPF 用戶端應用程式會用到它。

(Code Snippet – Using Push Notifications – Registration Service – GetSubscribers helper function region)

C#
  1. #region Helper public functionality
  2. public static List<Uri> GetSubscribers()
  3. {
  4. return subscribers;
  5. }
  6. #endregion

20. 打開 App.xaml.cs 文件。

21. 找到程式碼行 “//TODO - remove remark after creating registration service” 並從 WCF 服務主機初始化中刪除註解 (如下所示):

C#
  1. //TODO - remove remark after creating registration service
  2. host = new ServiceHost(typeof(RegistrationService));
  3. host.Open();

22. 編譯並執行應用程式。應用程式啟動之後,要檢查 RESTful WCF 服務是否可用。為此在 RegistrationService.cs 類別的Register 和 / 或 Unregister functions 內加上一個中斷點。


圖 12
使用中斷點檢查 RESTful WCF 服務

23. 執行Internet Explorer 並轉到下面位址:

http://localhost:8000/RegirstatorService/Register?uri=http://www.microsoft.com

24. 當中斷點命中時,檢查收到的 URI 位址是否為 http://www.microsoft.com。


圖 13
檢查 RESTful WCF 服務

25. 停止除錯,刪除中斷點並關閉 Internet Explorer。

26. 為了與Push Notification Service 通信,需要建立一個工具類別。這個類別會擁有所有通訊邏輯。從本實驗的 Source \ Assets 資料夾添加NotificationSenderUtility 項目。這是一個引用了 .NET 4 Framework 的空類別庫專案,而不是 .NET 4 Framework Client Profile。

27. 打開 NotificationSenderUtility.cs 空類別。

28. 這個類別包含與 Microsoft Push Notification Service 通信的所有必需功能。接下來的幾步中,你會添加功能來編寫並發送消息到Push Notification Services。與此同時你會公開 WPF Client 應用程式要使用的公用函數,並使之能夠向已註冊用戶端發送消息。這個類會使用非同步方式與 Push Notification Service 通信,並使用回呼函數 (callback function) 提醒叫用者。

為了方便建立業務邏輯和管理各種通知類型,在名稱空間中添加一個新的 NotificationType 列舉型別 (在 NotificationSenderUtility 類別上面或者下面)。

(Code Snippet – Using Push Notifications – NotificationSenderUtility – NotificationType Enum)

C#
  1. #region NotificationType Enum
  2. public enum NotificationType
  3. {
  4. Token = 1,
  5. Toast = 2,
  6. Raw = 3
  7. }
  8. #endregion

29. 下一步,緊接著 Enum 定義 (NotificationSenderUtility 類別之外) 添加一個類別來保存從 Push Notification Service 接收的資料:

(Code Snippet – Using Push Notifications – NotificationSenderUtility – CallbackArgs class)

C#
  1. #region Event & Event Handler Definition
  2. public class CallbackArgs
  3. {
  4. public CallbackArgs(NotificationType notificationType, HttpWebResponse response)
  5. {
  6. this.Timestamp = DateTimeOffset.Now;
  7. this.MessageId = response.Headers[NotificationSenderUtility.MESSAGE_ID_HEADER];
  8. this.ChannelUri = response.ResponseUri.ToString();
  9. this.NotificationType = notificationType;
  10. this.StatusCode = response.StatusCode;
  11. this.NotificationStatus = response.Headers[NotificationSenderUtility.NOTIFICATION_STATUS_HEADER];
  12. this.DeviceConnectionStatus = response.Headers[NotificationSenderUtility.DEVICE_CONNECTION_STATUS_HEADER];
  13. this.SubscriptionStatus = response.Headers[NotificationSenderUtility.SUBSCRIPTION_STATUS_HEADER];
  14. }
  15. public DateTimeOffset Timestamp { get; private set; }
  16. public string MessageId { get; private set; }
  17. public string ChannelUri { get; private set; }
  18. public NotificationType NotificationType { get; private set; }
  19. public HttpStatusCode StatusCode { get; private set; }
  20. public string NotificationStatus { get; private set; }
  21. public string DeviceConnectionStatus { get; private set; }
  22. public string SubscriptionStatus { get; private set; }
  23. }
  24. #endregion

30. 下面在 NotificationSenderUtility 類中添加與 Push Notification Service 通信相關的常數 。

(Code Snippet – Using Push Notifications – NotificationSenderUtility – Push Notification Service communication constants)

C#
  1. #region Local constants
  2. public const string MESSAGE_ID_HEADER = "X-MessageID";
  3. public const string NOTIFICATION_CLASS_HEADER = "X-NotificationClass";
  4. public const string NOTIFICATION_STATUS_HEADER = "X-NotificationStatus";
  5. public const string DEVICE_CONNECTION_STATUS_HEADER = "X-DeviceConnectionStatus";
  6. public const string SUBSCRIPTION_STATUS_HEADER = "X-SubscriptionStatus";
  7. public const string WINDOWSPHONE_TARGET_HEADER = "X-WindowsPhone-Target";
  8. public const int MAX_PAYLOAD_LENGTH = 1024;
  9. #endregion

31. 建立委派回呼函數,並添加輔助函數來執行委派回呼 (delegated callback):

(Code Snippet – Using Push Notifications – NotificationSenderUtility – Notification Callback Delegate)

C#
  1. #region Notification Callback Delegate Definition
  2. public delegate void SendNotificationToMPNSCompleted(CallbackArgs response);
  3. #endregion

(Code Snippet – Using Push Notifications – NotificationSenderUtility – Helper function to executed delegated callback function)

C#
  1. #region Callback call
  2. protected void OnNotified(NotificationType notificationType, HttpWebResponse response, SendNotificationToMPNSCompleted callback)
  3. {
  4. CallbackArgs args = new CallbackArgs(notificationType, response);
  5. if (null != callback)
  6. callback(args);
  7. }
  8. #endregion

32. 接下來的幾步中你會建立功能來發送 RAW HTTP 通知到 Push Notification Service。在這些步驟中,你會建立一個通用函數,它會根據通知類型準備並發送的消息。首先,建立一個名為SendRawNotifiaction 的 public 函數來向所有訂閱客戶發送 RAW HTTP 通知。在 NotificationSenderUtility 類別中添加以下程式碼片段:

(Code Snippet – Using Push Notifications – NotificationSenderUtility – SendRawNotifiaction function)

C#
  1. #region SendXXXNotification functionality
  2. public void SendRawNotification(List<Uri> Uris, byte[] Payload, SendNotificationToMPNSCompleted callback)
  3. {
  4. foreach (var uri in Uris)
  5. SendNotificationByType(uri, Payload, NotificationType.Raw, callback);
  6. }
  7. #endregion

33. 接下來,添加一個 SendNotificationByType 函數-通用函數,用來叫用指定的發送功能:

(Code Snippet – Using Push Notifications – NotificationSenderUtility – SendNotificationByType function)

C#
  1. #region SendNotificatioByType Logic
  2. private void SendNotificationByType(Uri channelUri, byte[] payload, NotificationType notificationType, SendNotificationToMPNSCompleted callback)
  3. {
  4. try
  5. {
  6. SendMessage(channelUri, payload, notificationType, callback);
  7. }
  8. catch (Exception ex)
  9. {
  10. throw;
  11. }
  12. }
  13. #endregion

34. Microsoft Push Notification Service 是一個雲端服務,它用於推送消息到 Windows Phone 7 訂閱客戶。本任務中隨後你會建立一個 Windows Phone 7 應用程式,這個應用程式會打開一個來自此服務的推送通道。從 Push Notification Service 訂閱流程返回的回復就是通道 URI - 用戶端設備的唯一標識。為了推送通知訊息到設備,Push Notification Service 在這個 URI 上等待包含送往此設備的資料負載的 web 叫用(web 請求)。收到請求後,Push Notification Service 會找到此設備並發送資料封包 (payload of data)。前面的步驟中,你建立了叫用服務所必需的功能。

SendMessage 函數負責通信,它會檢查資料封包的長度並在長度超過時拋出例外。如果封包正常,它會使用 HttpWebRequest 類別 (來自System.Net namespace,更多消息參考:http://msdn.microsoft.com/en-us/library/system.net.httpwebrequest.aspx) 來準備並向 URI 通道寫入請求資料流。發送資訊後,此函數等待來自 Push Notification Service 的回覆,然後透過委派回呼的方法通知叫用者函數。

添加以下函數,用來向 Push Notification Service 發送請求:

(Code Snippet – Using Push Notifications – NotificationSenderUtility – SendMessage function)

C#
  1. #region Send Message to Microsoft Push Service
  2. private void SendMessage(Uri channelUri, byte[] payload, NotificationType notificationType, SendNotificationToMPNSCompleted callback)
  3. {
  4. //Check the length of the payload and reject it if too long
  5. if (payload.Length > MAX_PAYLOAD_LENGTH)
  6. throw new ArgumentOutOfRangeException("Payload is too long. Maximum payload size shouldn't exceed " + MAX_PAYLOAD_LENGTH.ToString() + " bytes");
  7. try
  8. {
  9. // TODO : send message to MPNS
  10. }
  11. catch (WebException ex)
  12. {
  13. if (ex.Status == WebExceptionStatus.ProtocolError)
  14. {
  15. //Notify client on exception
  16. OnNotified(notificationType, (HttpWebResponse)ex.Response, callback);
  17. }
  18. throw;
  19. }
  20. }
  21. #endregion

35. 在 try 區塊中,找到 “// TODO : send message to MPNS” 注釋,用下面的 HttpWebRequest 初始化替換掉:

(Code Snippet – Using Push Notifications – NotificationSenderUtility – HttpWebRequest initializations)

C#
  1. //Create and initialize the request object
  2. HttpWebRequest request = (HttpWebRequest)WebRequest.Create(channelUri);
  3. request.Method = WebRequestMethods.Http.Post;
  4. request.ContentType = "text/xml; charset=utf-8";
  5. request.ContentLength = payload.Length;
  6. request.Headers[MESSAGE_ID_HEADER] = Guid.NewGuid().ToString();
  7. request.Headers[NOTIFICATION_CLASS_HEADER] = ((int)notificationType).ToString();
  8. if (notificationType == NotificationType.Toast)
  9. request.Headers[WINDOWSPHONE_TARGET_HEADER] = "toast";
  10. else if (notificationType == NotificationType.Token)
  11. request.Headers[WINDOWSPHONE_TARGET_HEADER] = "token";

36. 現在,當請求準備好之後,你需要用非同步方式打開它。當資料流打開時,你會得到 Stream 物件 (來自 System.IO namespace,更多資訊:http://msdn.microsoft.com/en-us/library/system.io.stream.aspx)。這個 Stream 物件代表請求資料流並會用來 (非同步) 寫入封包。寫入過程結束後,函數會切換到獲取 Push Notification Service 的回覆,收到回覆後它會使用委派函數來通知叫用者。將以下程式碼片段添加到函數內容,位於上一步驟的程式碼片段之後 (在try 語句之內) :

(Code Snippet – Using Push Notifications – NotificationSenderUtility – MPNS call)

C#
  1. request.BeginGetRequestStream((ar) =>
  2. {
  3. //Once async call returns get the Stream object
  4. Stream requestStream = request.EndGetRequestStream(ar);
  5. //and start to write the payload to the stream asynchronously
  6. requestStream.BeginWrite(payload, 0, payload.Length, (iar) =>
  7. {
  8. //When the writing is done, close the stream
  9. requestStream.EndWrite(iar);
  10. requestStream.Close();
  11. //and switch to receiving the response from MPNS
  12. request.BeginGetResponse((iarr) =>
  13. {
  14. using (WebResponse response = request.EndGetResponse(iarr))
  15. {
  16. //Notify the caller with the MPNS results
  17. OnNotified(notificationType, (HttpWebResponse)response, callback);
  18. }
  19. },
  20. null);
  21. },
  22. null);
  23. },
  24. null);

37. 保存並關閉 NotificationSenderUtility 類別。

38. 從 Weather 應用程式中添加一個引用到 NotificationSenderUtility 類別庫,在Weather 專案的 References 資料夾上點擊右鍵,然後選擇 Add Reference


圖 14
添加引用

39. 在打開的對話方塊中,點擊 Projects 頁籤,選擇 NotificationSenderUtility 並點擊 OK。


圖 15
添加專案引用

40. 打開位於 Weather 專案之下的 MainWindow.xaml.cs

41. 在類別中添加以下 using 宣告:

(Code Snippet – Using Push Notifications – MainWindow.xaml.cs – Using statements)

C#
  1. using System.Collections.ObjectModel;
  2. using System.Threading;
  3. using System.IO;
  4. using System.Xml;
  5. using WindowsPhone.PushNotificationManager;
  6. using WeatherService.Service;

42. 找到 Private variables 區域並使用以下程式碼片段替換 “TODO” 注釋:

(Code Snippet – Using Push Notifications – MainWindow.xaml.cs – Private variables)

C#
  1. private ObservableCollection<CallbackArgs> trace = new ObservableCollection<CallbackArgs>();
  2. private NotificationSenderUtility notifier = new NotificationSenderUtility();
  3. private string[] lastSent = null;

43. 我們的 Windows Phone 7 用戶端應用程式馬上就會擁有邏輯去訂閱前面建立的註冊服務。WPF 用戶端應用程式透過推送天氣資訊到連線的用戶端來類比真實世界網站解決方案,因此它應該瞭解用戶端Windows Phone 7 應用程式並接收註冊事件。另外,我們的 WPF 用戶端會顯示 Push notification Service 通信日誌。找到MainWindow 類別的建構函數並添加以下程式碼片段到“TODO” 注釋後面:

(Code Snippet – Using Push Notifications – MainWindow.xaml.cs – Ctor additional initializations)

C#
  1. Log.ItemsSource = trace;
  2. RegistrationService.Subscribed += new EventHandler<RegistrationService.SubscriptionEventArgs>(RegistrationService_Subscribed);

44. 找到 Event Handlers 區域並添加以下函數:

(Code Snippet – Using Push Notifications – MainWindow.xaml.cs – RegistrationService_Subscribed function)

C#
  1. void RegistrationService_Subscribed(object sender, RegistrationService.SubscriptionEventArgs e)
  2. {
  3. //Check previous notifications, and resent last one to connected client
  4. Dispatcher.BeginInvoke((Action)(() =>
  5. { UpdateStatus(); })
  6. );
  7. }

45. 在前面的函數之後,添加以下程式碼作為 NotificationSenderUtility 的回呼函數:

(Code Snippet – Using Push Notifications – MainWindow.xaml.cs – OnMessageSent function)

C#
  1. private void OnMessageSent(CallbackArgs response)
  2. {
  3. Dispatcher.BeginInvoke((Action)(() => { trace.Add(response); }));
  4. }

46. 找到 Private functionality 區域並添加以下函數 (這個函數更新 WPF 用戶端 應用程式 UI):

(Code Snippet – Using Push Notifications – MainWindow.xaml.cs – UpdateStatus function)

C#
  1. private void UpdateStatus()
  2. {
  3. int activeSubscribers = RegistrationService.GetSubscribers().Count;
  4. bool isReady = (activeSubscribers > 0);
  5. txtActiveConnections.Text = activeSubscribers.ToString();
  6. txtStatus.Text = isReady ? "Ready" : "Waiting for connection...";
  7. }

47. 找到 sendHttp 函數並添加以下程式碼。這個函數得到連線的用戶端清單,從 UI 輸入準備負載並使用一個 ThreadPool 透過 NotificationSenderUtility 非同步發送它 (來自System.Threading namespace. 瞭解更多 ThreadPool 的資訊,參考:http://msdn.microsoft.com/en-us/library/system.threading.threadpool.aspx):

(Code Snippet – Using Push Notifications – MainWindow.xaml.cs – sendHttp function body)

C#
  1. //Get the list of subscribed WP7 clients
  2. List<Uri> subscribers = RegistrationService.GetSubscribers();
  3. //Prepare payload
  4. byte[] payload = prepareRAWPayload(
  5. cmbLocation.SelectedValue as string,
  6. sld.Value.ToString("F1"),
  7. cmbWeather.SelectedValue as string);
  8. //Invoke sending logic asynchronously
  9. ThreadPool.QueueUserWorkItem((unused) => notifier.SendRawNotification(subscribers,
  10. payload,
  11. OnMessageSent)
  12. );
  13. //Save last RAW notification for future usage
  14. lastSent = new string[3];
  15. lastSent[0] = cmbLocation.SelectedValue as string;
  16. lastSent[1] = sld.Value.ToString("F1");
  17. lastSent[2] = cmbWeather.SelectedValue as string;

注意:在我們的具體情況下,當 NotificationSenderUtility 已經實現非同步通訊方式,沒有絕對必要使用 ThreadPool 非同步叫用功能,但是一般 d 來說會推薦使用這種最佳實踐方式 (Best Practice) 以叫用通信功能。通訊是一個長時間執行的行程,如果內部不能支援非同步作業,即使是簡單的叫用都會凍結 UI。這就是為什麼一般情況下叫用長時間執行的行程都會使用非同步方式。

48. 找到 Private functionality 區域並添加 prepareRAWPayload 函數。這個函數在記憶體中建立一個 XML 文件並傳回一個 byte 陣列 (可以隨時發送到 Windows Phone Push Notification Services 然後從那裡傳送到 Windows Phone 應用程式客戶)。本練習的後面你會在終端設備上添加功能來重新解析成 XML。添加以下程式碼片段:

(Code Snippet – Using Push Notifications – MainWindow.xaml.cs – PrepareRAWPayload function)

C#
  1. private static byte[] prepareRAWPayload(string location, string temperature, string weatherType)
  2. {
  3. MemoryStream stream = new MemoryStream();
  4. XmlWriterSettings settings = new XmlWriterSettings()
  5. { Indent = true, Encoding = Encoding.UTF8 };
  6. XmlWriter writer = XmlTextWriter.Create(stream, settings);
  7. writer.WriteStartDocument();
  8. writer.WriteStartElement("WeatherUpdate");
  9. writer.WriteStartElement("Location");
  10. writer.WriteValue(location);
  11. writer.WriteEndElement();
  12. writer.WriteStartElement("Temperature");
  13. writer.WriteValue(temperature);
  14. writer.WriteEndElement();
  15. writer.WriteStartElement("WeatherType");
  16. writer.WriteValue(weatherType);
  17. writer.WriteEndElement();
  18. writer.WriteStartElement("LastUpdated");
  19. writer.WriteValue(DateTime.Now.ToString());
  20. writer.WriteEndElement();
  21. writer.WriteEndElement();
  22. writer.WriteEndDocument();
  23. writer.Close();
  24. byte[] payload = stream.ToArray();
  25. return payload;
  26. }

49. 找到 RegistrationService_Subscribed 函數,並在 UpdateStatus 叫用之前添加以下程式碼片段來重新發送 RAW 消息到連線客戶:

(Code Snippet – Using Push Notifications – MainWindow.xaml.cs – Resend RAW message)

C#
  1. void RegistrationService_Subscribed(object sender, RegistrationService.SubscriptionEventArgs e)
  2. {
  3. //Check previous notifications, and resent last one to connected client
  4. if (null != lastSent)
  5. {
  6. string location = lastSent[0];
  7. string temperature = lastSent[1];
  8. string weatherType = lastSent[2];
  9. List<Uri> subscribers = new List<Uri>();
  10. subscribers.Add(e.ChannelUri);
  11. byte[] payload = prepareRAWPayload(location, temperature, weatherType);
  12. ThreadPool.QueueUserWorkItem((unused) => notifier.SendRawNotification(subscribers, payload, OnMessageSent));
  13. }
  14. Dispatcher.BeginInvoke((Action)(() =>
  15. { UpdateStatus(); })
  16. );
  17. }

50. 編譯應用程式並修正編譯錯誤 (如果有的話)。本任務到此結束。


任務 2 – 建立 Windows Phone 7 Client 應用程式

1. 在solution 中添加新的專案。選擇 Windows Phone Application,命名為 PushNotifications


圖 16
Solution 中添加新 Windows Phone Application 專案

2. 添加 System.Xml.Linq 組件的引用 (從 .NET 選項頁籤):


圖 17
添加引用到 System.Xml.Linq

3. 在本實驗的 Source \ Assets 下找到 Assets 資料夾並找到 Styles.txt。使用記事本打開它。

4. 打開 App.xaml.cs 並從 Styles.txt 中拷貝所有的 XAML 到 Application.Resources 部分。

5. 為 System CLR 命名空間添加以下藍色反白的 clr-namespace 宣告,位於 Application 標記內 (靠近檔頂部)。

XAML
<Application     ...    xmlns:system="clr-namespace:System;assembly=mscorlib">    ...</Application>

6. 從 Assets 資料夾找到名為 CloudBackgroundMobile.jpg 的圖片添加到專案。 為此在 PushNotifications (專案名稱) 上點擊右鍵然後選擇 Add à Existing Item。在“Add Existing Item” 對話方塊中,找到Source \ Assets 資料夾,選擇 CloudBackgroundMobile.jpg 並點擊 Add 按鈕。


圖 18
添加現有圖像資源

7. 打開 MainPage.xaml (如果沒有自動打開的話)。

8. 找到 LayoutRoot grid,並使用以下程式碼片段替換其內容:

XAML
<Grid x:Name="LayoutRoot" Background="Transparent">   <Grid.RowDefinitions>      <RowDefinition Height="120"/>      <RowDefinition Height="*"/>      <RowDefinition Height="150"/>      <RowDefinition Height="Auto"/>   </Grid.RowDefinitions>   <Image Source="cloudbackgroundmobile.jpg" Grid.RowSpan="4" />        <Grid x:Name="TitleGrid" Grid.Row="0" VerticalAlignment="Top">            <TextBlock Text="WEATHER SERVICE" x:Name="textBlockPageTitle" Style="{StaticResource PhoneTextPageTitle1Style}" />        </Grid>        <Grid Grid.Row="1" x:Name="ContentPanel" Background="#10000000">            <TextBlock x:Name="textBlockListTitle" FontFamily="Segoe WP Light" FontSize="108" Text="City" Margin="20,10,0,0" />            <TextBlock x:Name="txtTemperature" FontFamily="Segoe WP" FontSize="160" Text="80°" Margin="20,100,0,0" />            <Image x:Name="imgWeatherConditions" Width="128" Height="128" Stretch="None" HorizontalAlignment="Right" VerticalAlignment="Top" Margin="20,155,20,0" />        </Grid>        <StackPanel Grid.Row="3" x:Name="StatusStackPanel" Margin="20">            <TextBlock FontSize="34" FontFamily="Segoe WP Semibold" Foreground="#104f6f" Text="Status" Style="{StaticResource PhoneTextNormalStyle}" />            <TextBlock x:Name="txtStatus" FontFamily="Segoe WP" FontSize="24" Foreground="#0a364c" Margin="0,0,0,0" Style="{StaticResource PhoneTextNormalStyle}" Text="Not Connected" TextWrapping="Wrap" />        </StackPanel></Grid>

9. 打開 MainPage.xaml.cs。

10. 添加以下 using 宣告:

(Code Snippet – Using Push Notifications – MainPage.xaml.cs – Using statments)

C#
  1. using Microsoft.Phone.Notification;
  2. using System.Diagnostics;
  3. using System.Windows.Threading;
  4. using System.Windows.Media.Imaging;
  5. using System.IO;
  6. using System.Xml.Linq;
  7. using System.IO.IsolatedStorage;
  8. using System.Collections.ObjectModel;

11. 在類別的開始部分添加以下程式碼片段,包含一些私有變數和常數:

(Code Snippet – Using Push Notifications – MainPage.xaml.cs – Private variables)

C#
  1. private HttpNotificationChannel httpChannel;
  2. const string channelName = "WeatherUpdatesChannel";
  3. const string fileName = "PushNotificationsSettings.dat";
  4. const int pushConnectTimeout = 30;

12. 添加以下帶有輔助函數的程式碼片段。這些函數會更新Windows Phone 7 應用程式的 UI 狀態條並輸出有用的追蹤資訊:

(Code Snippet – Using Push Notifications – MainPage.xaml.cs – Updating and Tracing functions)

C#
  1. #region Tracing and Status Updates
  2. private void UpdateStatus(string message)
  3. {
  4. txtStatus.Text = message;
  5. }
  6. private void Trace(string message)
  7. {
  8. #if DEBUG
  9. Debug.WriteLine(message);
  10. #endif
  11. }
  12. #endregion

13. 設置 PushNotifications 專案為啟動專案。為此在專案名稱上點擊右鍵,然後選擇 Set as Startup Project


圖 19
設置 PushNotification 為啟動專案

14. 編譯並執行應用程式。

15. 此階段應用程式應該如下所示:


圖 20
執行 Weather 用戶端

16. 停止除錯並回到程式碼。本任務到此結束。


任務 3 – 建立 Notification 通道

在接下來的幾個步驟,您要開發建立新的 Push Notification 通道和訂閱通道事件所必需的功能。HttpNotificationChannel 是一個在 Push Notification 服務和 Push Client 之間建立通知通道的類別,它用來建立一個 raw,tile和toast 通知的訂閱。該通道的創造流程是這樣的:如果通道已經存在,則用戶端應用程式應嘗試重新打開它。試圖重新建立已存在的通道將導致例外。如果通道沒有打開,訂閱通道事件,並嘗試打開通道。一旦通道打開它會觸發 ChannelUriUpdated 事件。此事件可能向用戶端發送成功建立通道的信號。現有的通道可以根據名稱找到,成功找到通道的情況下,通道將被重新啟動,並可以在應用程式中使用。整個過程是非同步的。

1. 從 PushNotifications 專案打開 MainPage.xaml.cs

注意:在當前版本的模擬器存在一個錯誤,模擬器啟動之後立刻試圖打開新的通道會導致一個例外。此錯誤會阻礙模擬器打開一個有效的管道 20 – 30 秒,並且每次啟動模擬器都會出現。為了繞過這個問題,本實驗將檢查此例外,將應用程式執行延遲 30 秒。過了這段時間後停止應用程式,然後再執行它一次就可以了。請不要在應用程式啟動之間關閉模擬器。

2. 使用以下程式碼片段為其他功能建立一個新 region:

(Code Snippet – Using Push Notifications – MainPage.xaml.cs – DoConnect function)

C#
  1. #region Misc logic
  2. private void DoConnect()
  3. {
  4. //TODO - place connection logic here
  5. }
  6. #endregion

3. 使用以下程式碼片段在 DoConnect 函數內建立 Try / Catch 塊:

(Code Snippet – Using Push Notifications – MainPage.xaml.cs – DoConnect try-catch block in function body)

C#
  1. try
  2. {
  3. }
  4. catch (Exception ex)
  5. {
  6. Dispatcher.BeginInvoke(() => UpdateStatus("Channel error: " + ex.Message));
  7. }

4. 下一步,初始化一個通道變數,訂閱通道事件並嘗試打開通道。使用以下程式碼片段建立 try 塊主體:

(Code Snippet – Using Push Notifications – MainPage.xaml.cs – DoConnect try block body)

C#
  1. //First, try to pick up existing channel
  2. httpChannel = HttpNotificationChannel.Find(channelName);
  3. if (null != httpChannel)
  4. {
  5. Trace("Channel Exists - no need to create a new one");
  6. SubscribeToChannelEvents();
  7. Trace("Register the URI with 3rd party web service");
  8. SubscribeToService();
  9. //TODO: Place Notification
  10. Dispatcher.BeginInvoke(() => UpdateStatus("Channel recovered"));
  11. }
  12. else
  13. {
  14. Trace("Trying to create a new channel...");
  15. //Create the channel
  16. httpChannel = new HttpNotificationChannel(channelName, "HOLWeatherService");
  17. Trace("New Push Notification channel created successfully");
  18. SubscribeToChannelEvents();
  19. Trace("Trying to open the channel");
  20. httpChannel.Open();
  21. Dispatcher.BeginInvoke(() => UpdateStatus("Channel open requested"));
  22. }

5. 建立一個輔助函數,用來訂閱通道事件:

(Code Snippet – Using Push Notifications – MainPage.xaml.cs – SubscribeToChannelEvents function)

C#
  1. #region Subscriptions
  2. private void SubscribeToChannelEvents()
  3. {
  4. //Register to UriUpdated event - occurs when channel successfully opens
  5. httpChannel.ChannelUriUpdated += new EventHandler<NotificationChannelUriEventArgs>(httpChannel_ChannelUriUpdated);
  6. //Subscribed to Raw Notification
  7. httpChannel.HttpNotificationReceived += new EventHandler<HttpNotificationEventArgs>(httpChannel_HttpNotificationReceived);
  8. //general error handling for push channel
  9. httpChannel.ErrorOccurred += new EventHandler<NotificationChannelErrorEventArgs>(httpChannel_ExceptionOccurred);
  10. }
  11. #endregion

6. 添加一個輔助函數,在Windows Phone 7 用戶端訂閱Push Notification Service 消息。這個訂閱要透過向前面任務中建立的服務註冊並接收通道 URI 完成 。在我們這個例子中,此函數使用 WebClient 和寫死於程式碼內的 RESTful WCF 服務 URL 來註冊Client’s URI (接收自推送伺服器)。在前面插入的 Subscriptions 區域添加以下程式碼片段:

(Code Snippet – Using Push Notifications – MainPage.xaml.cs – SubscribeToService function)

C#
  1. private void SubscribeToService()
  2. {
  3. //Hardcode for solution - need to be updated in case the REST WCF service address change
  4. string baseUri = "http://localhost:8000/RegirstatorService/Register?uri={0}";
  5. string theUri = String.Format(baseUri, httpChannel.ChannelUri.ToString());
  6. WebClient client = new WebClient();
  7. client.DownloadStringCompleted += (s, e) =>
  8. {
  9. if (null == e.Error)
  10. Dispatcher.BeginInvoke(() => UpdateStatus("Registration succeeded"));
  11. else
  12. Dispatcher.BeginInvoke(() => UpdateStatus("Registration failed: " + e.Error.Message));
  13. };
  14. client.DownloadStringAsync(new Uri(theUri));
  15. }

7. 建立事件處理函數處理 ChannelUriUpdate 事件:

(Code Snippet – Using Push Notifications – MainPage.xaml.cs – httpChannel_ChannelUriUpdated function)

C#
  1. #region Channel event handlers
  2. void httpChannel_ChannelUriUpdated(object sender, NotificationChannelUriEventArgs e)
  3. {
  4. Trace("Channel opened. Got Uri:\n" + httpChannel.ChannelUri.ToString());
  5. Trace("Subscribing to channel events");
  6. SubscribeToService();
  7. Dispatcher.BeginInvoke(() => UpdateStatus("Channel created successfully"));
  8. }
  9. #endregion

8. 在 region 中添加 ExceptionOccured 事件處理函數:

(Code Snippet – Using Push Notifications – MainPage.xaml.cs – httpChannel_ExceptionOccured function)

C#
  1. void httpChannel_ExceptionOccurred(object sender, NotificationChannelErrorEventArgs e)
  2. {
  3. Dispatcher.BeginInvoke(() => UpdateStatus(e.ErrorType + " occurred: " + e.Message));
  4. }

9. 最後在同一 region 中添加 HttpNotificationReceived 事件處理函數:

(Code Snippet – Using Push Notifications – MainPage.xaml.cs – httpChannel_HttpNotificationReceived function)

C#
  1. void httpChannel_HttpNotificationReceived(object sender, HttpNotificationEventArgs e)
  2. {
  3. Trace("===============================================");
  4. Trace("RAW notification arrived:");
  5. //TODO - add parsing and UI updating logic here
  6. Trace("===============================================");
  7. }

這個函數解析來自 Push Notification Service 的 XML 負載並負責更新用戶端應用程式的 UI。下一個任務中你會建立它的邏輯。

10. 在類別的建構函數中加入一個對 DoConnect 的叫用。最後的 constructor 應該看上去和下面的程式碼片段相似:

C#
  1. public MainPage()
  2. {
  3. InitializeComponent();
  4. DoConnect();
  5. }

11. 編譯應用程式,如果有編譯錯誤,請修改它們。

12. 為 solution 定義多個啟動專案,這樣做的目的是為了同時執行 WPF Push Notification Client 和 Windows Phone 7 Push 用戶端。為此,在 Solution Explorer 中右鍵點擊 solution 名稱並在快顯功能表中選擇Properties


圖 21
打開 Solution 屬性

13. 從Common Properties 中選擇 Startup Projects 頁 (一般會自動選擇),選擇Multiple startup projects,並在設置 PushNotificationsWeather 專案的 ActionStart


圖 22
選擇多個啟動專案

14. 按 F5 編譯並執行應用程式。

15. 所有專案啟動之後你的螢幕應該如下圖所示:


圖 23
執行多個專案

16. 確保 Windows Phone 7 用戶端應用程式成功註冊了Registration Service:


圖 24
檢查用戶端應用程式註冊

17. 在 httpChannel_HttpNotificationReceived 函數中放置一個中斷點。


圖 25
通知事件處理函數中的中斷點

18. 修改 WPF Push Notification Client 的一些參數並點擊 Send Http 按鈕。當中斷點命中時,程式碼執行會暫停,檢查收到的資訊。


圖 26
通知到達時 命中中斷點


圖 27
Windows Phone 7 用戶端應用程式接收的通知封包

19. 按 F5 繼續執行程式。觀察 WPF Push Notification Client 新的日誌。


圖 28
更新日誌後的 WPF Push Notification Client

20. 停止除錯並返回 Visual Studio (不要關閉 Windows Phone 7模擬器!). 本任務到此結束。


任務 4 – 接收並處理來自 Push Notification Service 的事件

1. 本任務過程中你會建立一個函數來解析封包中包含的 XML 並更新 Windows Phone 7 應用程式的 UI。添加以下函數到 MainPage.xaml.cs 類別的Misc logic 區域:

(Code Snippet – Using Push Notifications – MainPage.xaml.cs – ParseRAWPayload function)

C#
  1. private void ParseRAWPayload(Stream e, out string weather, out string location, out string temperature)
  2. {
  3. XDocument document;
  4. using (var reader = new StreamReader(e))
  5. {
  6. string payload = reader.ReadToEnd().Replace('\0',
  7. ' ');
  8. document = XDocument.Parse(payload);
  9. }
  10. location = (from c in document.Descendants("WeatherUpdate")
  11. select c.Element("Location").Value).FirstOrDefault();
  12. Trace("Got location: " + location);
  13. temperature = (from c in document.Descendants("WeatherUpdate")
  14. select c.Element("Temperature").Value).FirstOrDefault();
  15. Trace("Got temperature: " + temperature);
  16. weather = (from c in document.Descendants("WeatherUpdate")
  17. select c.Element("WeatherType").Value).FirstOrDefault();
  18. }

2. 為了圖形化地顯示天氣你需要添加一些天氣情況的圖示 (本實驗中作為 assets 提供)。在 PushNotifications 專案中建立一個名為Images 的專案資料夾。

3. 從 Assets 資料夾中添加所有PNG 圖片 (除CloudBackgroundMobile.jpg 之外的所有圖片) (位於Source \ Assets 資料夾):


圖 29
從 Assets 資料夾中添加所有圖片


圖 30
從 Assets 資料夾中添加現有圖片

4. 把這些圖片的編譯動作標誌為 Content,為此打開圖像的 Properties 並在Build Action 中選擇 Content


圖 31
將圖像標誌為 Content 資源

5. 回想一下上個任務中建立的最後一個函數 httpChannel_HttpNotificationReceived (在MainPage.xaml.cs),這個函數會叫用前面建立的解析功能並更新 UI。在 “//TODO - add parsing and UI updating logic here” 注釋後面添加以下藍色反白程式碼片段:

(Code Snippet – Using Push Notifications – MainPage.xaml.cs – httpChannel_HttpNotificationReceived function body)

C#
  1. void httpChannel_HttpNotificationReceived(object sender, HttpNotificationEventArgs e)
  2. {
  3. Trace("===============================================");
  4. Trace("RAW notification arrived:");
  5. string weather, location, temperature;
  6. ParseRAWPayload(e.Notification.Body, out weather, out location, out temperature);
  7. Dispatcher.BeginInvoke(() => this.textBlockListTitle.Text = location);
  8. Dispatcher.BeginInvoke(() => this.txtTemperature.Text = temperature);
  9. Dispatcher.BeginInvoke(() => this.imgWeatherConditions.Source = new BitmapImage(new Uri(@"Images/" + weather + ".png", UriKind.Relative)));
  10. Trace(string.Format("Got weather: {0} with {1}F at location {2}", weather, temperature, location));
  11. Trace("===============================================");
  12. }

6. 編譯並執行應用程式。當兩個專案都啟動之後,修改 WPF Push Notification Client 中的一些值然後點擊 Send Http 按鈕。觀察一下通知到達 Windows Phone 7 用戶端應用和 UI 變化。觀察 Visual Studio 2010Output 視窗中的跟蹤資訊 Visual Studio 2010 Output (如果 Output 視窗沒有顯示,點擊Debug | Windows | Output 功能表項目或者按 Ctrl+W,O)。


圖 32
Http Notification 到達並被解析


圖 33
Output 視窗中的追蹤資訊

7. 發送不同的通知並觀察 UI 變化。


圖 34
Http Notification 到達並被解析。跟蹤日誌更新


圖 35
Http Notification 到達並被解析。跟蹤日誌更新

8. 在 Visual Studio 中,按 SHIFT+F5 停止除錯並返回編輯模式。

9. 前面提醒過,應用程式應該嘗試恢復已經存在的通道。為此,通道成功打開後首先要被保存,然後應用程式載入時可以按名稱查詢。在 MainPage.xaml.cs 中加入以下程式碼-它包含一個函數,可以利用 Windows Phone 7 應用程式的Isolated Storage (隔離儲存區) 保存並載入通道資訊 (瞭解更多 Silverlight 應用程式 Isolated Storage 的資訊,參考以下文章:http://www.silverlight.net/learn/quickstarts/isolatedstorage/):

(Code Snippet – Using Push Notifications – MainPage.xaml.cs – Saving and Loading Channel information functionality)

C#
  1. #region Loading/Saving Channel Info
  2. private bool TryFindChannel()
  3. {
  4. bool bRes = false;
  5. Trace("Getting IsolatedStorage for current Application");
  6. using (IsolatedStorageFile isf = IsolatedStorageFile.GetUserStoreForApplication())
  7. {
  8. Trace("Checking channel data");
  9. if (isf.FileExists(fileName))
  10. {
  11. Trace("Channel data exists! Loading...");
  12. using (IsolatedStorageFileStream isfs = new IsolatedStorageFileStream(fileName, FileMode.Open, isf))
  13. {
  14. using (StreamReader sr = new StreamReader(isfs))
  15. {
  16. string uri = sr.ReadLine();
  17. Trace("Finding channel");
  18. httpChannel = HttpNotificationChannel.Find(channelName);
  19. if (null != httpChannel)
  20. {
  21. if (httpChannel.ChannelUri.ToString() == uri)
  22. {
  23. Dispatcher.BeginInvoke(() => UpdateStatus("Channel retrieved"));
  24. SubscribeToChannelEvents();
  25. SubscribeToService();
  26. bRes = true;
  27. }
  28. sr.Close();
  29. }
  30. }
  31. }
  32. }
  33. else
  34. Trace("Channel data not found");
  35. }
  36. return bRes;
  37. }
  38. private void SaveChannelInfo()
  39. {
  40. Trace("Getting IsolatedStorage for current Application");
  41. using (IsolatedStorageFile isf = IsolatedStorageFile.GetUserStoreForApplication())
  42. {
  43. Trace("Creating data file");
  44. using (IsolatedStorageFileStream isfs = new IsolatedStorageFileStream(fileName, FileMode.Create, isf))
  45. {
  46. using (StreamWriter sw = new StreamWriter(isfs))
  47. {
  48. Trace("Saving channel data...");
  49. sw.WriteLine(httpChannel.ChannelUri.ToString());
  50. sw.Close();
  51. Trace("Saving done");
  52. }
  53. }
  54. }
  55. }
  56. #endregion

10. 使用以下程式碼片段修改類別的建構函數來叫用載入功能:

C#
  1. public MainPage()
  2. {
  3. InitializeComponent();
  4. if (!TryFindChannel())
  5. DoConnect();
  6. }

11. 在 httpChannel_ChannelUriUpdate 函數中添加保存通道的功能。在 “Channel opened … ” 後面添加以下程式碼片段:

(Code Snippet – Using Push Notifications – MainPage.xaml.cs – SaveChannelInfo function call)

C#
  1. void httpChannel_ChannelUriUpdated(object sender, NotificationChannelUriEventArgs e)
  2. {
  3. Trace("Channel opened. Got Uri:\n" + httpChannel.ChannelUri.ToString());
  4. Dispatcher.BeginInvoke(() => SaveChannelInfo());
  5. Trace("Subscribing to channel events");
  6. SubscribeToService();
  7. Dispatcher.BeginInvoke(() => UpdateStatus("Channel created successfully"));
  8. }

12. 編譯並執行程式兩次。使用除錯來檢查第一次應用程式打開並保存了通道資訊,第二次透過名稱找到了通道 。

本練習到此結束。本練習過程中你學習了如何與Microsoft Push Notification Services 進行通信,如何準備並發送消息到Windows Phone 7 用戶端應用程式,以及在 Windows Phone 7 中如何接收這些消息 。

注意:本練習的完整解決方案位於以下位置:Source \ Ex1-RawNotifications \ End


練習 2:用於提醒的 Toast 和 Tile Notifications 介紹

這部分你將學習另外兩種類型的通知 - Toast 和Tile 通知。本實驗包括了向 MSPNS 發佈資料的後台伺服器,和如何在你的 Windows Phone 7 應用程式中註冊並處理這些事件 。

Tiles 和 toast 通知是兩種機制,它們允許雲端服務在應用程式自己的 UI 之外向使用者投遞相關動態回饋。另外,雲端服務可以發送原始的通知請求。根據發送的通知類型,通知會被路由到應用程式或者是作業系統殼層 (shell)。

Tile Notifications

Tile 是應用程式或者其內容在手機快速啟動區的一個可視的,動態的呈現。例如天氣應用程式可能選擇使用 tile 顯示使用者本地時間和氣候情況。因為雲端服務可能在任何時間變動 tile 的外觀,這種機制可以用來將資訊持續不斷地傳達給用戶。每個手機應用程式都關聯到一個單獨的 tile,但是用戶可以控制哪些 tile 附加到Quick Launch 區。

雲端服務能夠控制 tile 的背景圖片,計數器 (或者徽章) 和標題屬性。這些屬性使用 Windows Phone Developer Tools 來配置。Tile 的動畫和聲音屬性由平臺設置而不是由應用程式來控制。例如,如果平台設置為用動畫和嘟嘟聲來回應任何 tile 更新,這對於任何tile 都是有效的。

Tile 的背景圖片可以參考一個本地資源,是應用程式部署的一部分,或雲端資源。透過引用在雲端中的資源,應用程式得以動態更新 tile 的背景圖像。這使得需要提前進行背景影像處理,然後才顯示出來的場景也能夠得以應用。在大多數情況下,應用套裝程式應包含所有需要的 tile 背景圖片,因此這是提高性能和電池壽命最好的解決方案。

Toast Notifications

雲端服務能夠產生一種特殊的 Push Notification,稱為 toast 通知,它通常在使用者當前螢幕之上疊加顯示。例如,天氣應用程式可能希望將惡劣天氣提醒作為 toast 通知顯示。如果使用者決定點擊 toast 通知,應用程式就會啟動並執行其他動作。

雲端服務能夠控制toast 通知的標題和副標題。toast 通知也會顯示包含在佈署包裡的應用程式圖示。

最佳實踐

  • Toast 通知應該是與個人相關並且時間上緊急。
  • Toast 通知應該主要集中在點對點通信。

任務 1 – 實現服務端程式碼來發送 Tiles & Toasts

1. 從開始|所有程式| Microsoft Visual Studio 2010 Express | Microsoft Visual Studio 2010 Express for Windows Phone 中打開 Microsoft Visual Studio 2010 Express for Windows Phone。

Visual Studio 2010:從開始|所有程式| Microsoft Visual Studio 2010 中打開 Visual Studio 2010。

重要提醒:為了執行自我裝載 WCF 服務,Visual Studio 2010 Express for Windows Phone 或者Microsoft Visual Studio 2010 必須在管理模式下打開。 關於建立和提供自承 WCF 服務請參考MSDN 文章 (http://msdn.microsoft.com/en-us/library/ms731758.aspx)。在管理模式下打開Visual Studio 2010 Express for Windows Phone or Visual Studio 2010 in Administrative Mode,找到Microsoft Visual Studio 2010 Express for Windows Phone 快捷方式Start | All Programs | Microsoft Visual Studio 2010 Express 或者 Microsoft Visual Studio 2010 快捷方式Start | All Programs | Microsoft Visual Studio 2010,在圖示上按一下右鍵,然後從快顯功能表中選擇 “Run as administrator”。可能會彈出 UAC 提醒 ,點擊“Yes” 來允許 Visual Studio 2010 Express for Windows Phone 或者Visual Studio 2010 使用管理員許可權。

2. 找到位於本實驗 Source \ Ex2-TileToastNotifications \ Begin 資料夾中的 starter 專案,選擇Begin.sln,然後點擊 Open。你也可以選擇繼續使用前面練習中的 solution。

3. 打開 NotificationSenderUtility.cs 檔,可以在 NotificationSenderUtility 專案下面找到它。

4. Toast 和Tile 通知是 Windows Phone 7 平台系統定義的通知。和 RAW 通知不同,所有的應用程式都可以建立他們自己的負載格式並相應地解析它們。下面幾個步驟中,你會建立一些通用功能來建立Tile & Toast 消息負載-這個負載可以與任何Windows Phone 7 應用程式合作。另外你會暴露公用的功能來發送這些消息並把 WPF Push Notification Client 的按鈕事件連接到它。找到SendXXXNotification functionality 區。添加以下公用函數到這個區 (隨後 WPF 應用程式會用到他們):

(Code Snippet – Using Push Notifications – NotificationSenderUtility – SendXXXNotification functions)

C#
  1. public void SendToastNotification(List<Uri> Uris, string message1, string message2, SendNotificationToMPNSCompleted callback)
  2. {
  3. byte[] payload = prepareToastPayload(message1, message2);
  4. foreach (var uri in Uris)
  5. SendNotificationByType(uri, payload, NotificationType.Toast, callback);
  6. }
  7. public void SendTileNotification(List<Uri> Uris, string TokenID, string BackgroundImageUri, int Count, string Title, SendNotificationToMPNSCompleted callback)
  8. {
  9. byte[] payload = prepareTilePayload(TokenID, BackgroundImageUri, Count, Title);
  10. foreach (var uri in Uris)
  11. SendNotificationByType(uri, payload, NotificationType.Token, callback);
  12. }

5. 下一步你會建立一個新的區,包含兩個函數-第一個準備 Toast 負載,第二個準備 Tile 負載。

Tile 通知消息應該使用下面的格式。注意 <background image path>,<count><title> 元素是 string 格式的。

XML
Content-Type: text/xmlX-WindowsPhone-Target: token<?xml version="1.0" encoding="utf-8"?><wp:Notification xmlns:wp="WPNotification">   <wp:Tile>      <wp:BackgroundImage><background image path></wp:BackgroundImage>      <wp:Count><count></wp:Count>      <wp:Title><title></wp:Title>   </wp:Tile> </wp:Notification>

Toast 通知消息則應該使用如下格式,注意 <Text1><Text2> 是string 格式。

XML
Content-Type: text/xmlX-WindowsPhone-Target: toast<?xml version="1.0" encoding="utf-8"?><wp:Notification xmlns:wp="WPNotification">   <wp:Toast>      <wp:Text1><string></wp:Text1>      <wp:Text2><string></wp:Text2>   </wp:Toast></wp:Notification>

6. 使用以下程式碼片段建立 Prepare Payload 一組函數:

(Code Snippet – Using Push Notifications – NotificationSenderUtility – Prepare Payload functions)

C#
  1. #region Prepare Payloads
  2. private static byte[] prepareToastPayload(string text1, string text2)
  3. {
  4. MemoryStream stream = new MemoryStream();
  5. XmlWriterSettings settings = new XmlWriterSettings() { Indent = true, Encoding = Encoding.UTF8 };
  6. XmlWriter writer = XmlWriter.Create(stream, settings);
  7. writer.WriteStartDocument();
  8. writer.WriteStartElement("wp", "Notification", "WPNotification");
  9. writer.WriteStartElement("wp", "Toast", "WPNotification");
  10. writer.WriteStartElement("wp", "Text1", "WPNotification");
  11. writer.WriteValue(text1);
  12. writer.WriteEndElement();
  13. writer.WriteStartElement("wp", "Text2", "WPNotification");
  14. writer.WriteValue(text2);
  15. writer.WriteEndElement();
  16. writer.WriteEndElement();
  17. writer.WriteEndDocument();
  18. writer.Close();
  19. byte[] payload = stream.ToArray();
  20. return payload;
  21. }
  22. private static byte[] prepareTilePayload(string tokenId, string backgroundImageUri, int count, string title)
  23. {
  24. MemoryStream stream = new MemoryStream();
  25. XmlWriterSettings settings = new XmlWriterSettings() { Indent = true, Encoding = Encoding.UTF8 };
  26. XmlWriter writer = XmlWriter.Create(stream, settings);
  27. writer.WriteStartDocument();
  28. writer.WriteStartElement("wp", "Notification", "WPNotification");
  29. writer.WriteStartElement("wp", "Tile", "WPNotification");
  30. writer.WriteStartElement("wp", "BackgroundImage", "WPNotification");
  31. writer.WriteValue(backgroundImageUri);
  32. writer.WriteEndElement();
  33. writer.WriteStartElement("wp", "Count", "WPNotification");
  34. writer.WriteValue(count.ToString());
  35. writer.WriteEndElement();
  36. writer.WriteStartElement("wp", "Title", "WPNotification");
  37. writer.WriteValue(title);
  38. writer.WriteEndElement();
  39. writer.WriteEndElement();
  40. writer.Close();
  41. byte[] payload = stream.ToArray();
  42. return payload;
  43. }
  44. #endregion

7. 在 Weather 專案中打開 MainWindow.xaml.cs

8. 找到 sendToast 函數,這個函數應該從 Push Notification Client UI 獲取 Toast 消息並發送到所有的訂閱者。在此函數中添加如下程式碼片段:

(Code Snippet – Using Push Notifications – MainWindow.xaml.cs –sendToast function body)

C#
  1. private void sendToast()
  2. {
  3. string msg = txtToastMessage.Text;
  4. txtToastMessage.Text = "";
  5. List<Uri> subscribers = RegistrationService.GetSubscribers();
  6. ThreadPool.QueueUserWorkItem((unused) => notifier.SendToastNotification(subscribers,
  7. "WEATHER ALERT", msg, OnMessageSent));
  8. }

9. 找到 sendTile 函數。這個函數從 Push Notification Client UI 獲取參數並發送到所有訂閱者。在此函數內中添加如下程式碼片段:

(Code Snippet – Using Push Notifications – MainWindow.xaml.cs –sendTile function body)

C#
  1. private void sendTile()
  2. {
  3. string weatherType = cmbWeather.SelectedValue as string;
  4. int temperature = (int)sld.Value;
  5. string location = cmbLocation.SelectedValue as string;
  6. List<Uri> subscribers = RegistrationService.GetSubscribers();
  7. ThreadPool.QueueUserWorkItem((unused) => notifier.SendTileNotification(subscribers, "PushNotificationsToken", "/Images/" + weatherType + ".png", temperature, location, OnMessageSent));
  8. }

10. 編譯並執行應用程式。檢查消息被發送到 Push Notification Service。


圖 36
WPF Push Notifications Client 日誌

注意:如果你從 Begin solution 開始本練習,而不是在前一練習的基礎上繼續工作,執行應用之前你應該為 solution 設置多個啟動項目,才能同時執行 WPF Push Notification Client 和Windows Phone 7 Push Client。為此, 在 Solution Explorer 中右鍵點擊 solution 名稱並在快顯功能表中選擇Properties。從Common Properties 中選擇 Startup Projects 頁(一般會自動選擇),選擇Multiple startup projects,並設置 PushNotificationsWeather 專案的 ActionStart

本任務到此結束


任務 2 – 在手機端處理 Tile & Toast Notifications

1. 打開 PushNotifications專案中的MainPage.xaml.cs

2. 接下來幾步中你會訂閱Tile 和 Toast 通知事件並處理這些事件。找到 Subscriptions 區並添加以下程式碼片段:

(Code Snippet – Using Push Notifications – MainPage.xaml.cs – SubscribeToNotifications function)

C#
  1. private void SubscribeToNotifications()
  2. {
  3. //////////////////////////////////////////
  4. // Bind to Toast Notification
  5. //////////////////////////////////////////
  6. try
  7. {
  8. if (httpChannel.IsShellToastBound == true)
  9. {
  10. Trace("Already bounded (register) to to Toast notification");
  11. }
  12. else
  13. {
  14. Trace("Registering to Toast Notifications");
  15. httpChannel.BindToShellToast();
  16. }
  17. }
  18. catch (Exception ex)
  19. {
  20. // handle error here
  21. }
  22. //////////////////////////////////////////
  23. // Bind to Tile Notification
  24. //////////////////////////////////////////
  25. try
  26. {
  27. if (httpChannel.IsShellTileBound == true)
  28. {
  29. Trace("Already bounded (register) to Tile Notifications");
  30. }
  31. else
  32. {
  33. Trace("Registering to Tile Notifications");
  34. // you can register the phone application to receive tile images from remote servers [this is optional]
  35. Collection<Uri> uris = new Collection<Uri>();
  36. uris.Add(new Uri("http://jquery.andreaseberhard.de/pngFix/pngtest.png"));
  37. httpChannel.BindToShellTile(uris);
  38. }
  39. }
  40. catch (Exception ex)
  41. {
  42. //handle error here
  43. }
  44. }

3. 現在找到 SubscribeToChannelEvents 函數並添加以下藍色反白程式碼片段到函數內:

(Code Snippet – Using Push Notifications – MainPage.xaml.cs – SubscribeToChannelEvents function body)

C#
  1. private void SubscribeToChannelEvents()
  2. {
  3. //Register to UriUpdated event - occurs when channel successfully opens
  4. httpChannel.ChannelUriUpdated += new EventHandler<NotificationChannelUriEventArgs>(httpChannel_ChannelUriUpdated);
  5. //Subscribed to Raw Notification
  6. httpChannel.HttpNotificationReceived += new EventHandler<HttpNotificationEventArgs>(httpChannel_HttpNotificationReceived);
  7. //general error handling for push channel
  8. httpChannel.ErrorOccurred += new EventHandler<NotificationChannelErrorEventArgs>(httpChannel_ExceptionOccurred);
  9. //subscrive to toast notification when running app
  10. httpChannel.ShellToastNotificationReceived += new EventHandler<NotificationEventArgs>(httpChannel_ShellToastNotificationReceived);
  11. }

4. 找到 Channel event handlers 區並添加以下事件處理函數:

(Code Snippet – Using Push Notifications – MainPage.xaml.cs – Tile and Toast notifications event handler function)

C#
  1. void httpChannel_ShellToastNotificationReceived(object sender, NotificationEventArgs e)
  2. {
  3. Trace("===============================================");
  4. Trace("Toast/Tile notification arrived:");
  5. foreach (var key in e.Collection.Keys)
  6. {
  7. string msg = e.Collection[key];
  8. Trace(msg);
  9. Dispatcher.BeginInvoke(() => UpdateStatus("Toast/Tile message: " + msg));
  10. }
  11. Trace("===============================================");
  12. }

注意:在我們這個簡單的案例中,函式只用來追蹤訊息封包,但是在真實世界的應用程式,你可以用它來執行任何的商業邏輯。

5. 最後添加以下程式碼片段到幾個位置:

(Code Snippet – Using Push Notifications – MainPage.xaml.cs – Subscribe toNotifications call)

C#
  1. SubscribeToNotifications();

添加此程式碼的位置:

a. 在 DoConnect 函數內的 try 塊中,位於 “SubscribeToService();” 和 “Dispatcher.BeginInvoke();” 之間:

C#
  1. if (null != httpChannel)
  2. {
  3. Trace("Channel Exists - no need to create a new one");
  4. SubscribeToChannelEvents();
  5. Trace("Register the URI with 3rd party web service");
  6. SubscribeToService();
  7. Trace("Subscribe to the channel to Tile and Toast notifications");
  8. SubscribeToNotifications();
  9. Dispatcher.BeginInvoke(() => UpdateStatus("Channel recovered"));
  10. }

b. 在 httpChannel_ChannelUriUpdated 函數內,“SubscribeToService();” 與 “Dispatcher.BeginInvoke(…);” 之間:

C#
  1. void httpChannel_ChannelUriUpdated(object sender, NotificationChannelUriEventArgs e)
  2. {
  3. Trace("Channel opened. Got Uri:\n" + httpChannel.ChannelUri.ToString());
  4. Dispatcher.BeginInvoke(() => SaveChannelInfo());
  5. Trace("Subscribing to channel events");
  6. SubscribeToService();
  7. SubscribeToNotifications();
  8. Dispatcher.BeginInvoke(() => UpdateStatus("Channel created successfully"));
  9. }

6. 按 F5 編譯並執行應用程式。在手機模擬器上,點擊Back 按鈕 ( ) 退出 Push Notification 應用程式並來到 Quick Launch 區域。

7. 在 Quick Launch 區域,點擊 Right Arrow 按鈕打開“All Applications” 螢幕。


圖 37
打開應用程式螢幕

8. 找到 PushNotifications,按下並保持直到快顯功能表彈出,點擊 Pin to Start 附加到快速啟動區域。


圖 38
附加 tile 到開始螢幕

9. 模擬器會自動回到Quick Launch 區域,這裡你可以看到 PushNotifications tile 已被附加到此區域。


圖 39
PushNotifications Tile

10. 在 WPF Push Notification Client 上,修改通知參數然後點擊 Send Tile 按鈕。觀察模擬器上的Tile 變化。如果你點擊Toast消息你會被帶回到這個應用程式


圖 40
Tile Notification 到達模擬器

11. 在 WPF Push Notification Client 上輸入一些消息然後點擊 Send Toast 按鈕。 觀察 Toast 消息如何到達手機。點擊這個Toast 消息會切換到該應用程式。


圖 41
手機模擬器上的 Toast 消息


圖 42
Windows Phone 應用程式上的 Toast 消息

本任務到此為止。


任務 3 – 在手機上處理預定的 Tile Notifications

注意:您也可以使用 Microsoft.Phone.Shell.ShellTileSchedule 類提供的 shell tile 預定來更新應用程式的 tile。這種特殊的類別允許應用程式定期對其 tile 背景圖像更新,透過設置背景圖像的完整 URI 和相關的重複時間間隔屬性即可完成。當手機啟動 tile 預定實例,它會自動發送一個 tile 通知到應用程式,然後根據 URI 提取圖像,並更新 tile。

接下來的幾步你會建立一個 ShellTileSchedule 類的實例,並執行應用程式的 tile 更新。

1. 打開 PushNotifications 專案中的 App.xaml.cs。

2. 找到應用程式的建構式 - App 方法,然後緊隨其後插入以下程式碼片段:

(Code Snippet – Using Push Notifications – MainPage.xaml.cs – CreateShellTileScheduleFunction)

C#
  1. // To store the instance for the application lifetime
  2. private ShellTileSchedule shellTileSchedule;
  3. /// <summary>
  4. /// Create the application shell tile schedule instance
  5. /// </summary>
  6. private void CreateShellTileSchedule()
  7. {
  8. shellTileSchedule = new ShellTileSchedule();
  9. shellTileSchedule.Recurrence = UpdateRecurrence.Interval;
  10. shellTileSchedule.Interval = UpdateInterval.EveryHour;
  11. shellTileSchedule.StartTime = DateTime.Now;
  12. shellTileSchedule.RemoteImageUri = new Uri(@"http://cdn3.afterdawn.fi/news/small/windows-phone-7-series.png");
  13. shellTileSchedule.Start();
  14. }

注意:你只能提供一個 RemoteImageUri。因此你必須提供一個連線並且有效的 URI 來提供圖片下載並顯示,你不能引用本地應用程式的 URI。圖像大小不能超過 80KB,並且下載時間不能超過 60 秒。

3. 現在,找到 App() 函數並從這裡叫用 CreateShellTileSchedule 函數 (參照反白的程式碼行):

(Code Snippet – Using Push Notifications – MainPage.xaml.cs – CreateShellTileScheduleFunction)

C#
  1. // Constructor
  2. public App()
  3. {
  4. // Global handler for uncaught exceptions.
  5. // Note that exceptions thrown by ApplicationBarItem.Click will not get caught here.
  6. UnhandledException += Application_UnhandledException;
  7. // Standard Silverlight initialization
  8. InitializeComponent();
  9. // Phone-specific initialization
  10. InitializePhoneApplication();
  11. // Create the shell tile schedule instance
  12. CreateShellTileSchedule();
  13. }

你剛剛提供了一個物件,它會根據 schedule 屬性更新應用程式的 tile。

你需要設置以下屬性:

  • Recurrent,設置其值為 Interval,這可以使 tile 定期更新。這個屬性的另一個可設置值為OneTime,tile 的更新只發生一次。
  • Interval,設置為 EveryHour,可以使 tile 每 60 分鐘更新 一次。需要提一下的是一小時是可以設置的最小更新間隔,這個規定是為了節約手機資源。
  • RemoteImageUri,設置為你可以獲取圖像的位址。

最後,你需要叫用 Start 方法啟動更新 tile。請注意,第一次更新可能會延遲一小時,因為這個類別會整個小時地叫用更新。即使您設置開始時間為現在,你也需要等待手機每隔 60 分鐘發生一次的更新。不幸的是,這是一個系統級的限制,所以如果你要除錯的程式碼,你必須等一個小時。

4. 按 F5 編譯並執行應用程式。在手機模擬器上點擊 Back 按鈕 ( ) 退出Push Notification 應用,並回到 Quick Launch 區域.

5. 確認一下你的應用程式 tile 被附加到Quick Launch 區域,否則按照 Task 2 的第 7-9 步附加它。

6. PushNotifications tile 圖像現在會根據 ShellTileSchedule 的定義進行更新。因此當第一個時間間隔過去之後的下一個小時,應用程式的 tile 會顯示來自 RemoteImageUri 屬性中所示位址的圖像,http://cdn3.afterdawn.fi/news/small/windows-phone-7-series.png。


圖 43
Scheduled Tile Image

本練習和本實驗到此結束。

原文地址:http://msdn.microsoft.com/zh-tw/windowsphone/gg471245.aspx;

原创粉丝点击