Hands-On Lab 使用消息推送機制(Push Notifications)

 

 

 

Hands-On Lab

使用消息推送機制(Push Notifications)

 

 

Lab version:    1.0.0

Last updated:          2/29/2012

 


 

內容

概述... 3

課程 1: 介紹 Windows Phone RAW Notifications 消息... 3

任務 1 – 創建天氣服務項目... 3

任務2 – 創建 Windows® Phone 7 客戶端應用程序... 3

任務3 – 創建 Notification Channel 3

任務4 –Push NotificationService的接受和處理事件... 3

課程 2: 介紹 Toast 和 Tile Notifications forAlerts. 3

任務1 – 實現發送 Tiles & Toasts的服務器端代碼... 3

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

任務3 – 在手機端處理 Scheduled Tile Notifications. 3

課程 3: 使用 sub-tiles 和 deep toast notifications. 3

任務 1 – 創建獨立的 MSPN註冊頁面... 3

任務 2 – 更新客戶端主界面... 3

任務 3 – 添加特定的位置頁面,並更新服務器... 3

課程總結... 3

 

 


 

微軟Windows® Phone推送通知服務( Push Notification Service)爲第三方開發者提供了一個彈性、專注、可持續的通道,支持發送消息、從服務器(web services)端更新Windows® Phone應用程序。

在這一部分中,一個移動應用程序需要經常的查看對應的web service,以瞭解是否有未處理的Notification。如果保持有效狀態,就會造成設備的手機模塊始終處於打開狀態,進而影響待機時間。如果使用Push Notifications,Web Service只要在重要的更新時,纔會通知到應用程序。

Figure 1

Push Notifications

 

當Web Service有信息需要發送到應用程序時,它發送一個Push Notification 到微軟的Push Notification Service服務器上,然後會將這個Push Notification轉發給應用程序。依賴於Push Notification的格式和負載量,這個信息會有三種形式展現:作爲原始數據發佈給應用程序,應用程序的Tile將更新,或者彈出一個Toast notification。如果需要,應用程序可以使用自有的協議來與Web Service通訊。

在Push Notification被髮送之後,Push Notification Service服務器會發送一個響應代碼到您的WebService。事實上,Push Notification Service不會提供您的推送通知從您的Web Service到應用程序之間的端到端確認方式。更多信息, 請參考 Push NotificationService Response Codes for Windows® Phone.

本次動手實驗將會覆蓋推送通知,並介紹Silverlight中http服務的用法。在整個實驗中,您將會創建服務器端的邏輯,用來發送消息到Push Notification Service。您也會創建一個簡單的Windows® Phone 7 Mango應用程序,作爲客戶端,用來接收推送通知。客戶端應用程序將會接收天氣更新。服務器端的業務應用程序(一個簡單的WPF應用)將會發送天氣提醒道已經通過Push Notification Services註冊的客戶端應用程序上去。一旦Windows® Phone 7客戶端應用程序接收到提醒,將會顯示接收到的信息。

提示: 服務器端的天氣應用程序使用WindowsPhone.Recipes.Push.Messasges.dll , 封裝了所有發送、從Microsoft PushNotification Services接受響應的業務邏輯和功能。更多信息請參考: http://windowsteamblog.com/windows_phone/b/wpdev/archive/2011/01/14/windows-push-notification-server-side-helper-library.aspx.

 

目標

在本次實驗課程中,您將會:

·                 熟悉Windows® Phone 7應用程序的通訊功能

·                 熟悉Push Notification的概念和在手機上的使用行爲

·                 理解Push Notifications在雲端和手機端如何工作

·                 使用手機Push Notifications服務,創建一個Tokens (tiles),Toasts, 和 raw push notifications的訂閱

·                 使用 Web Client 註冊Push Notifications

·                 使用Networkstatus來顯示當前手機網絡的狀態

·                 創建一個 SL 應用程序,用來註冊push notification services (包括 token和toast)

◦                   運行期控制push 事件events (token, toast, 和 raw)

◦                   在Shell中顯示token 和toast信息

·                 管理應用程序獨立更新的sub-tiles,並鏈接到應用程序的制定位置 (僅在 Windows® Phone 7.1中有效)

 

前提條件

您在開始本次動手實驗前,請先確認達到下列前提條件:

·                 Microsoft Visual Studio 2010Express for Windows® Phone 或者 Microsoft Visual Studio 2010

·                 Windows® Phone Developer Tools

注意: 所有的工具可以從下面的網址下載到:http://go.microsoft.com/?linkid=9772716

 

實驗提綱

這個動手實驗將包括一個完整的實驗,包括下列的任務:

1.      介紹 Windows Phone RAW Notifications 消息

2.      介紹 Toast 和 TileNotifications for Alerts

3.      使用 sub-tiles 和 deep toastnotifications  (僅在Windows® Phone 7.1中有效)

 

 

完成該實驗預計花費: 110 分鐘.

 


 

課程 1: 介紹 Windows Phone RAW Notifications 消息

在這一章節中,我們打開開始解決方案:

·                 實現服務器端的notification,並註冊服務

·                 創建 Windows® Phone 7 客戶端應用程序

·                 創建notification channel,並訂閱channel事件

·                 從Push Notification Services,接收和處理 RAW Push Notification 消息

 

您將會使用MicrosoftVisual Studio 2010 Express for Windows® Phone作爲開發環境,並且將程序部署到Windows®Phone Emulator中進行調試。您將要使用的解決方案基於Silverlight for Windows® Phone Application模板。

提示: 本次動手實驗的步驟是按照MicrosoftVisual Studio 2010 Express for Windows® Phone來說明的, 但是如果我們採用Microsoft Visual Studio2010 with the Windows®Phone Developer Tools,實驗手冊中的知道也同樣有效。

任務 1 –創建天氣服務項目

在這個任務中,您將會使用一個開始的MicrosoftVisual Studio 2010 Express for Windows® Phone 或者 Microsoft Visual Studio2010解決方案。在這個解決方案中包括了一個簡單的WPF客戶端應用程序,我們用來通過MicrosoftPush Notification Service,發送消息給Windows® Phone 7 應用程序。WPF應用程序將會包含一個WCF註冊服務。該服務會在本次任務中被創建,WPF應用程序

1.       打開 Microsoft Visual Studio 2010Express for Windows® Phone 通過開始菜單 | 所有程序 | Microsoft Visual Studio 2010 Express | Microsoft Visual Studio2010 Express for Windows® Phone.

Visual Studio 2010: 打開 Visual Studio 2010 通過 開始菜單 | 所有程序 | Microsoft Visual Studio 2010.

