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万+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章