重要提示: 爲了運行Visual Phone 2010Express for Windows® Phone 或者 Microsoft Visual Studio 2010所包含的WCF服務,該解決方案應該被以Administrative模式被打開。如何創建WCF服務,可以參考MSDN文檔: (http://msdn.microsoft.com/en-us/library/ms731758.aspx). 爲了以Administrative模式打開 Visual Studio 2010 Express for Windows® Phone 或者 Visual Studio 2010 。我們可以在Start | All Programs | Microsoft Visual Studio 2010 Express中找到Microsoft Visual Studio 2010 Express for Windows® Phone圖標,右鍵單擊圖標,選擇“Run as administrator”,UAC的提示會彈出,選擇“Yes”提高VisualStudio 2010 Express for Windows® Phone 或 VisualStudio 2010的權限。

2.       在 File 菜單中, 選擇 Open Project.

Visual Studio 2010: 在 File 菜單中,指向 open ,並且然後選擇 Project/Solution.

3.       找到初始工程所在的文件夾 Source\Ex1-RawNotifications\Begin, 選擇 Begin.sln 並點擊 Open.

Figure 2

Openingthe starter project

4.      查看被打開的工程, 這是一個標準的 WPF應用程序:

提示: 該應用程序是基於.NET 4 Framework,並且沒有 .NET 4 FrameworkClient Profile,這是爲了支持self-hosted RESTful WCF services.

a.                WPF 應用程序中包括 MainWindow 界面,PushNotificationsLogViewer用戶控件和 StatusToBrush 值類型轉換.

b.                “Service” 工程目錄包括WCF RESTfull service的接口定義.

c.                 除了標準的 WPF應用程序引用之外,還包括System.ServiceModel和 System.ServiceModel.Web等程序集來支持WCF services.

d.                除此之外,應用程序引用WindowsPhone.Recipes.Push.Messasges程序集, 用來包裝推送通知操作的類庫。該程序集位於Assets\Lib文件夾中。

5.      我們將使用WindowsPhone.Recipes.Push.Messasges 程序集的下列類庫:RawPushNotificationMessage, ToastPushNotificationMessage和 TilePushNotificationMessage來發送raw, toast和tile 消息。所有這些類派生自PushNotificationMessage抽象類,該類實現了SendAsync方法,用來異步發送一個推送通知到指定的URI。派生類將提供包含特定屬性的消息。

a.                下面是 SendAsync方法的描述:

C#

/// <summary>

///Asynchronously send this messasge to the destination address.

/// </summary>

/// <remarks>

/// This method uses the .NETThread Pool. Use this method to

/// send one or few messagesasynchronously. If you have many

/// messages to send, pleaseconsider using the synchronous

/// method with a custom(external) queue-thread solution.

///

/// Note that properties ofthis instance may be changed by

/// different threads whilesending, but once the payload is

/// created, it will not bechanged until the next send.

/// </remarks>

/// <paramname="uri">Destination address uri.</param>

/// <paramname="messageSent">Message sentcallback.</param>

/// <paramname="messageError">Message send errorcallback.

/// </param>

/// <exceptioncref="ArgumentNullException">One of thearguments

/// is null.</exception>

/// <exceptioncref="ArgumentOutOfRangeException">Payloadsize

/// is out of range. Formaximum allowed message size see

/// <seecref="PushNotificationMessage.MaxPayloadSize"/>

/// </exception>       

public voidSendAsync(Uri uri,

Action<MessageSendResult>messageSent = null,

Action<MessageSendResult> messageError= null)

{...}

b.                Callback 方法的參數在同一個類庫中被定義,包括下列定義:

C#

/// <summary>

/// Push notification message sendoperation result.

/// </summary>

public classMessageSendResult

{

 

    /// <summary>

    /// Gets the response time offset.

    /// </summary>

    public DateTimeOffsetTimestamp { get; }

 

    /// <summary>

    /// Gets the associated message.

    /// </summary>

    public PushNotificationMessageAssociatedMessage { get; }

 

    /// <summary>

    /// Gets the channel URI.

    /// </summary>

    public UriChannelUri { get; }

 

    /// <summary>

    /// Gets the web request status.

    /// </summary>

    public HttpStatusCodeStatusCode { get; }

 

    /// <summary>

    /// Gets the push notification status.

    /// </summary>

    public NotificationStatusNotificationStatus { get; }

 

    /// <summary>

    /// Gets the device connection status.

    /// </summary>

    public DeviceConnectionStatusDeviceConnectionStatus { get; }

 

    /// <summary>

    /// Gets the subscription status.

    /// </summary>

    public SubscriptionStatusSubscriptionStatus { get; }

 

}

 

/// <summary>

/// Represents errors that occurduring push notification message send operation.

/// </summary>

public classMessageSendException : Exception
{

    /// <summary>

    /// Gets the message send result.

    /// </summary>

    public MessageSendResultResult { get; }

 

}

 

c.                 RawPushNotificationMessage 有下列的定義:

C#

/// <summary>

/// Represents a raw pushnotification message.

/// </summary>

/// <remarks>

/// If you do not wish to updatethe tile or send a toast

/// notification, you can insteadsend raw information

/// to your application.

/// If your application is notcurrently running,

/// the raw notification isdiscarded on the Microsoft Push

/// Notification Service and is notdelivered to the device.

///

/// This class members are threadsafe.

/// </remarks>

public sealedclass RawPushNotificationMessage:

PushNotificationMessage

{

   #region Properties

 

    /// <summary>

    /// Gets or sets the message raw data bytes.

    /// </summary>

    public byte[] RawData{ get; set; }

 

   #endregion

 

   #region Ctor

    /// <summary>

    /// Initializes a new instance of this type.

    /// </summary>

    /// <paramname="sendPriority">

    /// The send priority of this message in the MPNS.</param>

    public RawPushNotificationMessage(

     MessageSendPriority sendPriority = MessageSendPriority.High)

    { ... }

 

   #endregion

}

 

d.                TilePushNotificationMessage 定義如下:

C#

/// <summary>

/// Represents a tile push notification message.

/// </summary>

/// <remarks>

/// Every phone application has one assigned 'tile' –

/// a visual, dynamic representation of the application

/// or its content.

/// A tile displays in the Start screen

/// if the end user has pinned it.

///

/// This class members are thread safe.

/// </remarks>

public sealed classTilePushNotificationMessage : PushNotificationMessage

{

    #regionProperties

 

    /// <summary>

    /// Gets or sets the phone's local path,

    /// or a remote path for the background image.

    /// </summary>

    /// <remarks>

    /// If the uri references a remote resource,

    /// the maximum allowed size of the tile

    /// image is 80 KB, with a maximum

    /// download time of 15 seconds.

    /// </remarks>

    public Uri BackgroundImageUri { get;set; }

 

    /// <summary>

    /// Gets or sets an integer value from 1 to 99

    /// to be displayed in the tile, or 0 to clear count.

    /// </summary>

    public int Count { get; set; }

 

    /// <summary>

    /// Gets or sets the title text should be displayed in thetile.

    /// Null keeps the existing title.

    /// </summary>

    /// <remarks>

    /// The Title must fit a single line of text and should not

    /// be wider than the actual tile.

    /// Imperatively a good number of letters

    /// would be 18-20 characters long.

    /// </remarks>

    public string Title { get; set; }

 

    /// <summary>

   /// Gets or sets the URI and query string of thesecondary

    /// tile as specifiedby the Windows Phone client.

   /// <example>/GameList.xaml</example>

   /// <example>/GameList.xaml?sort=byName</example>

   /// </summary>

   /// <remarks>

   /// Null orempty string refers to the main tile.

   /// Onlysupported when communicating with Windows Phone 7.1

   /// applications.

   /// </remarks>

   public string SecondaryTile { get; set; }

 

    #endregion

 

    #regionCtor

    /// <summary>

    /// Initializes a new instance of this type.

    /// </summary>

    /// <param name="sendPriority">

    /// The send priority of this message in the MPNS.</param>

    publicTilePushNotificationMessage(

        MessageSendPrioritysendPriority =

        MessageSendPriority.Normal): base(sendPriority)

    {...}

    #endregion

}

e.                ToastPushNotificationMessage定義如下:

C#

/// <summary>

/// Represents a toast pushnotification message.

/// </summary>

/// <remarks>

/// Toast notifications aresystem-wide notifications

/// that do not disrupt the userworkflow or require

/// intervention to resolve.

/// They are displayed at the topof the screen for

/// ten seconds beforedisappearing.

/// If the toast notification istapped,

/// the application that sent thetoast notification

/// will launch.

/// A toast notification can bedismissed with a flick.

///

/// This class’s members are threadsafe.

/// </remarks>   

public sealedclass ToastPushNotificationMessage:

    PushNotificationMessage

{

   #region Properties

 

    /// <summary>

    /// Gets or sets a bolded string that should bedisplayed

    /// immediately after the application icon.

    /// </summary>

    public string Title {get; set; }

 

    /// <summary>

    /// Gets or sets a non-bolded string

    /// that should be displayed

    /// immediately after the Title.

    /// </summary>

    public stringSubTitle { get; set;}

 

   /// <summary>

   /// Gets or sets the URI and query string of the target page

   /// should be navigated when clicking on the toast

   /// notification at client side.

   /// </summary>

   /// <example>/AppSettings.xaml</example>

   /// <example>/AppSettings.xaml?tab=push</example>

   /// <remarks>

   /// Null or empty string refers the main page.

   /// Onlysupported when communicating with Windows Phone 7.1

   /// applications.

    /// </remarks>

   public string TargetPage { get;set; }

 

   #endregion

 

   #region Ctor

    /// <summary>

    /// Initializes a new instance of this type.

    /// </summary>

    /// <paramname="sendPriority">

    /// The send priority of this message in the MPNS.</param>

    public ToastPushNotificationMessage(

    MessageSendPriority sendPriority =

    MessageSendPriority.Normal)

        : base(sendPriority)

    {

 

    }

   #endregion

}

我們解釋了Helper類的方法之後,將回到實驗中來。

6.      點擊F5,編譯並運行應用程序。熟悉操作之後,關閉應用程序,回到Visual Studio中。該應用程序支持發送Tile, Toast 和Raw HTTPnotifications。

Figure 3

PushNotifications Server Application

7.      接下來的幾步中,您將創建WCF service實例並且進行初始化。爲了完成與Push Notification Service間的通訊,Windows® Phone應用程序應該提供一個MSPN (Microsoft PushNotifications)的註冊通道URI給WPF應用程序。在下一個實驗中,您將會設置手機應用到WPF應用之間的通道,並且用它來註冊手機的URI給WPF應用程序。開始時,在Service工程文件夾中添加新的類,右鍵單擊工程文件夾的名稱,選擇Add,然後是Class。

Figure 4

Addinga New Class to the Project

 

8.       將其命名爲 RegistrationService 並點擊 Add 按鈕。

Figure 5

Naminga new class

9.      如果類沒有自動打開,打開所創建的類。

10.  將類設置爲public,並實現IRegistrationService 接口。該接口定義了一系列方法,允許手機應用程序註冊他們的MSPN URI。

C#

public class RegistrationService :IRegistrationService

{

 

}

 

11.   點擊 IRegistrationService,將鼠標懸停在“_”標記上,打開上下文菜單。

Figure 6

Openinginterface options

 

12.   選擇 Implement interface IRegistrationService來用默認方式實現接口。

Figure 7

Implementinginterface

13.  實現之後的類代碼如下所示:

C#

public class RegistrationService: IRegistrationService

{

    #region IRegistrationService Members

    public void Register(string uri)

    {

        throw new NotImplementedException();

    }

 

    public void Unregister(string uri)

    {

        throw new NotImplementedException();

    }

    #endregion

}

14.   註冊服務器會保留所有註冊過的Windows® Phone客戶端應用程序URI。 服務器會檢查客戶端註冊的請求,防止一個URI被重複註冊。爲了實現這個功能添加下列的變量定義:

C#

public static eventEventHandler<SubscriptionEventArgs> Subscribed;

 

private static List<Uri>subscribers = new List<Uri>();

private static object obj = newobject();

15.   用下列代碼來替換Register 方法:

C#

Uri channelUri = new Uri(uri,UriKind.Absolute);

Subscribe(channelUri);

16.   用下列代碼來替換 Unregister方法:

C#

Uri channelUri = new Uri(uri,UriKind.Absolute);

Unsubscribe(channelUri);

17.   創建新的輔助方法: SubscribeUnsubscribe, 參考下列代碼:

C#

#region Subscription/Unsubscribing logic

private void Subscribe(UrichannelUri)

{

    lock(obj)

    {

            if(!subscribers.Exists((u) => u == channelUri))

            {

               subscribers.Add(channelUri);

            }

    }

    OnSubscribed(channelUri, true);

}

 

public static voidUnsubscribe(Uri channelUri)

{

    lock(obj)

    {

       subscribers.Remove(channelUri);

    }

      OnSubscribed(channelUri, false);

}

#endregion

18.   創建輔助方法 OnSubscribed,參考下列代碼:

C#

#region Helper private functionality

private static voidOnSubscribed(Uri channelUri, bool isActive)

{

   EventHandler<SubscriptionEventArgs> handler = Subscribed;

    if(handler != null)

    {

          handler(null, newSubscriptionEventArgs(channelUri, isActive));

    }

}

#endregion

 

19.   爲了保存Subscribed 事件參數,創建SubscriptionEventArgs 內部類 (在RegistrationService 類中) ,參考下列代碼:

C#

#region Internal SubscriptionEventArgs class definition

public class SubscriptionEventArgs : EventArgs

{

    publicSubscriptionEventArgs(Uri channelUri, bool isActive)

    {

        this.ChannelUri= channelUri;

        this.IsActive= isActive;

    }

 

    publicUri ChannelUri { get;private set; }

    publicbool IsActive { get;private set; }

}

#endregion

20.  最後,在該類中創建公開方法,返回所有訂閱的列表 – 該列表會在WPF客戶端應用程序中被用到。

C#

#region Helper public functionality

public static List<Uri>GetSubscribers()

{

    returnsubscribers;

}

#endregion

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

22.  定位到下列代碼行: “//TODO - remove remark after creating registration service”,並將WCF service host初始化代碼解除註釋:

C#

//TODO - remove remark after creating registration service

host = new ServiceHost(typeof(RegistrationService));

host.Open();

23.   編譯並運行應用程序。一旦應用程序啓動,會首先檢查RESTful WCF service是否工作。爲此,我們在RegistrationService.cs.文件中,爲RegisterUnregister方法中增加一個斷點。

Figure 8

Breakpointto check RESTful WCF service

24.   啓動Internet Explorer瀏覽器,並訪問下面的網址:

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

25.   當斷點生效時,檢查收到的URI地址是:http://www.microsoft.com.

Figure 9

CheckingRESTful WCF service

26.  停止雕飾,去掉斷點,並關閉IE瀏覽器。

27.  在WPPushNotification.ServerSideWeatherSimulator工程中創建一個新的類,按照之前的描述,命名爲PushNotificationsLogMsg。打開新創建的 PushNotificationsLogMsg.cs 文件,用下面代碼替換:

C#

using System;

using System.Net;

usingWindowsPhone.Recipes.Push.Messasges;

28.  添加enum定義,用來表示不同的通知類型,並將其設置爲public,參考下面代碼:

C#

namespaceWPPushNotification.ServerSideWeatherSimulator

{

   #region NotificationType Enum

    public enum NotificationType

    {

        Token = 1,

        Toast = 2,

        Raw = 3

    }

   #endregion

 

    /// <summary>

    /// Converts push notificationresult to log message

    /// </summary>

    public class PushNotificationsLogMsg

    {

    }

}

29.   定義日誌消息的屬性:

C#

#region Properties

 

public DateTimeOffsetTimestamp { get; privateset; }

public stringMessageId { get; privateset; }

public stringChannelUri { get; privateset; }

public NotificationTypeNotificationType { get; private set; }

public HttpStatusCodeStatusCode { get; privateset; }

public stringNotificationStatus { get; private set; }

public stringDeviceConnectionStatus { get; private set; }

public stringSubscriptionStatus { get; private set; }

 

#endregion

30.  定義類的構造函數,根據通知發送結果和類型來創建日誌消息:

C#

#region Ctor

 

public PushNotificationsLogMsg(

    NotificationType notificationType,

    MessageSendResult messageSendResult)

{

    this.Timestamp = messageSendResult.Timestamp;

    this.MessageId =

       messageSendResult.AssociatedMessage.Id.ToString();

    this.ChannelUri =messageSendResult.ChannelUri.ToString();

    this.NotificationType = notificationType;

    this.StatusCode = messageSendResult.StatusCode;

    this.NotificationStatus =

       messageSendResult.NotificationStatus.ToString();

    this.DeviceConnectionStatus =

       messageSendResult.DeviceConnectionStatus.ToString();

    this.SubscriptionStatus =

        messageSendResult.DeviceConnectionStatus.ToString();

}

 

#endregion

31.   在WPPushNotification.ServerSideWeatherSimulator工程中,打開 MainWindow.xaml.cs 文件:

32.  在該類中添加下列引用聲明:

C#

using System;

using System.Collections.Generic;

using System.Collections.ObjectModel;

using System.IO;

using System.Text;

using System.Windows;

using System.Windows.Documents;

using System.Xml;

using WeatherService.Service;

using WindowsPhone.Recipes.Push.Messasges;

33.   定位到 Private variables 區域,用下列代碼替換 “TODO”註釋中的部分:

C#

private ObservableCollection<PushNotificationsLogMsg> trace =

    new ObservableCollection<PushNotificationsLogMsg>();

private RawPushNotificationMessagerawPushNotificationMessage =

    new RawPushNotificationMessage();

34.  我們的Windows® Phone 7 Mango客戶端應用程序,將會使用我們所創建的Weather Server RegistrationService服務。WPF應用程序模仿了真實的網站解決方案,用來推送天氣信息到所連接的客戶端,因爲它應該瞭解Windows® Phone 7 Mango客戶端應用程序和接受註冊的事件。另外,我們的WPF客戶端應該顯示Pushnotification Service連接日誌,定位到MainWindow類的構造函數,在“TODO”註釋後,添加下列代碼:

C#

Log.ItemsSource = trace;

RegistrationService.Subscribed += new EventHandler<RegistrationService.SubscriptionEventArgs>(RegistrationService_Subscribed);

35.   定位在Event Handlers 區域,並添加下列函數:

C#

void RegistrationService_Subscribed(objectsender, RegistrationService.SubscriptionEventArgs e)

{  

    Dispatcher.BeginInvoke((Action)(() =>

        { UpdateStatus(); })

    );

}

36.  在上述代碼之後,添加下列方來來創建下列方法,用來作爲一個回調函數被推送通知調用:

C#

private voidOnMessageSent(NotificationType type,

 MessageSendResult result)

{

    PushNotificationsLogMsg msg =

        new PushNotificationsLogMsg(type,result);

   Dispatcher.BeginInvoke((Action)(()=>

    {trace.Add(msg); }));

}

37.   定位在Private functionality 部分,添加下列代碼,這部分代碼用來更新WPF客戶端應用程序的界面:

C#

private void UpdateStatus()

{

   int activeSubscribers =     RegistrationService.GetSubscribers().Count;

   boolisReady = (activeSubscribers > 0);

   txtActiveConnections.Text =activeSubscribers.ToString();

   txtStatus.Text = isReady ? "Ready" : "Waitingfor connection...";

}

38.  定位 sendHttp 方法,並添加下列代碼。該方法將給出一個連接客戶端的列表,根據UI的輸入,將rawPushNotificationMessage中的消息異步發送:

C#

//Get the list of subscribed WP7clients

List<Uri>subscribers = RegistrationService.GetSubscribers();

//Prepare payload

byte[] payload = prepareRAWPayload(

   cmbLocation.SelectedValue as string,

       sld.Value.ToString("F1"),

   cmbWeather.SelectedValue as string);

 

rawPushNotificationMessage.RawData = payload;

subscribers.ForEach(uri =>

   rawPushNotificationMessage.SendAsync(uri,

    (result) =>

       OnMessageSent(NotificationType.Raw,result),

    (result) =>{ }));

 

39.   定位到Private functionality 區域,並添加prepareRAWPayload 方法。該函數創建一個內存中的XML文檔,並作爲一個byte數組返回(準備發送給Windows® Phone Push Notification Services,並轉發給Windows®Phone應用程序客戶端)。在本次課程的稍後章節中,您將添加一個方法用來解析從設備端返回的XML文件。添加下列代碼:

C#

private static byte[] prepareRAWPayload(string location, string temperature, string weatherType)

{

   MemoryStreamstream = new MemoryStream();

 

   XmlWriterSettingssettings = new XmlWriterSettings()

    { Indent = true, Encoding = Encoding.UTF8 };

   XmlWriterwriter = XmlTextWriter.Create(stream,settings);

 

   writer.WriteStartDocument();

   writer.WriteStartElement("WeatherUpdate");

 

   writer.WriteStartElement("Location");

   writer.WriteValue(location);

   writer.WriteEndElement();

 

   writer.WriteStartElement("Temperature");

   writer.WriteValue(temperature);

   writer.WriteEndElement();

 

  writer.WriteStartElement("WeatherType");

   writer.WriteValue(weatherType);

   writer.WriteEndElement();

 

   writer.WriteStartElement("LastUpdated");

   writer.WriteValue(DateTime.Now.ToString());

   writer.WriteEndElement();

 

   writer.WriteEndElement();

   writer.WriteEndDocument();

   writer.Close();

 

   byte[]payload = stream.ToArray();

   returnpayload;

}

40.  編譯應用程序,並且修復編譯錯誤。該步驟完成了本次任務。

 

任務 2 – 創建Windows® Phone 7客戶端應用程序

1.      在解決方案中添加新的工程。該工程是Windows® Phone Application,名爲WPPushNotification.TestClient

Figure 10

Addinga new Windows® Phone Application project to the solution

2.       添加 System.Xml.Linq 程序集的引用 (在.NET 屬性頁中).

Figure 11

Addingreference to the System.Xml.Linq

3.       打開Source\Assets中的Assets 文件夾,定位到Styles.txt文件,使用Notepad打開。

4.       打開App.xaml.cs文件,從Styles.txt中將所有XAML拷貝到 Application.Resources 片段中。

5.      將下面藍條高亮部分的clr-namespace 聲明,添加到Application標籤中 (位於文件的頂部).

XAML

<Application

    ...

   xmlns:system="clr-namespace:System;assembly=mscorlib">

    ...

</Application>

 

6.      從Assets文件夾中,添加CloudBackgroundMobile.jpg 到該工程中。右鍵單擊WPPushNotification.TestClient(工程名),選擇Add à Existing Item 。在“Add Existing Item”對話框中,導航到Source\Assets文件夾中,選擇CloudBackgroundMobile.jpg,點擊Add按鈕。

Figure 12

Addingexisting image resource

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="WEATHERSERVICE" 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="{StaticResourcePhoneTextNormalStyle}" />

            <TextBlock x:Name="txtStatus"FontFamily="Segoe WP" FontSize="24" Foreground="#0a364c"Margin="0,0,0,0" Style="{StaticResourcePhoneTextNormalStyle}" Text="Not Connected"TextWrapping="Wrap" />

        </StackPanel>

 

</Grid>

 

9.       打開 MainPage.xaml.cs.

10.   添加下列的using聲明:

C#

using Microsoft.Phone.Notification;

using System.Diagnostics;

using System.Windows.Threading;

using System.Windows.Media.Imaging;

using System.IO;

using System.Xml.Linq;

using System.Collections.ObjectModel;

11.  添加下列代碼片段,在類的開頭來定義私有變量和常數:

C#

private HttpNotificationChannel httpChannel;

const string channelName ="WeatherUpdatesChannel";

const string fileName ="PushNotificationsSettings.dat";

const int pushConnectTimeout = 30;

12.   添加下列輔助方法。這些方法會更新Windows® Phone 7應用程序的UI狀態,並且打印一些有用的trace信息:

C#

#region Tracing and Status Updates

private void UpdateStatus(stringmessage)

{

   txtStatus.Text = message;

}

 

private void Trace(stringmessage)

{

#if DEBUG

   Debug.WriteLine(message);

#endif

}

#endregion

13.   設置 WPPushNotification.TestClient 工程作爲啓動工程。右鍵單擊工程的名稱,並且選擇Set as Startup Project

Figure 13

Settinga project as StartUp Project

14.   編譯並運行應用程序。

15.  應用程序執行效果如下:

 

Figure 14

Runningthe Weather client

16.  停止調試並且返回代碼。這一步我們完成了本次任務。

 

任務 3 – 創建Notification Channel

在接下來的第二部中,您將會創建新的Push NotificationChannel(推送通知通道),使用它可以通過Microsoft Corporation Push Notification 服務發送消息。HttpNotificationChannel類用來在Push Notification service和Windows® Phone Push Client之間創建一個通知通道,並創建一個針對raw, tile, 和 toast通知的訂閱。該通道創建的流程如下:如果通道已將存在,客戶端應用程序將嘗試重新打開它。試圖重新創建一個已經存在的通道,會拋出一個異常。如果通道仍然沒有打開,會通知通道事件,並嘗試打開通道。一旦通道打開,會觸發ChannelUriUpdated事件。該事件通知Windows® Phone應用程序,該通道被成功地打開。已經存在的通道可以通過通道名稱被搜索到。如果通過名稱成功搜索到該通道,您可以重新激活,並在應用程序中使用。整個過程是異步的。

1.       從WPPushNotification.TestClient工程中打開 MainPage.xaml.cs 文件。

2.      爲了各種輔助函數,創建一個新的區域,參考下面代碼:

C#

#region Misc logic

private void ConnectToMSPN()

{

   //TODO - place connection logichere

}

#endregion

3.       在ConnectToMSPN方法中,創建 Try/Catch 代碼,參考下面代碼:

C#

try

{

 

}

catch (Exception ex)

{

  Dispatcher.BeginInvoke(()=> UpdateStatus("Channel error: "+ ex.Message));

}

4.      接下來,初始化通道變量,訂閱通道事件,然後嘗試打開通道。創建try代碼來包括下面的代碼:

C#

//First, try to pick up existing channel

httpChannel = HttpNotificationChannel.Find(channelName);

 

if (null != httpChannel)

{

  Trace("Channel Exists - no need to create a new one");

  SubscribeToChannelEvents();

 

  Trace("Register the URI with 3rd party web service");

  SubscribeToService();

   //TODO: Place Notification

 

  Dispatcher.BeginInvoke(() => UpdateStatus("Channelrecovered"));

}

else

{

  Trace("Trying to create a new channel...");

  //Create the channel

  httpChannel = new HttpNotificationChannel(channelName, "HOLWeatherService");

  Trace("New Push Notification channel created successfully");

 

  SubscribeToChannelEvents();

 

  Trace("Trying to open the channel");

  httpChannel.Open();

  Dispatcher.BeginInvoke(() => UpdateStatus("Channelopen requested"));

}

5.      創建一個輔助方法,訂閱通道事件:

C#

#region Subscriptions

private void SubscribeToChannelEvents()

{

  //Register to UriUpdated event- occurs when channel successfully opens

   httpChannel.ChannelUriUpdated += new EventHandler<NotificationChannelUriEventArgs>(httpChannel_ChannelUriUpdated); 

   //Subscribed to RawNotification

   httpChannel.HttpNotificationReceived += newEventHandler<HttpNotificationEventArgs>(httpChannel_HttpNotificationReceived);

 

    //general error handling for pushchannel

    httpChannel.ErrorOccurred += new EventHandler<NotificationChannelErrorEventArgs>(httpChannel_ExceptionOccurred);

}

#endregion

 

6.      添加輔助方法,用於訂閱Channel URI發送到服務器端天氣應用程序中。當推送通知通道URI被註冊到服務器端天氣應用程序中時,該事件被觸發。該方法使用WebClient和硬編碼RESTful WCF service URL來註冊client’s URI (從推送服務器中獲得)。添加下來代碼到Subscriptions區域中:

C#

private void SubscribeToService()

{

    //Hardcode for solution - need tobe updated in case the REST WCF service address change

    stringbaseUri ="http://localhost:8000/RegirstatorService/Register?uri={0}";

    string theUri =String.Format(baseUri, httpChannel.ChannelUri.ToString());

    WebClientclient = new WebClient();

    client.DownloadStringCompleted += (s, e)=>

   {

      if(null == e.Error)

         Dispatcher.BeginInvoke(()=>UpdateStatus("Registration succeeded"));

      else

         Dispatcher.BeginInvoke(()=>UpdateStatus("Registration failed: " + e.Error.Message));

   };

   client.DownloadStringAsync(new Uri(theUri));

}

7.       創建事件處理函數來接受 ChannelUriUpdate 事件:

C#

#region Channel event handlers

void httpChannel_ChannelUriUpdated(objectsender, NotificationChannelUriEventArgs e)

{

    Trace("Channel opened. GotUri:\n" +httpChannel.ChannelUri.ToString());

 

    Trace("Subscribing tochannel events");

    SubscribeToService();

     

    Dispatcher.BeginInvoke(() =>UpdateStatus("Channel createdsuccessfully"));

}

#endregion

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

C#

void httpChannel_ExceptionOccurred(objectsender, NotificationChannelErrorEventArgs e)

{

    Dispatcher.BeginInvoke(() =>UpdateStatus(e.ErrorType + " occurred: "+ e.Message));

}

9.       最後添加 HttpNotificationReceived 事件處理函數:

C#

void httpChannel_HttpNotificationReceived(objectsender,  HttpNotificationEventArgs e)

{

   Trace("===============================================");

    Trace("RAW notificationarrived:");

 

    //TODO - add parsing and UIupdating logic here

   Trace("===============================================");

}

該函數將會解析從Push Notification Service接收到的XML,並更新客戶端應用程序的UI。

10.  在類的構造函數中,添加ConnectToMSPN 的調用。在構造函數的最終結果類似下面的代碼:

C#

public MainPage()

{

   InitializeComponent();

  ConnectToMSPN();

}

11.  編譯應用程序,並修復編譯錯誤。

12.   在解決方案中,定義多個啓動工程,同時運行WPFPush Notification Client 和 Windows® Phone 7 Push客戶端。在Solution Explorer中右鍵單擊解決方案名稱,並且在右鍵菜單中選擇Properties

Figure 15

OpeningSolution Properties

13.   在Common Properties中,選擇Startup Projects 頁面,選擇 Multiple startup projects ,並在Action下拉菜單中,設置 WPPushNotification.TestClientWPPushNotification.ServerSideWeatherSimulator 工程爲 Start

Figure 16

Selectingmultiple startup projects

14.  按 F5 編譯並運行應用程序。

15.  在所有工程啓動之後,您的屏幕將顯示下面畫面:

Figure 17

Runningmultiple projects

16.   確認 Windows® Phone 7 客戶端應用程序可以成功地註冊到RegistrationService中:

Figure 18

Checkingclient application registration

17.  在httpChannel_HttpNotificationReceived 函數中設置斷點。

Figure 19

Breakpointin notification event handler function

18.  改變WPF Push Notification Client的一些參數,並點擊SendHttp 按鈕。當斷點被觸發,代碼執行暫停,檢查所接收到的消息。

Figure 20

Breakpointhit when notification arrives

 

Figure 21

Notificationpayload received by Windows® Phone 7 client application

19.   點擊 F5 繼續程序的執行。觀察WPF Push Notification Client中新的日誌記錄。

Figure 22

WPFPush Notification Client with updated log

20.   停止調試,並且返回到Visual Studio中 (請不要關閉Windows® Phone 7 emulator!). 這一步將完成該任務。

 

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

1.      在本次任務中,您將創建一個方法,用來解析XML文件,並更新Windows® Phone 7應用程序UI。在MainPage.xaml.cs文件的Misc logic部分添加下列方法:

C#

private void ParseRAWPayload(Stream e, out string weather, outstring location, out string temperature)

{

    XDocumentdocument;

 

    using(var reader = newStreamReader(e))

    {

      stringpayload = reader.ReadToEnd().Replace('\0',' ');

      document = XDocument.Parse(payload);

    }

 

    location = (from c indocument.Descendants("WeatherUpdate")

    selectc.Element("Location").Value).FirstOrDefault();

    Trace("Gotlocation: " + location);

 

   temperature = (from c in     document.Descendants("WeatherUpdate")

     select    c.Element("Temperature").Value).FirstOrDefault();

    Trace("Gottemperature: " + temperature);

 

    weather = (from c indocument.Descendants("WeatherUpdate")

     select     c.Element("WeatherType").Value).FirstOrDefault();

}

2.      爲了展現天氣圖像,您需要添加天氣條件圖標(在本次實驗室的assets中提供)。在PushNotifications工程中,創建一個新的工程文件夾,並命名爲Images

3.      從實驗的Assets文件夾中,添加所有的PNG 文件 (除了CloudBackgroundMobile.jpg外的所有圖片文件) ,該文件夾位於 Source\Assets 中:

Figure 23

Addingexisting images from Assets folder

 

Figure 24

Addingexisting images from Assets folder

4.      將所有添加圖片的build action設置爲 Content。打開圖片的Properties窗口,並在Build Action下拉菜單中選擇 Content

Figure 25

Markingimage as Content resource

5.      重新調用在上一個任務中創建的方法(httpChannel_HttpNotificationReceived,在 MainPage.xaml.cs文件中)。在更新UI之前,這些方法將會調用解析方法在“TODO”註釋之後,添加下列藍色高亮的代碼:

C#

void httpChannel_HttpNotificationReceived(object sender, HttpNotificationEventArgse)

{

    Trace("===============================================");

    Trace("RAW notification arrived:");

 

    string weather, location, temperature;

    ParseRAWPayload(e.Notification.Body, outweather, out location, out temperature);

 

   Dispatcher.BeginInvoke(() => this.textBlockListTitle.Text= location);

    Dispatcher.BeginInvoke(() => this.txtTemperature.Text = temperature);

    Dispatcher.BeginInvoke(() => this.imgWeatherConditions.Source = new BitmapImage(new Uri(@"Images/" + weather + ".png", UriKind.Relative)));

    Trace(string.Format("Got weather: {0} with {1}F at location {2}",weather, temperature, location));

 

    Trace("===============================================");

}

6.      編譯並運行該應用程序。在兩個應用程序啓動之後,在WPF Push Notification Client中改變某些值,並點擊Send Http按鈕。觀察通知到達Windows® Phone 7客戶端,然後界面改變。觀察Visual Studio 2010 Output窗口中Trace信息(如果Output窗口沒有顯示,點擊Debug | Windows | Output菜單項,或者Ctrl+W, O)

 

Figure 26

HttpNotification arrived and parsed

Figure 27

Trace informationin Output window

 

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

Figure 28

HttpNotification arrived and parsed. Trace log updated

Figure 29

HttpNotification arrived and parsed. Trace log updated

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

 

9.      第二次編譯並運行應用程序。使用調試來檢查第一次應用程序打開通道,並保存通道信息,第二次通過名稱找到通道。

這一步將完成本次課程。在本次課程中,您學習瞭如何與Microsoft Push Notification Services進行通訊,如何準備併發送消息到Windows®Phone 7客戶端應用程序,如何在Windows® Phone 7接受消息。

提示: 本次課程已經完成的解決方案在下面的位置: Source\Ex1-RawNotifications\End.

 


 

課程 2: 介紹 Toast 和 TileNotifications for Alerts

在本章中,您將學習兩個附加通知類型 – Toast和 Tile notifications。本次實驗將包括後臺服務器發送數據到MSPNS,在您的Windows®Phone 7 Mango應用程序中,如何註冊並控制事件。

Tiles和 toastnotifications是兩種使雲端服務可以傳遞反饋到應用程序自有用戶界面之外的渠道。另外,雲端服務可以發送Raw notification請求,依賴於通知發送類型,這些通知會被轉發到應用程序或Shell中。

TileNotifications

Tile是一種可視、動態的展現應用程序及其內容的方式,Tile在手機啓動界面的Quick Launch區域展示。例如一個天氣預報應用程序,也許會選擇在一個Tile裏顯示用戶的本地時間和氣候條件。因爲雲計算服務可以在任何時候改變Tile的顯示,這個機制可以被用於在信息變化時與用戶通訊。用戶可以在一個單獨的Tile裏顯示每個天氣信息,也可以控制選擇哪些Tile顯示到Quick Launch區域中(Windows® Phone 7.1的一個變化是允許應用程序有多個Tile,每個Tile可以對應應用程序的不同功能,每個Tile都可以獨立更新)。

雲計算服務可以控制每個Tile的背景圖片、counter(或 'badge')和標題屬性。這些數據使用進行Windows® Phone Developer Tools配置。動畫和音效屬性是被操作系統平臺配置的,而不是通過應用程序。例如,如果在任意Tile更新時,平臺配置了動畫和嗶嗶聲,這個效果會發生在每一個Tile上。

Tile的背景圖片可以引用本地資源或者雲端資源,本地資源作爲應用程序部署的一部分。引用一個雲端資源,應用程序可以動態更新Tile的背景圖片。這個功能需要在顯示之前,對於背景圖片進行相應的處理。在大多數場景中,應用程序會包括所有Tile所需的背景圖片,因爲這樣對於效率和待機時間都是最有的方案。

ToastNotifications

一個雲端服務可以產生一個特殊類型的pushnotification,叫做toast notification,將在用戶當前界面上顯示一個浮動提示。例如,一個天氣預報應用程序也許希望在服務器端天氣預報有效時,顯示一個toast notification。如果用戶點擊toast notification,應用程序將會啓動,並執行一些其他操作。

 

一個雲端服務可以控制一個toastnotification的標題和子標題。這些將會顯示應用程序的圖標,該圖標包含在應用程序的發佈包裏(在中,可以在啓動應用程序時提供一個自定義URI,使用其導航到指定頁面)。

最佳事件

·                 Toast notifications應該被設計爲與用戶密切相關,並且時間限制比較愛哦嚴格的。

 

任務 1 – 實現發送Tiles & Toasts的服務器端

1.       打開 Microsoft Visual Studio 2010Express for Windows® Phone 通過開始菜單 | 所有程序 | Microsoft Visual Studio 2010 Express | Microsoft Visual Studio2010 Express for Windows® Phone.

Visual Studio 2010: 打開 Visual Studio 2010 通過 開始菜單 | 所有程序 | Microsoft Visual Studio 2010.

重要提示: 爲了運行Visual Phone 2010Express for Windows® Phone 或者 Microsoft Visual Studio 2010所包含的WCF服務,該解決方案應該被以Administrative模式被打開。如何創建WCF服務,可以參考MSDN文檔: (http://msdn.microsoft.com/en-us/library/ms731758.aspx). 爲了以Administrative模式打開 Visual Studio 2010 Express for Windows® Phone 或者 Visual Studio 2010 。我們可以在Start | All Programs | Microsoft Visual Studio 2010 Express中找到Microsoft Visual Studio 2010 Express for Windows® Phone圖標,右鍵單擊圖標,選擇“Run as administrator”,UAC的提示會彈出,選擇“Yes”提高VisualStudio 2010 Express for Windows® Phone 或 VisualStudio 2010的權限。

2.       從Source\Ex2-TileToastNotifications\Begin文件夾中,打開 Begin.sln 。或者您也可以繼續上面工作過的解決方案。

3.       從WPPushNotification.ServerSideWeatherSimulator打開 MainWindow.xaml.cs 文件。

4.       定位 sendToast 方法。該方法會從Push Notification Client UI得到一個Toast消息,併發送給所有的訂閱者。參考下列代碼:

C#

string msg = txtToastMessage.Text;

txtToastMessage.Text = "";

List<Uri>subscribers = RegistrationService.GetSubscribers();

 

toastPushNotificationMessage.Title = "WEATHER ALERT";

toastPushNotificationMessage.SubTitle = msg;

 

subscribers.ForEach(uri =>

   toastPushNotificationMessage.SendAsync(uri,

    (result) =>OnMessageSent(NotificationType.Toast,result),

    (result) =>{ }));

 

5.       定位 sendTile 方法。該方法從Push Notification Client UI獲得參數,併發送給所有的訂閱者。參考下列代碼:

C#

string weatherType =cmbWeather.SelectedValue as string;

int temperature = (int)(sld.Value + 0.5);

string location =cmbLocation.SelectedValue as string;

List<Uri>subscribers = RegistrationService.GetSubscribers();

 

tilePushNotificationMessage.BackgroundImageUri =

    new Uri("/Images/" + weatherType + ".png",

        UriKind.Relative);

tilePushNotificationMessage.Count = temperature;

tilePushNotificationMessage.Title = location;

 

subscribers.ForEach(uri =>

   tilePushNotificationMessage.SendAsync(uri,

    (result) =>OnMessageSent(NotificationType.Token,result),

    (result) => {}));

6.      定位 sendRemoteTile 函數。該函數應該使用位於遠程服務器上的圖片發送一個tile notification方法。添加下列代碼:

C#

List<Uri>subscribers = RegistrationService.GetSubscribers();

 tilePushNotificationMessage.BackgroundImageUri= new Uri(

"http://www.larvalabs.com/user_images/screens_thumbs/12555452181.jpg");

 

subscribers.ForEach(uri =>tilePushNotificationMessage.SendAsync(uri,

    (result) =>OnMessageSent(NotificationType.Token,result),

    (result) =>{ }));

7.      編譯並運行應用程序。檢查由Push Notification Service分發的消息。

Figure 30

WPFPush Notifications Client log

提示: 如果您是從Begin解決方案開始本次課程,而不是上次課程完成的工程,爲了運行應用程序,您必須首先定義多重啓動工程,這是爲了讓WPF Push Notification Client and Windows® Phone 7 Push 客戶端同時運行。If you’ve startedthis exercise from the Begin solution instead of continuing with the previousexercise solution, to run the application, you first need to define multiplestartup projects for this solution in order to run WPF Push Notification Clientand Windows® Phone 7 Push client together.

在Solution Explorer中右鍵單擊解決方案名稱,並且在右鍵菜單中選擇Properties。在Common Properties中,選擇Startup Projects 頁面,選擇 Multiple startup projects ,並在Action下拉菜單中,設置 PushNotificationsWeather 工程爲 Start

該步驟完成本次任務。

 

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

1.       在WPPushNotification.TestClient工程中打開 MainPage.xaml.cs。

2.       在下列幾步中,您將訂閱Tile和Toast通知時間,然後處理這些事件。定位在Subscriptions部分,添加下列代碼:

C#

private void SubscribeToNotifications()

{

    //////////////////////////////////////////

    // Bind toToast Notification

    //////////////////////////////////////////

    try

    {

        if(httpChannel.IsShellToastBound == true)

        {

            Trace("Already bounded (register) to Toastnotification");

        }

        else

        {

            Trace("Registering to Toast Notifications");

            httpChannel.BindToShellToast();

        }

    }

    catch (Exception ex)

    {

        // handle errorhere

    }

 

    //////////////////////////////////////////

    // Bind to TileNotification

    //////////////////////////////////////////

    try

    {

        if(httpChannel.IsShellTileBound == true)

        {

            Trace("Already bounded (register) to TileNotifications");

        }

        else

        {

            Trace("Registering to Tile Notifications");

 

            // you canregister the phone application to receive tile images from remote servers [thisis optional]

            Collection<Uri> uris = newCollection<Uri>();

            uris.Add(new Uri("http://www.larvalabs.com"));

 

            httpChannel.BindToShellTile(uris);

        }

    }

    catch (Exception ex)

    {

        //handle errorhere

    }

}

 

3.       現在定位 SubscribeToChannelEvents 方法中,並添加下列藍色高亮的代碼:

C#

private void SubscribeToChannelEvents()

{

   //Register to UriUpdated event - occurs when channel successfullyopens

  httpChannel.ChannelUriUpdated += new EventHandler<NotificationChannelUriEventArgs>(httpChannel_ChannelUriUpdated); 

 

   //Subscribed to Raw Notification

  httpChannel.HttpNotificationReceived += newEventHandler<HttpNotificationEventArgs>(httpChannel_HttpNotificationReceived);

 

   //general errorhandling for push channel

  httpChannel.ErrorOccurred += new EventHandler<NotificationChannelErrorEventArgs>(httpChannel_ExceptionOccurred);

   //subscrive to toastnotification when running app

httpChannel.ShellToastNotificationReceived += newEventHandler<NotificationEventArgs>(httpChannel_ShellToastNotificationReceived);

}

4.       定位 Channel event handlers 區域,添加下列代碼:

C#

void httpChannel_ShellToastNotificationReceived(objectsender, NotificationEventArgs e)

{

   Trace("===============================================");

   Trace("Toast/Tilenotification arrived:");

   foreach(var key ine.Collection.Keys)

   {

      stringmsg = e.Collection[key];

 

      Trace(msg);

      Dispatcher.BeginInvoke(() =>UpdateStatus("Toast/Tile message: "+ msg));

   }

        

   Trace("===============================================");

}

Note: In our simple case the functions just trace the message payload,but in a real-world application you could use it to perform any business logic.

5.      最後,添加下列代碼到相應的位置:

C#

SubscribeToNotifications();

該位置添加下列代碼:

6.       在ConnectToMSPN 函數中,在“SubscribeToService();”和“Dispatcher.BeginInvoke();”之間使用try 模塊:

C#

if (null != httpChannel)

{

    Trace("ChannelExists - no need to create a new one");

    SubscribeToChannelEvents();

 

    Trace("Registerthe URI with 3rd party web service");

    SubscribeToService();

 

    Trace("Subscribe to the channel to Tile and Toast notifications");

    SubscribeToNotifications();

 

    Dispatcher.BeginInvoke(() => UpdateStatus("Channel recovered"));

}

 

7.       在 httpChannel_ChannelUriUpdated 函數中,在 “SubscribeToService();”和 “Dispatcher.BeginInvoke(…);”中

C#

void httpChannel_ChannelUriUpdated(objectsender, NotificationChannelUriEventArgs e)

{

    Trace("Channel opened. Got Uri:\n" + httpChannel.ChannelUri.ToString());

    Dispatcher.BeginInvoke(()=> SaveChannelInfo());

    Trace("Subscribing to channel events");

    SubscribeToService();

   SubscribeToNotifications();

    Dispatcher.BeginInvoke(()=> UpdateStatus("Channel created successfully"));

}

8.       按 F5 編譯並運行整個應用程序。 在手機模擬器中,點擊 Back 物理按鈕 () 推出 Push Notification 應用程序切換到 Quick Launch 區域。

9.      在 Quick Launch區域,點擊Right Arrow 按鈕,切換到 “All Applications” 屏幕。

Figure 31

Openingthe installed applications screen

10.  定位到 WPPushNotification.TestClient 項,按住該項目,在彈出菜單中,點擊Pin to Start.

 

Figure 32

Pinningtile to Start screen

 

11.  模擬器將會自動返回 Quick Launch 區域,您會注意到WPPushNotification.TestClient的tile 已經出現。.

Figure 33

WPPushNotification.TestClientTile

 

12.   在 WPF Push NotificationClient中,改變一些通知參數,確認 Tile 已經被選中,點擊 Send按鈕。觀察模擬器中 Tile的改變。

 

Figure 34

Tile Notification arrived on emulator

 

13.   在 WPF Push Notification Client中,確認Remote Tile 被選中,然後點擊 Send 按鈕。觀察模擬器中Tile的改變。

Figure 35

RemoteTile Notification arrived on emulator

14.  在 WPF Push Notification Client中,輸入一些信息,點擊Send Toast 按鈕。 觀察Toast 消息如何出現在手機上。點擊 Toast message 切換回應用程序中。

Figure 36

Toastmessage on phone emulator

Figure 37

Toastmessage on Windows® Phone Application

提示: 真實的應用程序要確保顯示在Tile上的信息與程序內顯示的信息保持同步,這個簡單的應用程序中並未實現。這事因爲手機客戶端不會主動從WPF服務器上請求消息,當應用程序切換到後臺時,RAW消息會丟失。

另外,一個真實的服務器會與Tile消息一起發送RAW消息,因爲無法知道客戶端應用程序當前是否激活。我們創建的服務器只能根據用戶的選擇,發送指定類型的消息。

15.  這一步將完成本次任務。

 

任務 3 – 處理手機端的 Scheduled Tile Notifications

你也可以通過Shell tile schedule來更新應用程序的Tile,該功能將使用Microsoft.Phone.Shell.ShellTileSchedule類。該類允許應用程序按計劃,通過設置背景圖片適當的URI、調度循環和時間間隔來,更新Tile的背景圖片。當手機啓動Tile Schedule實例,它會自動發送tilenotification到應用程序,然後基於URI來獲取圖片,並更新Tile。

在下列步驟中,你將創建一個ShellTileSchedule類的實例,執行應用程序Tile的更新。

1.       打開PushNotifications對象中的 App.xaml.cs文件。

2.      定位到應用程序的構造函數 - App 方法,並插入下列代碼:

C#

// To store the instance for the application lifetime

private ShellTileScheduleshellTileSchedule;

 

  /// <summary>

  /// Create the applicationshell tile schedule instance

  /// </summary>

  private void CreateShellTileSchedule()

  {

      shellTileSchedule= new ShellTileSchedule();

      shellTileSchedule.Recurrence= UpdateRecurrence.Interval;

      shellTileSchedule.Interval= UpdateInterval.EveryHour;

      shellTileSchedule.StartTime= DateTime.Now;

      shellTileSchedule.RemoteImageUri= new   Uri(@"http://cdn3.afterdawn.fi/news/small/windows-phone-7-series.png");

      shellTileSchedule.Start();

  }

提示: 注意您只能提供一個 RemoteImageUri。所以,你必須提供一個在線且有效的URI能夠將顯示圖片下載並顯示。你不能引用來自本地應用程序的URI。圖片文件的尺寸不能超過,下載時間不能超過60秒。

3.       現在,定位到 App() 函數,在其中調用 CreateShellTileSchedule 方法 (查看高亮代碼):

C#

// Constructor

public App()

{

    // Global handler for uncaught exceptions.

    // Note that exceptions thrown by ApplicationBarItem.Clickwill not get caught here.

    UnhandledException += Application_UnhandledException;

 

    // Standard Silverlight initialization

    InitializeComponent();

 

    // Phone-specific initialization

    InitializePhoneApplication();

 

    // Create the shell tile schedule instance

    CreateShellTileSchedule();

}

 

你只需要提供應用程序的計劃屬性,便可以通過對象來更新Tile。

你需要設置下列屬性:

◦                   Recurrent 設置爲 Interval, 指定Tile更新的週期。其他可能會影響Tile更新的屬性,是OneTime,Tile更新只發生一次。

◦                   Interval 設置爲 EveryHour, 指定Tile每60分鐘更新一次。值得一提的是,一個小時可能是更新間隔的最小值。這樣的限制可以節省手機的資源。

◦                   RemoteImageUri t指定您想使Tile圖像更新的地址。

 

在這些之後,您可以調用Start方法來啓動Tile更新。需要了解的是,第一次更新可能會延遲一個小時,因爲該類會在整點調用。Tile更新計劃有些讓人不安。如果您設置StartTime爲now,您需要等待每60分鐘發生的手機更新。不幸的是,這是系統限制,因此如果您想調試這段代碼,您必須等待一個小時。我經常通宵調試這段代碼。

1.       按 F5 編譯並運行應用程序。在手機模擬器中點擊Back 物理按鈕 ()  退出 Push Notification 應用程序,並回到 Quick Launch 區域。

2.      驗證您的應用程序Tile已經被放置在 Quick Launch 區域,否則請遵照任務2的 7-9步驟 .

3.      PushNotifications tile 圖片將會根據ShellTileSchedule定義來進行更新。因此當地一個計劃安排的時間間隔過期,應用程序Tile將顯示RemoteImageUri屬性所指定的圖片,如http://cdn3.afterdawn.fi/news/small/windows-phone-7-series.png

Figure 38

ScheduledTile Image

 

該步驟將完成本次課程和實驗。

提示: 這次課程中完成的解決方案可以在下面地址找到:Source\Ex2-TileToastNotifications\End.

 

課程 3: 使用 sub-tiles 和 deep toast notifications

這次課程關注於Windows® Phone 7.1的新功能。我們提及的功能已經包括在上次課程的開始部分了– sub-tiles 和 deep toastnotifications.

最引人注目的改變是,我們將在本次課程中改變Windows® Phone客戶端應用程序,允許它爲一個單獨的地區,保留從服務器端接收到的天氣信息。客戶端應用程序將會有新的主界面,允許用戶選擇所在區域,以分別顯示不同的天氣信息。新的主界面也會允許用戶將sub-tiles定位到開始界面的任意區域。

我們將使用sub-tiles在開始界面上來顯示關於特定位置的信息,點擊sub-tile將會打開應用程序,並顯示相應位置的天氣信息。

Wewill also change the way toast notifications sent from the server work. The newtoast notifications, when clicked, will lead to the information page belongingto the location that they relate to.

我們將改變toast notifications從服務器端發送的方式。新的toastnotifications,當點擊時,會進入其所屬位置的信息頁面。

任務 1 – 使MSPN 註冊頁面獨立

Windows® Phone客戶端應用程序,目前假定整個應用程序是一個單獨頁面。因此我們想添加新的頁面到客戶端時,我們必須確保所有應用程序級的功能是獨立於任何頁面的。所有MSPN邏輯當前是包含在應用程序的主界面中的,在本次任務中,我們將改正它。

1.       打開 Microsoft Visual Studio 2010Express for Windows® Phone 通過開始菜單 | 所有程序 | Microsoft Visual Studio 2010 Express | Microsoft Visual Studio2010 Express for Windows® Phone.

Visual Studio 2010: 打開 Visual Studio 2010 通過 開始菜單 | 所有程序 | Microsoft Visual Studio 2010.

重要提示: 爲了運行Visual Phone 2010Express for Windows® Phone 或者 Microsoft Visual Studio 2010所包含的WCF服務,該解決方案應該被以Administrative模式被打開。如何創建WCF服務,可以參考MSDN文檔: (http://msdn.microsoft.com/en-us/library/ms731758.aspx). 爲了以Administrative模式打開 Visual Studio 2010 Express for Windows® Phone 或者 Visual Studio 2010 。我們可以在Start | All Programs | Microsoft Visual Studio 2010 Express中找到Microsoft Visual Studio 2010 Express for Windows® Phone圖標,右鍵單擊圖標,選擇“Run as administrator”,UAC的提示會彈出,選擇“Yes”提高VisualStudio 2010 Express for Windows® Phone 或 VisualStudio 2010的權限。

2.       打開位於Source\ Ex3-SecondaryTilesToastInput\Begin Begin.sln 工程文件。當然,您也可以繼續在上一個工程的基礎上繼續工作。

3.       在WPPushNotification.TestClient工程中創建一個新的類,按照之前的產品描述命名爲LocationInformation。打開新創建的LocationInformation.cs文件,用下列代碼替換using聲明:

C#

using System;

using System.ComponentModel;

4.       在該類中,實現INotifyPropertyChanged接口,幷包括下列變量和屬性:

C#

public class LocationInformation : INotifyPropertyChanged

{

   private bool tilePinned;

   private string name;

   private string temperature;

   private string imageName;

 

   /// <summary>

   /// Whether or not the location's secondary tile has been

   /// pinned by the user.

   /// </summary>

   public bool TilePinned

   {

       get

       {

            returntilePinned;

       }

       set

       {

            if (value != tilePinned)

            {

                tilePinned = value;

 

                if(PropertyChanged != null)

                {

                    PropertyChanged(this, new  PropertyChangedEventArgs("TilePinned"));

                }

            }

       }

   }

 

   /// <summary>

   /// The location's name.

   /// </summary>

   public string Name

   {

       get

       {

            returnname;

       }

       set

       {

            if (value != name)

            {

                name = value;

 

                if(PropertyChanged != null)

                {

                    PropertyChanged(this, new   PropertyChangedEventArgs("Name"));

                }

            }

       }

   }

 

   /// <summary>

   /// The temperature at the location.

   /// </summary>

   public string Temperature

   {

       get

       {

            returntemperature;

       }

       set

       {

            if (value != temperature)

            {

                temperature = value;

                if(PropertyChanged != null)

                {

                    PropertyChanged(this, new   PropertyChangedEventArgs("Temperature"));

                }

            }

       }

   }

 

   /// <summary>

   /// The name of the image to use for representing theweather

   /// at the location.

   /// </summary>

   public string ImageName

   {

       get

       {

            returnimageName;

       }

       set

       {

            if (value != imageName)

            {

                imageName = value;

                if(PropertyChanged != null)

                {

                    PropertyChanged(this, new   PropertyChangedEventArgs("ImageName"));

                }

            }

       }

   }

 

   public event PropertyChangedEventHandler PropertyChanged;

}

該類封裝了所有的與sub-tile相關的地理位置信息,包括:位置Tile是否被放置在開始區域,位置的名稱和溫度。我們將這個類作爲MSPN狀態添加,其中包括sub-tiles中所需要的信息。

5.       在WPPushNotification.TestClient工程中,創建新的類,遵照之前命名爲Status。打開Status.cs文件,用下列代碼替換using聲明。

C#

using System;

using System.ComponentModel;

6.       使該類實現INotifyPropertyChanged接口,包括下列的變量和屬性:

C#

public class Status : INotifyPropertyChanged

{

   private string message;

 

   /// <summary>

   /// A message representing some status.

   /// </summary>

   public string Message

   {

       get

       {

            returnmessage;

       }

       set

       {

            if (value != message)

            {

                message = value;

 

                if(PropertyChanged != null)

                {

                    PropertyChanged(this, new   PropertyChangedEventArgs("Message"));

                }

           }

       }

   }

 

 

   #region INotifyPropertyChanged Members

 

   public event PropertyChangedEventHandler PropertyChanged;

 

   #endregion

}

該類簡單實現了支持文本變量,當通知改變被觸發時,我們將在下一步中表示狀態改變消息。

7.       在WPPushNotification.TestClient工程中,創建新的類,命名爲PushHandler。打開PushHandler.cs文件,用下面代碼替代using聲明:

C#

using System;

using Microsoft.Phone.Notification;

using System.Collections.Generic;

using System.Windows.Threading;

using System.Collections.ObjectModel;

using System.IO;

using System.Xml.Linq;

using System.Net;

8.       爲PushHandler類添加下列的變量和屬性:

C#

private HttpNotificationChannelhttpChannel;

const stringchannelName = "WeatherUpdatesChannel";

 

private boolconnectedToMSPN;

private boolconnectedToServer;

private boolnotificationsBound;

 

/// <summary>

/// Contains informationabout the locations displayed by the

/// application.

/// </summary>

public Dictionary<string, LocationInformation>Locations
{ get; private set; }

 

/// <summary>

/// A dispatcher usedto interact with the UI.

/// </summary>

public DispatcherDispatcher { get; privateset; }

 

/// <summary>

/// Push servicerelated status information.

/// </summary>

public StatusPushStatus { get; privateset; }

 

/// <summary>

/// Whether or not thehandler has fully established a connection
/// to boththe MSPN and the application server.

/// </summary>

public boolConnectionEstablished

{

   get

   {

       return connectedToMSPN && connectedToServer &&notificationsBound;

   }

}

這些變量和屬性被用於建立並保持MSPN的連接狀態。我們將看到它們在接下來的幾步中,如何被使用。注意“Locations”屬性,我們將使用“Locations”屬性將每個位置的名稱映射到信息上。

 

9.       爲類添加構造函數:

C#

/// <summary>

/// Creates a new instance ofthe class.

/// </summary>

/// <paramname="pushStatus">Status object toupdate with push

/// related status messages.</param>

/// <paramname="locationsInformation">Dictionarycontaining

/// information about thelocations tracked by the

/// application</param>

/// <paramname="uiDispatcher">A dispatcher to usewhen updating

/// the user interface.</param>

public PushHandler (StatuspushStatus, Dictionary<string,

           LocationInformation>locationsInformation, Dispatcher

           uiDispatcher)

{

   PushStatus = pushStatus;

   Locations = locationsInformation;

   Dispatcher = uiDispatcher;

}

該構造函數只是簡單地將類的屬性進行初始化。

10.   爲類添加下列方法:

C#

/// <summary>

/// Connects to the MicrosoftPush Service and registers the

/// received channel with theapplication server.

/// </summary>

public voidEstablishConnections()

{

   connectedToMSPN = false;

   connectedToServer = false;

   notificationsBound = false;

 

   try

   {

       //First, try to pick up existing channel

       httpChannel = HttpNotificationChannel.Find(channelName);

 

       if (null != httpChannel)

       {

            connectedToMSPN = true;

 

            App.Trace("ChannelExists – no need to create a new one");

            SubscribeToChannelEvents();

 

            App.Trace("Registerthe URI with 3rd party web service");

            SubscribeToService();

 

            App.Trace("Subscribeto the channel to Tile and Toast notifications");

            SubscribeToNotifications();

 

            UpdateStatus("Channelrecovered");

       }

       else

       {

            App.Trace("Tryingto create a new channel...");

            //Create the channel

            httpChannel = new
                HttpNotificationChannel(channelName,

                        "HOLWeatherService");

            App.Trace("NewPush Notification channel created successfully");

 

            SubscribeToChannelEvents();

 

            App.Trace("Tryingto open the channel");

            httpChannel.Open();

            UpdateStatus("Channelopen requested");

       }

   }

   catch (Exception ex)

   {

       UpdateStatus("Channel error: " +ex.Message);

   }

}

這些方法建立了MSPN與WPF服務器應用程序之間的連接。與課程 1, 任務 3, 步驟 2的“ConnectToMSPN”方法非常類似。

11.   爲類添加下列代碼:

C#

private voidSubscribeToChannelEvents()

{

   //Register to UriUpdated event - occurs when channel

   //successfully opens

   httpChannel.ChannelUriUpdated+= new

       EventHandler<NotificationChannelUriEventArgs>(

       httpChannel_ChannelUriUpdated);

 

   //Subscribed to Raw Notification

   httpChannel.HttpNotificationReceived+= new

       EventHandler<HttpNotificationEventArgs>(

       httpChannel_HttpNotificationReceived);

 

   //general error handling for push channel

   httpChannel.ErrorOccurred+= new

       EventHandler<NotificationChannelErrorEventArgs>(

       httpChannel_ExceptionOccurred);

 

   //subscribe to toast notification when running app   

   httpChannel.ShellToastNotificationReceived+= new

       EventHandler<NotificationEventArgs>(

       httpChannel_ShellToastNotificationReceived);

}

上述方法簡單地註冊與MSPN通知通道相關的事件

12.   爲類添加下列代碼:

C#

private voidSubscribeToService()

{

   //Hardcode for solution - need to be updated in case the REST

   //WCF service address change

   string baseUri = "http://localhost:8000/RegirstatorService/Register?uri={0}";

   string theUri = String.Format(baseUri,

       httpChannel.ChannelUri.ToString());

   WebClient client = new WebClient();

   client.DownloadStringCompleted+= (s, e) =>

   {

       if(null == e.Error)

       {

            connectedToServer = true;

            UpdateStatus("Registration succeeded");

       }

       else

       {

            UpdateStatus("Registration failed: " +

                e.Error.Message);

       }

   };

 

   client.DownloadStringAsync(new Uri(theUri));

}

這些類將WPF服務進行註冊。

13.   爲類添加下列代碼:

C#

private voidSubscribeToNotifications()

{

   //////////////////////////////////////////

   // Bind to Toast Notification

   //////////////////////////////////////////

   try

   {

       if(httpChannel.IsShellToastBound == true)

       {

            App.Trace("Already bound to Toast notification");

       }

       else

       {

                App.Trace("Registering to Toast Notifications");

            httpChannel.BindToShellToast();

       }

   }

   catch (Exception ex)

   {

       // handle error here

   }

 

   //////////////////////////////////////////

   // Bind to Tile Notification

    //////////////////////////////////////////

   try

   {

       if(httpChannel.IsShellTileBound == true)

       {

            App.Trace("Already bound to Tile Notifications");

       }

       else

       {

            App.Trace("Registering to Tile Notifications");

 

            // you can registerthe phone application to receive

            // tile images fromremote servers [this is optional]

            Collection<Uri> uris = newCollection<Uri>();

            uris.Add(new Uri("http://www.larvalabs.com"));

 

            httpChannel.BindToShellTile(uris);

       }

   }

   catch (Exception ex)

   {

       //handle error here

   }

 

   notificationsBound= true;

}

該方法註冊toast 和 tile notifications。

14.  添加下列方法用於定義步驟11中的事件控制方法:

C#

void httpChannel_ChannelUriUpdated(object sender,

NotificationChannelUriEventArgs e)

{

   connectedToMSPN= true;

 

   App.Trace("Channel opened.Got Uri:\n" +

       httpChannel.ChannelUri.ToString());

   App.Trace("Subscribing tochannel events");

   SubscribeToService();

   SubscribeToNotifications();

 

    UpdateStatus("Channel created successfully");

}

 

void httpChannel_ExceptionOccurred(object sender,

NotificationChannelErrorEventArgs e)

{

   UpdateStatus(e.ErrorType+ " occurred: " + e.Message);

}

 

voidhttpChannel_HttpNotificationReceived(objectsender,

HttpNotificationEventArgs e)

{

   App.Trace("===============================================");

   App.Trace("RAW notificationarrived");

 

   Dispatcher.BeginInvoke(()=>

       ParseRAWPayload(e.Notification.Body));

 

   App.Trace("===============================================");

}

 

voidhttpChannel_ShellToastNotificationReceived(objectsender,

NotificationEventArgs e)

{

   App.Trace("===============================================");

   App.Trace("Toast/Tilenotification arrived:");

 

   string msg = e.Collection["wp:Text2"];

 

   App.Trace(msg);

   UpdateStatus("Toast/Tile message: " + msg);

 

   App.Trace("===============================================");

}

這些事件處理函數與之前在MainPage類中定義的類似。

15.  爲類添加下列兩個輔助方法:

C#

private voidUpdateStatus(string message)

{

   Dispatcher.BeginInvoke(() => PushStatus.Message = message);

}

 

private voidParseRAWPayload(Stream e)

{

    XDocumentdocument;

 

    using (var reader = new StreamReader(e))

    {

        string payload = reader.ReadToEnd().Replace('\0', ' ');

        document = XDocument.Parse(payload);

    }

 

    XElement updateElement = document.Root;

 

    string locationName = updateElement.Element("Location").

        Value;

    LocationInformation locationInfo =Locations[locationName];

    App.Trace("Gotlocation: " + locationName);

 

    string temperature = updateElement.Element("Temperature").

        Value;

   locationInfo.Temperature = temperature;

    App.Trace("Gottemperature: " + temperature);

 

    string weather = updateElement.Element("WeatherType").Value;

   locationInfo.ImageName = weather;

    App.Trace("Gotweather type: " + weather);           

}

第一個方法簡單地更新客戶端連接狀態的消息,第二個用來解析來自WPF服務器的Raw推動通知,天氣信息中擴展了位置信息。

16.   打開 App.xaml ,在Application標籤中添加附加的命名空間叫做“local”:

XAML

<Application

    x:Class="WPPushNotification.TestClient.App"

    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation

    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

    xmlns:system="clr-namespace:System;assembly=mscorlib"

    xmlns:phone="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone"

    xmlns:shell="clr-namespace:Microsoft.Phone.Shell;assembly=Microsoft.Phone"

    xmlns:local="clr-namespace:WPPushNotification.TestClient">

17.   在 Application.Resources 標籤中添加新的元素:

XAML

<!--ApplicationResources-->

<Application.Resources>

    <local:Status x:Key="PushStatus">

        <local:Status.Message>Not connected</local:Status.Message>

    </local:Status>

...

我們從之前步驟的代碼中,引用這些新的資源,將他們用在顯示應用程序的連接狀態上。

18.   打開 App.xaml.cs ,並添加下列using聲明:

C#

using System.IO.IsolatedStorage;

using Microsoft.Phone.Notification;

using System.Diagnostics;

using System.Windows.Threading;

using System.Collections.ObjectModel;

19.  在App類中添加下列屬性:

C#

/// <summary>

/// Whether or not it isnecessary to refresh the pin status of

/// the secondary tiles.

/// </summary>

public boolTileRefreshNeeded { get; set; }

 

/// <summary>

/// Contains information aboutthe locations displayed by the

/// application.

/// </summary>

public Dictionary<string, LocationInformation>Locations { get; set; }

 

/// <summary>

/// Returns the application'sdispatcher.

/// </summary>

public DispatcherDispatcher

{

    get

    {

        return Deployment.Current.Dispatcher;

    }

}

 

private PushHandlerPushHandler { get; set; }

20.   定位 Application_Launching 方法,改爲下列代碼:

C#

private void Application_Launching(object sender,LaunchingEventArgs e)

{

    TileRefreshNeeded = true;

 

    InitializeLocations();

    RefreshTilesPinState();

 

    PushHandler = newPushHandler(Resources["PushStatus"] asStatus, Locations, Dispatcher);

    PushHandler.EstablishConnections();

}

這些代碼使應用程序初始化位置信息,確認哪些位置信息會作爲sub-tile顯示在開始界面上,並使用PushHandler類連接到MSPN和WPF服務器。

21.   定位 Application_Activated 方法,修改爲下列代碼:

C#

private voidApplication_Activated(object sender, ActivatedEventArgs e)

{

    if (!e.IsApplicationInstancePreserved)

    {

        // The application was tombstoned, so restore its state

        foreach(var keyValue inPhoneApplicationService.Current.State)

        {

           Locations[keyValue.Key] = keyValue.Value asLocationInformation;

        }

 

        // Reconnect to the MSPN

        PushHandler= new PushHandler(Resources["PushStatus"] as

 Status, Locations, Dispatcher);

       PushHandler.EstablishConnections();

    }

    else if(!PushHandler.ConnectionEstablished)

    {

        // Connection was not fully established before fast app

        // switching occurred

        PushHandler.EstablishConnections();

    }

 

   RefreshTilesPinState();

}

上述代碼使應用程序存儲位置信息,位置信息從應用程序狀態信息中獲取,當重建連接時,這些信息會被存儲在墓碑機制中。如果正確建立連接之後需要重新嘗試建立連接,那麼應用程序實例被保留。最終,作爲用戶將會刪除一些應用程序的sub-tiles,應用程序需要檢查哪些Tile在開始界面上。

22.   定位 Application_Deactivated 方法,改爲下列代碼:

C#

private voidApplication_Deactivated(object sender, DeactivatedEventArgs e)

{

    foreach (var keyValuein Locations)

    {

        PhoneApplicationService.Current.State[keyValue.Key]=

           keyValue.Value;

    }

}

當應用程序觸發墓碑機制時,這些代碼將所有的位置信息保存到應用程序的狀態變量中。

23.  在App類中添加下列的輔助方法:

C#

/// <summary>

/// Writes debug output indebug mode.

/// </summary>

/// <paramname="message">The message to write todebug output.

/// </param>

internal static void Trace(string message)

{

#if DEBUG

   Debug.WriteLine(message);

#endif

}       

 

/// <summary>

/// Initializes the contents ofthe location dictionary.

/// </summary>

private voidInitializeLocations()

{

    List<LocationInformation>locationList =
        new List<LocationInformation>(new[] {

        new LocationInformation {Name = "Redmond",

            TilePinned = false },

        new LocationInformation {Name = "Moscow",

            TilePinned = false },

        new LocationInformation {Name = "Paris",

            TilePinned = false },

        new LocationInformation {Name = "London",

            TilePinned = false },

        new LocationInformation {Name = "New York",

            TilePinned = false }

    });

 

    Locations = locationList.ToDictionary(l => l.Name);

}

 

/// <summary>

/// Sees which of theapplication's sub-tiles are pinned and

/// updates the locationinformation accordingly.

/// </summary>

private voidRefreshTilesPinState()

{

    Dictionary<string, LocationInformation>updateDictionary =

        Locations.Values.ToDictionary(li => li.Name);

 

    foreach (ShellTile tile in ShellTile.ActiveTiles)

    {

        string[] querySplit =

            tile.NavigationUri.ToString().Split('=');

 

        if (querySplit.Count() !=2)

        {

            continue;

        }

 

        string locationName =

        Uri.UnescapeDataString(querySplit[1]);

        updateDictionary[locationName].TilePinned = true;

        updateDictionary.Remove(locationName);

    }

 

    foreach (LocationInformationlocationInformation in

        updateDictionary.Values)

    {

        locationInformation.TilePinned = false;

    }

}

 “Trace”方法會寫入調試信息。“InitializeLocations”在應用程序的五個位置上填充位置信息字典, “RefreshTilesPinState”遍歷所有的應用程序活動Tile(使用Windows®Phone 7.1 ShellTile類),並更新所有的位置信息。

提示: ShellTile.ActiveTiles 返回所有設置到開始區域的應用程序主Tile和sub-tiles,以及其是否被設置到開始區域上。

 

任務 2 – 更新客戶端主界面

在之前的任務中,我們重新實現了應用程序端的功能。在這個任務中,我們將改變主界面,因此允許我們查看所有有效的位置信息,查看開始區域中包括那些位置的sub-tiles,並且直接維護應用程序的主Tile。

1.       在WPPushNotification.TestClient工程中,添加 Microsoft.Phone.Controls.Toolkit 程序集的引用(使用Browse屬性頁)。該程序集包含附加的Windows®Phone控件,並定位在Source\Assets\Lib文件夾中。

Figure 39

Addingreference to the toolkit assembly

2.       在WPPushNotification.TestClient工程的Images文件夾下,創建  MainTile 子文件夾。

3.       將Source\Assets\MainTile下的所有圖片文件添加至剛纔創建的工程文件夾中。

4.       將所有添加圖片的build action選擇爲Content。打開圖片的Properties窗口,並且在Build Action下拉菜單中選擇爲Content。步驟與課程1,任務4,步驟4相同。

5.       在WPPushNotification.TestClient工程中添加Converters  工程文件夾。

6.       在該文件夾中添加新的類BoolToVisibilityConverter,打開BoolToVisibilityConverter.cs文件,添加下列using聲明:

C#

using System.Windows.Data;

7.       用下列代碼改變 BoolToVisibilityConverter類:

C#

public class BoolToVisibilityConverter : IValueConverter

{

 

    #region IValueConverter Members

 

    public object Convert(object value, Type targetType,

        object parameter,

        System.Globalization.CultureInfo culture)

    {

        return ((bool)value) ? Visibility.Visible :

            Visibility.Collapsed;

    }

 

    public object ConvertBack(object value, Type targetType,

        object parameter,

        System.Globalization.CultureInfo culture)

    {

        throw new NotImplementedException();

    }

 

    #endregion

}

這些類負責將Boolean值轉換爲界面元素visibility值。

8.       在上面創建的文件夾中,添加叫做 BoolToInverseVisibilityConverter的新類。打開BoolToInverseVisibilityConverter.cs文件,添加下列using聲明:

C#

using System.Windows.Data;

9.       在 BoolToInverseVisibilityConverter類中添加下列代碼:

C#

public class BoolToInverseVisibilityConverter: IValueConverter

{

 

    #region IValueConverter Members

 

    public object Convert(object value, Type targetType,

        object parameter,
        System.Globalization.CultureInfo culture)

    {

        return ((bool)value) ? Visibility.Collapsed :

            Visibility.Visible;

    }

 

    public object ConvertBack(object value, Type targetType,

        object parameter,

        System.Globalization.CultureInfo culture)

    {

        throw new NotImplementedException();

    }

 

    #endregion

}

這些類負責將Boolean值轉換爲界面元素visibility值,如果爲true,則顯示隱藏的界面元素。

10.   從WPPushNotification.TestClient工程打開 MainPage.xaml ,在頁面標籤中定義兩個附加的命名空間,設置數據上下文:

XAML

<phone:PhoneApplicationPage

    x:Class="WPPushNotification.TestClient.MainPage"

    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

    xmlns:phone="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone"

    xmlns:shell="clr-namespace:Microsoft.Phone.Shell;assembly=Microsoft.Phone"

    xmlns:toolkit="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone.Controls.Toolkit"

   xmlns:conv="clr-namespace:WPPushNotification.TestClient.Converters"

    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"

    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"

    mc:Ignorable="d" d:DesignWidth="480" d:DesignHeight="768"

    FontFamily="{StaticResourcePhoneFontFamilyNormal}"

    FontSize="{StaticResourcePhoneFontSizeNormal}"

    Foreground="{StaticResourcePhoneForegroundBrush}"

    SupportedOrientations="Portrait"Orientation="Portrait"

    shell:SystemTray.IsVisible="True"

    DataContext="{Binding RelativeSource={RelativeSource Self}}">

11.   改變 “LayoutRoot” grid中的內容:

XAML

    <!--LayoutRoot is the root grid where all page content isplaced-->

    <Grid x:Name="LayoutRoot" Background="Transparent">

        <Grid.Resources>

            <conv:BoolToVisibilityConverter x:Key="BoolToVisibilityConverter"/>

            <conv:BoolToInverseVisibilityConverter x:Key="BoolToInverseVisibilityConverter"/>

        </Grid.Resources>

 

        <Grid.RowDefinitions>

            <RowDefinition Height="Auto"/>

            <RowDefinition Height="*"/>

            <RowDefinition Height="Auto"/>

            <RowDefinition Height="Auto"/>

        </Grid.RowDefinitions>

 

        <Image Source="cloudbackgroundmobile.jpg" Grid.RowSpan="4"/>

 

        <StackPanel x:Name="TitlePanel" Grid.Row="0" Margin="24,24,0,12">

            <TextBlock x:Name="ApplicationTitle" Text="WEATHERSERVICE" Style="{StaticResource PhoneTextNormalStyle}"/>

            <TextBlock x:Name="PageTitle" Text="Locations" Margin="-3,-8,0,0" Style="{StaticResourcePhoneTextTitle1Style}"/>

        </StackPanel>

 

        <Grid Grid.Row="1" x:Name="ContentPanel" Background="#10000000">

            <ListBox DataContext="{Binding Locations}" ItemsSource="{Binding Values}" HorizontalContentAlignment="Stretch" HorizontalAlignment="Stretch" ScrollViewer.VerticalScrollBarVisibility="Disabled"SelectionChanged="ListBox_SelectionChanged"Margin="8,0,0,0">

                <ListBox.ItemTemplate>

                   <DataTemplate>

                       <Grid>

                            <toolkit:ContextMenuService.ContextMenu>

                            <toolkit:ContextMenu IsZoomEnabled="False">

                            <toolkit:MenuItem Header="Pinlocation" Visibility="{Binding TilePinned, Converter={StaticResourceBoolToInverseVisibilityConverter}}" Click="PinItem_Click"/>

                                <toolkit:MenuItem Header="Un-pinlocation" Visibility="{Binding TilePinned, Converter={StaticResourceBoolToVisibilityConverter}}" Click="UnpinItem_Click"/>

                            </toolkit:ContextMenu>

                       </toolkit:ContextMenuService.ContextMenu>

                       <Grid.ColumnDefinitions>

                            <ColumnDefinition Width="Auto"/>

                                <ColumnDefinition Width="400"/>

                            </Grid.ColumnDefinitions>

                       <CheckBox Grid.Column="0" IsEnabled="False" IsChecked="{Binding TilePinned}"/>

                   <TextBlock Grid.Column="1" Text="{Binding Name}" VerticalAlignment="Center" FontSize="30" Margin="{StaticResource PhoneTouchTargetOverhang}"/>

                   </Grid>

                </DataTemplate>

            </ListBox.ItemTemplate>

        </ListBox>

    </Grid>

 

    <Grid Grid.Row="2" Margin="20,0,20,0">

        <Grid.RowDefinitions>

            <RowDefinition Height="Auto"/>

                <RowDefinition Height="Auto"/>

            </Grid.RowDefinitions>

            <Grid.ColumnDefinitions>

            <ColumnDefinition Width="Auto"/>

            <ColumnDefinition Width="*"/>

            <ColumnDefinition Width="Auto"/>

    </Grid.ColumnDefinitions>

 

            <TextBlock Text="Image:" Foreground="#104f6f" FontSize="24" Grid.Column="0" VerticalAlignment="Center"/>

            <toolkit:ListPicker x:Name="listMainTileImage" Grid.Column="1" Margin="{StaticResourcePhoneTouchTargetOverhang}">

            <toolkit:ListPickerItem>Star</toolkit:ListPickerItem>

            <toolkit:ListPickerItem>Swirl</toolkit:ListPickerItem>

            </toolkit:ListPicker>

 

            <TextBlock Grid.Row="1" Text="Title:" Foreground="#104f6f" FontSize="24" VerticalAlignment="Center" Grid.Column="0"/>

            <TextBox x:Name="txtMainTileTitle" Grid.Row="1" Grid.Column="1" Margin="0,0,1,0"/>

 

            <Button Grid.Column="3" Grid.RowSpan="2" Foreground="#104f6f" Click="ChangeMainTile_Click">

            <Button.Content>

                <TextBlock Text="Apply to maintile" Width="100" TextWrapping="Wrap"/>

            </Button.Content>

        </Button>

    </Grid>

        <StackPanel Grid.Row="3" x:Name="StatusStackPanel" Margin="20,0">

        <TextBlock FontSize="34" FontFamily="Segoe WPSemibold" Foreground="#104f6f" Text="Status" Style="{StaticResource PhoneTextNormalStyle}"/>

        <TextBlock x:Name="txtStatus" DataContext="{StaticResource PushStatus}" Text="{Binding Message}" FontFamily="SegoeWP" FontSize="24" Foreground="#0a364c" Margin="0,0,0,0" Style="{StaticResourcePhoneTextNormalStyle}" TextWrapping="Wrap"/>

       </StackPanel>

 

    </Grid>

12.   我們現在需要改變主界面中的代碼來匹配新的可視界面,刪除掉MSPN相關的功能。打開MainPage.xaml.cs ,並改變 MainPage 類:

C#

public partial class MainPage : PhoneApplicationPage

{

    /// <summary>

    /// Contains informationabout the locations displayed by the

    /// application.

    /// </summary>

   public Dictionary<string, LocationInformation>Locations
        { get; set; }

 

   // Constructor

   public MainPage()

   {

       InitializeComponent();

   }

 

    #region Navigation

   protected override void OnNavigatedTo(
        System.Windows.Navigation.NavigationEventArgs e)

   {

       Locations = (App.Current as App).Locations;

 

       base.OnNavigatedTo(e);

   }

    #endregion

}

當頁面顯示時,新版本的主界面簡單地從應用程序對象中獲取位置信息字典。

13.   添加下列代碼到主界面類中:

C#

private staticUri MakeTileUri(LocationInformation
locationInformation)

{

    return new Uri(Uri.EscapeUriString(

        String.Format("/CityPage.xaml?location={0}",

vlocationInformation.Name)), UriKind.Relative);

}

該方法會創建URI,用來顯示不同地點的信息。這些URI使用“CityPage”的參數,我們稍後將詳細介紹。

14.   在主界面的類中添加下列方法:

C#

private voidUnpinItem_Click(object sender, RoutedEventArgs e)

{

    LocationInformation locationInformation =

        (sender as MenuItem).DataContextas LocationInformation;

 

    ShellTile tile = ShellTile.ActiveTiles.FirstOrDefault(

        t =>t.NavigationUri.ToString().EndsWith(

       locationInformation.Name));

 

    if (tile == null)

    {

        MessageBox.Show("Tileinconsistency detected. It is suggested that you restart the application.");

        return;

    }

 

    try

    {

       tile.Delete();

       locationInformation.TilePinned = false;

    }

    catch (Exceptionex)

    {

        MessageBox.Show(ex.Message, "Error deleting tile",

        MessageBoxButton.OK);

        return;

    }

}

 

private voidPinItem_Click(object sender, RoutedEventArgs e)

{

    LocationInformation locationInformation =

         (sender as MenuItem).DataContextas LocationInformation;

 

    Uri tileUri = MakeTileUri(locationInformation);

 

    StandardTileData initialData = new StandardTileData()

    {

       BackgroundImage = new Uri("Images/Clear.png",UriKind.Relative),

        Title =locationInformation.Name

    };

 

     ((sender as MenuItem).Parentas ContextMenu).IsOpen= false;

 

    try

    {

        ShellTile.Create(tileUri, initialData);

    }

    catch (Exceptionex)

    {

        MessageBox.Show(ex.Message, "Error creating tile", MessageBoxButton.OK);

        return;

    }

}

 

private voidListBox_SelectionChanged(object sender,SelectionChangedEventArgs e)

{

    if (e.AddedItems.Count != 0)

    {

         (sender as ListBox).SelectedIndex= -1;

       NavigationService.Navigate(MakeTileUri(e.AddedItems[0] as LocationInformation));

    }

}

 

private voidChangeMainTile_Click(object sender,RoutedEventArgs e)

{

    // Get the main tile (it will always be available, even if

    // not pinned)

    ShellTile mainTile = ShellTile.ActiveTiles.FirstOrDefault(

        t =>t.NavigationUri.ToString() == "/");

    StandardTileData newData = new StandardTileData()

    {

       BackgroundImage = new Uri(String.Format("Images/MainTile/{0}.png"

,(listMainTileImage.SelectedItem as

                ListPickerItem).Content),UriKind.Relative),

        Title =txtMainTileTitle.Text

    };

 

   mainTile.Update(newData);

}

前兩個方法會控制上下文菜單,控制我們添加在新主界面上的位置信息入口點。這些允許pin或者un-pin位置信息顯示到開始頁面中,以顯示當前狀態。“ListBox_SelectionChanged”方法,在用戶在主界面上選擇了之後,會導航到指定的位置頁面。“ChangeMainTile_Click”方法會改變應用程序的主Tile,按照在主界面上選擇模式進行顯示。

15.   按 F5 編譯和運行客戶端應用程序和WPF服務器。新的主界面應該看起來如下所示:

Figure 40

The new main page

16.   按住一個未顯示在開始界面上的位置(在右側的checkbox裏沒有標記顯示),打開菜單允許該位置顯示在開始界面上。完成操作之後,界面會立刻顯示開始界面,新的Tile將顯示出來。這些Tile會指向“CityPage”頁面:

Figure 41

Opening a location’s context menu

Figure 42

A sub-tile pinned to the start area

17.   按Back 硬件按鈕 (ß) 回到應用程序中,使用界面下方的新控件來操作應用程序的主Tile:

Figure 43

Updating the main tile

Figure 44

The start area after updating theapplication’s main tile

18.   停止客戶端應用程序和服務器端的運行。

 

任務 3 – 添加特定地域頁面並更新服務器

我們現在需要添加一個新的頁面來查看特定位置的信息(與原有主界面相似),並更新服務器端,使其能夠發送增強的tile 和 toast notifications。

1.      在Converters文件夾中添加新的類NameToImageConverter 。打開NameToImageConverter.cs文件,添加using聲明:

C#

using System.Windows.Data;

2.       將NameToImageConverter 類添加下列代碼:

C#

public class NameToImageConverter : IValueConverter

{

    #regionIValueConverter Members

 

    public object Convert(object value, Type targetType,

    object parameter,

    System.Globalization.CultureInfo culture)

    {

    return new BitmapImage(new Uri(String.Format(

    "/Images/{0}.png", value), UriKind.Relative));

    }

 

    public object ConvertBack(object value,TypetargetType,

    object parameter,

    System.Globalization.CultureInfo culture)

    {

        throw new NotImplementedException();

    }

 

    #endregion

}

該類將天氣情況的名稱轉換爲一個對應的圖片。

3.       在WPPushNotification.TestClient工程中,添加新的頁面,右鍵單擊工程,然後選擇Add| New Item…

4.       添加一個新的portrait page,命名爲CityPage:

Figure 45

Addinga new portrait page

5.       在CityPage.xaml中,在頁面元素中添加命名空間:

XAML

<phone:PhoneApplicationPage

    x:Class="WPPushNotification.TestClient.CityPage"

    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

    xmlns:phone="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone"

    xmlns:shell="clr-namespace:Microsoft.Phone.Shell;assembly=Microsoft.Phone"

    xmlns:conv="clr-namespace:WPPushNotification.TestClient.Converters"

    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"

    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"

    mc:Ignorable="d" d:DesignWidth="480" d:DesignHeight="768"

    FontFamily="{StaticResourcePhoneFontFamilyNormal}"

    FontSize="{StaticResourcePhoneFontSizeNormal}"

    Foreground="{StaticResourcePhoneForegroundBrush}"

    SupportedOrientations="Portrait"Orientation="Portrait"

    shell:SystemTray.IsVisible="True">

6.       改變 “LayoutRoot” grid的內容:

XAML

<!--LayoutRoot is theroot grid where all page content is placed-->

    <Grid x:Name="LayoutRoot"Background="Transparent">

        <Grid.Resources>

            <conv:NameToImageConverter x:Key="NameToImageConverter"/>

        </Grid.Resources>

        <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="WEATHERSERVICE" x:Name="textBlockPageTitle" Style="{StaticResourcePhoneTextPageTitle1Style}" />

        </Grid>

 

        <Grid Grid.Row="1" x:Name="ContentPanel" Background="#10000000">

        <TextBlock x:Name="textBlockListTitle" FontFamily="Segoe WPLight" FontSize="108" Text="{Binding Name}" Margin="20,10,0,0"/>

        <TextBlock x:Name="txtTemperature" FontFamily="SegoeWP" FontSize="160" Text="{Binding Temperature}" Margin="20,100,0,0" />

        <Image x:Name="imgWeatherConditions" Width="128" Height="128" Stretch="None" HorizontalAlignment="Right" VerticalAlignment="Top" Margin="20,155,20,0" Source="{Binding ImageName, Converter={        NameToImageConverter}}"/>

        </Grid>

 

        <StackPanel Grid.Row="3" x:Name="StatusStackPanel" Margin="20">

            <TextBlock FontSize="34" FontFamily="SegoeWP Semibold" Foreground="#104f6f" Text="Status" Style="{StaticResourcePhoneTextNormalStyle}" />

            <TextBlock x:Name="txtStatus"DataContext="{StaticResource PushStatus}" Text="{Binding Message}" FontFamily="SegoeWP" FontSize="24" Foreground="#0a364c" Margin="0,0,0,0" Style="{StaticResourcePhoneTextNormalStyle}" TextWrapping="Wrap"/>

       </StackPanel>

     </Grid>

7.       打開 CityPage.xaml.cs ,並將 CityPage 類改爲如下代碼:

C#

public partial class CityPage : PhoneApplicationPage

{

    // Constructor

    public CityPage()

    {

       InitializeComponent();

    }

 

 

    protected override void OnNavigatedTo(
        System.Windows.Navigation.NavigationEventArgse)

    {

        DataContext= (App.Current as App).Locations[

                   NavigationContext.QueryString["location"]];

        base.OnNavigatedTo(e);

    }

}

這將地理信息位置作爲導航URI的查詢參數,設置頁面數據源。

8.       我們完成了客戶端的添加,現在來修改服務器端。打開WPPushNotification.ServerSideWeatherSimulator工程中的MainWindow.xaml.cs ,定位在sendToast方法,並改爲下面的代碼:

C#

private void sendToast()

{

    string msg =txtToastMessage.Text;

    txtToastMessage.Text = "";

    List<Uri> subscribers = RegistrationService.GetSubscribers();

 

    toastPushNotificationMessage.Title = String.Format("WEATHER ALERT

    ({0})", cmbLocation.SelectedValue);

   toastPushNotificationMessage.SubTitle = msg;

   toastPushNotificationMessage.TargetPage =
        MakeTileUri(cmbLocation.SelectedValue.ToString()).ToString();

 

    subscribers.ForEach(uri => toastPushNotificationMessage.

        SendAsync(uri,

         (result) =>

            OnMessageSent(NotificationType.Toast, result),

         (result) => { }));

}

This alteredmethod will cause toast messages to lead to the page of the location selectedwhen the message was sent from the server.該方法將會發送Toast消息,到根據地理位置信息選擇的頁面。

9.       發現 sendTile 方法,並將其改爲下面的代碼:

C#

private void sendTile()

{

    stringweatherType = cmbWeather.SelectedValue as string;

    inttemperature = (int)(sld.Value + 0.5);

    stringlocation = cmbLocation.SelectedValue as string;

    List<Uri> subscribers = RegistrationService.GetSubscribers();

 

    tilePushNotificationMessage.BackgroundImageUri=

        new Uri("/Images/"+ weatherType + ".png",

        UriKind.Relative);

    tilePushNotificationMessage.Count =temperature;

    tilePushNotificationMessage.Title =location;

    tilePushNotificationMessage.SecondaryTile =

    MakeTileUri(location).ToString();

 

    subscribers.ForEach(uri =>tilePushNotificationMessage.

        SendAsync(uri,

         (result) =>

            OnMessageSent(NotificationType.Token, result),

         (result) => { }));

}

新版本的方法將會發送tile notification到sub-tile來匹配通知的指定位置。

10.   定位sendRemoteTile 方法,改變爲下列代碼:

C#

private void sendRemoteTile()

{

    List<Uri> subscribers = RegistrationService.GetSubscribers();

 

    tilePushNotificationMessage.BackgroundImageUri= new Uri(

    "http://www.larvalabs.com/user_images/screens_thumbs/12555452181.jpg");

    tilePushNotificationMessage.SecondaryTile = null;

   tilePushNotificationMessage.Title = null;

   tilePushNotificationMessage.Count = 0;

 

    subscribers.ForEach(uri =>

        tilePushNotificationMessage.SendAsync(uri,

         (result) =>

            OnMessageSent(NotificationType.Token, result),

         (result) => { }));

}

當該方法仍舊是相同的方式,當發送Tile消息時,成員變量需要被重置。

11.   最後,爲服務器添加下列方法:

C#

private staticUri MakeTileUri(stringlocationName)

{

    return new Uri(Uri.EscapeUriString(String.Format(

        "/CityPage.xaml?location={0}",

               locationName)), UriKind.Relative);

}

該方法是我們之前添加在客戶端應用程序上的副本,用來以同樣的方式構造URI。

12.  按下 F5 編譯並運行客戶端應用程序和WPF服務器。在windowsphone模擬器中,點擊Windows物理按鈕,來顯示開始界面。然後試着從WPF服務器,發送 tilenotifications。當你發送一個帶有地理信息的tile notification,開始界面中的Tile也會根據相應的信息來更新:

Figure 46

A sub-tile after being updated by a tilenotification

13.   試圖發送toast notification,並按下按鈕:

Figure 47

A toast notification

Figure 48

Clicking the toast notification

14.  發送一些關於顯示位置信息的Raw 數據:

Figure 49

The specific location page displayinginformation

提示: 你也許希望有一個特別的位置頁面來通過位置的sub-tile,顯示指定的信息。事實上真實的應用程序也會這樣來工作。事實上,爲了簡介的目的,這個功能在客戶端和服務器端被省略掉了。爲了支持這些功能,客戶端應用程序必須能夠主動從服務器端,請求最新的數據。該實驗安裝目錄會包含Source\Bonus文件夾,來實現這個功能,但是我們沒有在實驗中實現這個功能。

 

在本次課程中,您學習了關於Windows® Phone 7上推送通知服務的知識。您瞭解了Notifications類型,如何通過Microsoft PushNotification Service準備併發送它們。您創建了商務程序的客戶端應用程序,準備發送上述消息給Windows® Phone 7客戶端應用程序。Windows® Phone 7客戶端應用程序會訂閱notifications,在接收到這些消息時負責更新用戶界面。您也利用了Windows® Phone 7.1的新功能,來管理應用程序的sub-tiles,併發送deep-toastnotifications。


發佈了23 篇原創文章 · 獲贊 8 · 訪問量 4萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章