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

实现消息推送机制代码 已经实现的功能: 1.服务器定时推送(多个机器测试通过) 2.服务器端能够撤销已经发送的推送通知 3.服务器端实时发送消息(多个机器) 4.客户端接受推送,并且不能强制关掉 5.客户端缩小到任务栏小图标,不能被关闭 6.客户端一旦接收到了推送消息,无法切换到其他windows界面去工作,除非点击“确认已阅”按钮 立即下载

 

 

 

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.   创建新的辅助方法: Subscribe 和 Unsubscribe, 参考下列代码:

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.文件中,为Register和Unregister方法中增加一个断点。

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.TestClient和 WPPushNotification.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下拉菜单中,设置 PushNotifications和 Weather 工程为 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。


2021-8-14 EDK II编译 2021-8-14 EDK II 编译为什么要写这篇文章新的改变功能快捷键合理的创建标题,有助于目录的生成如何改变文本的样式插入链接与图片如何插入一段漂亮的代码片生成一个适合你的列表创建一个表格设定内容居中、居左、居右SmartyPants创建一个自定义列表如何创建一个注脚注释也是必不可少的KaTeX数学公式新的甘特图功能,丰富你的文章UML 图表FLowchart流程图导出与导入导出导入 为什么要写这篇文章 你好! 这是你第一次使用 Markdown编辑器 所展示的欢迎页。如果你想学习如何使用Markdo 阅读详情

相关推荐

牛顿法vs高斯牛顿法vs LM算法:三行代码说清核心区别(附性能对比表格)

本文通过对比牛顿法、高斯牛顿法和LM算法的核心迭代代码,揭示了三者在非线性最小二乘优化中的本质区别。LM算法通过引入阻尼因子,在高斯牛顿法的快速收敛与梯度下降的稳定性之间取得平衡,成为工程实践中鲁棒性最强的首选。文章还提供了清晰的性能对比表格,帮助开发者根据初始估计和问题性质快速选择合适算法。

weixin_29062255的博客 418

hbuilder 个推 问题记录

截止版本日期: 2017/09/19 一,安卓没有角标问题:   Android - 2.2+ (支持): 目前仅支持小米(MIUI v5),其它设备调用后无任何效果。官方文档 二,ios角标混乱问题:   1,iOS - 4.3+ (支持): 应用需开启“Push Notifications”服务才生效。   2,推荐使用服务端同步ios角标:关于ios角标同步服务器的问题...

weixin_33862041的博客 537

Circos图审美拯救计划:用TBtools复刻Nature级共线性分析图谱

本文针对Circos图在共线性分析中的审美提升问题,提供了一套基于TBtools的完整优化方案。通过构建和谐的色彩体系、精细控制信息层布局以及模仿顶刊构图逻辑,详细讲解了如何从配色、透明度、标签显示到高级参数设置,将默认图表升级为具有高级学术美感的Nature级图谱,助力研究者提升论文图表质量。

cherry的博客 910

2025终极指南:全平台推送测试神器PushNotifications实战教程

你是否还在为跨平台推送通知测试焦头烂额?证书过期导致推送失败、令牌配置流程繁琐、多环境切换复杂、调试信息不明确——这些问题耗费开发者70%的调试时间。PushNotifications作为一款开源跨平台推送测试工具,彻底解决了iOS/Android双平台、证书/令牌双认证、多环境快速切换的痛点。本文将带你从0到1掌握这款工具的安装配置、高级功能与最佳实践,让推送测试效率提升10倍。 读完本文你将...

gitblog_00370的博客 945

【AI 编程工具进阶用法】如何在Cursor、Trae等工具中使用Skills

OpenSkills是一个开源项目,用于在Cursor、Trae等AI编程工具中添加可复用、可组合的"能力模块"。安装流程包括:1)全局安装OpenSkills CLI工具;2)选择安装Anthropic官方Skills到项目或全局;3)创建AGENTS.md文件同步Skills;4)在AI工具中调用Skills。此外还介绍了UI/UX Pro Max Skill项目,通过npm安装后可为AI添加设计能力。这些Skills相当于为AI工具安装"高级技能包"

肌肉金轮的博客 3540

stm32最小系统_完成一个最小FOC矢量控制系统所需的基本模块和功能配置

当我们读懂 FOC 矢量控制的基本原理之后,便迫不及待的想动手尝试,去实现一个矢量控制系统,让电机先转起来,有一个直观的感受。因此,我们需要设计实现一个矢量控制的最小系统,具备矢量控制的基本功能,满足电机矢量运行的基本条件。首先,看一下矢量控制的基本架构:矢量控制架构如上图所描述,矢量控制系统包括实时电流的采集、clarke 变换、park 变换、SVPWM 、实时角度的反馈和计算以及电流环和速...

weixin_39524048的博客 1782

push notification

push是推,按的意思,notification是通知,警告的意思。合起来就是推送通知的意思,相当于手机即时通讯工具,就像网络上我们的即时通讯工具QQ,MSN等Push Notification Service短信功能是苹果花费一年时间开发的iPhone新功能,利用Apple Push Notification Service时,即使在iPhone上的应用软件退出运行之后,苹果运营的服务器仍可与个

passtome的专栏 1614

RT-Thread NANO V4.1.1 在Cortex-M0上移植踩坑记:解决内存分配宏导致的线程创建失败

本文详细记录了RT-Thread NANO V4.1.1在Cortex-M0平台移植过程中遇到的内存分配宏配置问题,导致线程创建失败的解决方案。通过分析内存管理算法宏定义,将RT_USING_SMALL_MEM替换为RT_USING_SMALL_MEM_AS_HEAP,成功解决了线程创建问题,为开发者提供了实用的移植经验。

weixin_29189373的博客 306

关于推送push中notification的一点问题

对于推送,一般的APP都会有这种功能,这是一种拉回用户,使其重新用我们APP增强用户粘性的一种方法,所以这也是一种很常见的功能。但是之前一直没注意 ,所以也忽略了一些问题,最近正好碰到了,记录下来,防止大家再次踩坑。          因为最近的业务场景的需要,一个用户会在很短的时间内收到比较多的推送,但是在之前的应用中,其实这种业务场景不会那么多的。但是现在出现了,也出现了两个问题。那么出现的

和小胖 2498

多台树莓派(主机)通信

在做无人机实验时,有个任务需要 两台无人机 或 无人机与小车 进行通信。 由于我们飞机上用了树莓派,相当于一台电脑,索性选用Socket网络编程。 条件:树莓派、局域网(手机热点或路由器)。需要通信的两台树莓派接入同一个局域网。 如若现场没有网络,可将树莓派配置为无线路由器,发射wifi。网上很多教程,注意一点是树莓派需要先用网线接入路由器再配置。 发送数据: # -*- coding...

LNL_LNL的博客 7685

使用PyTorch和TextCNN实现英文长文本诗歌文本分类

在这个过程中,我们了解了如何进行文本数据预处理、构建TextCNN模型,并进行训练与评估。首先,我们需要将文本文件加载到内存中,并将每个诗歌作为一个样本。然后,我们需要将每个样本转换为数字表示,这可以通过将词汇表中的每个词语映射到一个唯一的整数来实现。此外,我们还需要将每个样本的长度统一为固定值,以便于模型输入。通过这个示例,我们可以学习如何构建一个简单但有效的文本分类模型,并在具体的应用场景中进行训练和预测。该模型通过使用多个不同尺寸的卷积核来捕捉文本中的不同长度的信息,并通过最大池化操作提取关键特征。

loop_syntax648的博客 254

Windows Phone 7 - Push Notification机制初探

在Windows phone 7即将问世之际,笔者想要跟大家分享一个在开发上很好用的机制:Push notification。以往,我们习惯使用Web Service或WCF service来让本地程式与远端伺服器沟通,随时随地,随心所欲的存取资料,always connect!但这个方式到了新的云端应用上,却出现了新的挑战。 首先,因为系统资源、效能与电力等考量,不支援3rd party

Working harder, getting stronger! 1197

【python数据处理】python实现vlookup字符串模糊匹配及在实战中的应用(FuzzyWuzzy库)

利用FuzzyWuzzy库匹配字符串1. 背景前言2. FuzzyWuzzy库介绍2.1 安装2.1 fuzz模块2.1.1 简单匹配(Ratio)2.1.2 非完全匹配(Partial Ratio)2.1.3 忽略顺序匹配(Token Sort Ratio)2.1.4 去重子集匹配(Token Set Ratio)2.2 process模块2.2.1 extract提取多条数据2.2.2 extractOne提取一条数据3. pandas实战应用3.1 公司名称字段模糊匹配3.1.1 参数讲解:3.1.2

lys_828的博客 8321

查看windows服务进程对应服务名称及常见服务描述

友链 tasklist /svc | findstr pid WpnService: Windows Push Notifications System Service windows消息推送服务

ma_de_hao_mei_le的博客 4647

精雕软件(JDpaint) 5.5 绿色版

精雕软件(JDpaint) 5.5 绿色版精雕软件(JDpaint) 5.5 绿色版精雕软件(JDpaint) 5.5 绿色版

大中型超市收银系统(可直接运行)

光步前台收银系统系统功能:商品销售、商品退货、挂单、收银员权限、会员销售、单品取消、整单取消、商品查询、脱机销售、脱机数据上传,基本资料下载,开钱箱、小票打印、收款结算,支持多币种付款方式(现金、储值卡、礼券、微信等),支持多种促销。运行环境:.net 4.0登陆信息用户名:1001 密码:123456内置商品信息,商品编码可输入00001000 - 00010000,测试效果相关功能键F5 结算 F9挂单 F11商品查询 F8退货 Insert单品取消 Del整单取消 F12开钱箱F2 会员 Ctrl+Z退出系统 Home 锁屏 相关源码待公开

Windows 10 非常高的已提交内存占用分析与解决

问题描述: 我最近安装了 Windows 10 ,发现我的电脑正在缓慢增加其已提交内存占用,直到达到极限。然后 PC 开始关闭进程并最终重新启动。一旦 PC 重新启动,提交占用就会回落到更正常的水平。 达到内存占满上限大约需要 2 天时间。 我在这个问题上搜索了我所能做的一切,并通过各种修复方法解决了问题,但没有一个奏效。 通过使用“Process Hacker”、Poolmon.exe 等,我将问题缩小到“splwow64.exe” 当我启动 PC 时,我有这个文件的两个实例,一个在本地帐户下运

我的博客 2万+

蓝牙模块HM-10手册

蓝牙模块HM-10手册,该手册对蓝牙模块HM-10进行了详细的介绍和使用方法以及一些AT指令。

【运维】Windows 通过注册表禁用服务

HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\{服务名称}FailureActions 第一列 10 18 这两行 对应的 第6列 01 改为 00。【运维】Windows 通过注册表禁用服务。Start 改为4 十六进制。

我是Superman丶的博客 3786

深度学习工具包 DeepLearningToolbox

深度学习工具包Deprecation notice.-----This toolbox is outdated and no longer maintained.There are much better tools available for deep learning than this toolbox, e.g. [Theano](http://deeplearning.net/software/theano/), [torch](http://torch.ch/) or [tensorflow](http://www.tensorflow.org/)I would suggest you use one of the tools mentioned above rather than use this toolbox.Best, Rasmus.DeepLearnToolbox================A Matlab toolbox for Deep Learning.Deep Learning is a new subfield of machine learning that focuses on learning deep hierarchical models of data.It is inspired by the human brain's apparent deep (layered, hierarchical) architecture.A good overview of the theory of Deep Learning theory is[Learning Deep Architectures for AI](http://www.iro.umontreal.ca/~bengioy/papers/ftml_book.pdf)For a more informal introduction, see the following videos by Geoffrey Hinton and Andrew Ng.* [The Next Generation of Neural Networks](http://www.youtube.com/watch?v=AyzOUbkUf3M) (Hinton, 2007)* [Recent Developments in Deep Learning](http://www.youtube.com/watch?v=VdIURAu1-aU) (Hinton, 2010)* [Unsupervised Feature Learning and Deep Learning](http://www.youtube.com/watch?v=ZmNOAtZIgIk) (Ng, 2011)If you use this toolbox in your research please cite [Prediction as a candidate for learning deep hierarchical models of data](http://www2.imm.dtu.dk/pubdb/views/publication_details.php?id=6284)```@MASTERSTHESIS\{IMM2012-06284, author = "R. B. Palm", title = "Prediction as a candidate for learning deep hierarchical models of data", year = "2012",}```Contact: rasmusbergpalm at gmail dot comDirectories included in the toolbox-----------------------------------`NN/` - A library for Feedforward Backpropagation Neural Networks`CNN/` - A library for Convolutional Neural Networks`DBN/` - A library for Deep Belief Networks`SAE/` - A library for Stacked Auto-Encoders`CAE/` - A library for Convolutional Auto-Encoders`util/` - Utility functions used by the libraries`data/` - Data used by the examples`tests/` - unit tests to verify toolbox is workingFor references on each library check REFS.mdSetup-----1. Download.2. addpath(genpath('DeepLearnToolbox'));Example: Deep Belief Network---------------------```matlabfunction test_example_DBNload mnist_uint8;train_x = double(train_x) / 255;test_x = double(test_x) / 255;train_y = double(train_y);test_y = double(test_y);%% ex1 train a 100 hidden unit RBM and visualize its weightsrand('state',0)dbn.sizes = [100];opts.numepochs = 1;opts.batchsize = 100;opts.momentum = 0;opts.alpha = 1;dbn = dbnsetup(dbn, train_x, opts);dbn = dbntrain(dbn, train_x, opts);figure; visualize(dbn.rbm{1}.W'); % Visualize the RBM weights%% ex2 train a 100-100 hidden unit DBN and use its weights to initialize a NNrand('state',0)%train dbndbn.sizes = [100 100];opts.numepochs = 1;opts.batchsize = 100;opts.momentum = 0;opts.alpha = 1;dbn = dbnsetup(dbn, train_x, opts);dbn = dbntrain(dbn, train_x, opts);%unfold dbn to nnnn = dbnunfoldtonn(dbn, 10);nn.activation_function = 'sigm';%train nnopts.numepochs = 1;opts.batchsize = 100;nn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.10, 'Too big error');```Example: Stacked Auto-Encoders---------------------```matlabfunction test_example_SAEload mnist_uint8;train_x = double(train_x)/255;test_x = double(test_x)/255;train_y = double(train_y);test_y = double(test_y);%% ex1 train a 100 hidden unit SDAE and use it to initialize a FFNN% Setup and train a stacked denoising autoencoder (SDAE)rand('state',0)sae = saesetup([784 100]);sae.ae{1}.activation_function = 'sigm';sae.ae{1}.learningRate = 1;sae.ae{1}.inputZeroMaskedFraction = 0.5;opts.numepochs = 1;opts.batchsize = 100;sae = saetrain(sae, train_x, opts);visualize(sae.ae{1}.W{1}(:,2:end)')% Use the SDAE to initialize a FFNNnn = nnsetup([784 100 10]);nn.activation_function = 'sigm';nn.learningRate = 1;nn.W{1} = sae.ae{1}.W{1};% Train the FFNNopts.numepochs = 1;opts.batchsize = 100;nn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.16, 'Too big error');```Example: Convolutional Neural Nets---------------------```matlabfunction test_example_CNNload mnist_uint8;train_x = double(reshape(train_x',28,28,60000))/255;test_x = double(reshape(test_x',28,28,10000))/255;train_y = double(train_y');test_y = double(test_y');%% ex1 Train a 6c-2s-12c-2s Convolutional neural network %will run 1 epoch in about 200 second and get around 11% error. %With 100 epochs you'll get around 1.2% errorrand('state',0)cnn.layers = { struct('type', 'i') %input layer struct('type', 'c', 'outputmaps', 6, 'kernelsize', 5) %convolution layer struct('type', 's', 'scale', 2) %sub sampling layer struct('type', 'c', 'outputmaps', 12, 'kernelsize', 5) %convolution layer struct('type', 's', 'scale', 2) %subsampling layer};cnn = cnnsetup(cnn, train_x, train_y);opts.alpha = 1;opts.batchsize = 50;opts.numepochs = 1;cnn = cnntrain(cnn, train_x, train_y, opts);[er, bad] = cnntest(cnn, test_x, test_y);%plot mean squared errorfigure; plot(cnn.rL);assert(er<0.12, 'Too big error');```Example: Neural Networks---------------------```matlabfunction test_example_NNload mnist_uint8;train_x = double(train_x) / 255;test_x = double(test_x) / 255;train_y = double(train_y);test_y = double(test_y);% normalize[train_x, mu, sigma] = zscore(train_x);test_x = normalize(test_x, mu, sigma);%% ex1 vanilla neural netrand('state',0)nn = nnsetup([784 100 10]);opts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samples[nn, L] = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.08, 'Too big error');%% ex2 neural net with L2 weight decayrand('state',0)nn = nnsetup([784 100 10]);nn.weightPenaltyL2 = 1e-4; % L2 weight decayopts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex3 neural net with dropoutrand('state',0)nn = nnsetup([784 100 10]);nn.dropoutFraction = 0.5; % Dropout fraction opts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex4 neural net with sigmoid activation functionrand('state',0)nn = nnsetup([784 100 10]);nn.activation_function = 'sigm'; % Sigmoid activation functionnn.learningRate = 1; % Sigm require a lower learning rateopts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex5 plotting functionalityrand('state',0)nn = nnsetup([784 20 10]);opts.numepochs = 5; % Number of full sweeps through datann.output = 'softmax'; % use softmax outputopts.batchsize = 1000; % Take a mean gradient step over this many samplesopts.plot = 1; % enable plottingnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex6 neural net with sigmoid activation and plotting of validation and training error% split training data into training and validation datavx = train_x(1:10000,:);tx = train_x(10001:end,:);vy = train_y(1:10000,:);ty = train_y(10001:end,:);rand('state',0)nn = nnsetup([784 20 10]); nn.output = 'softmax'; % use softmax outputopts.numepochs = 5; % Number of full sweeps through dataopts.batchsize = 1000; % Take a mean gradient step over this many samplesopts.plot = 1; % enable plottingnn = nntrain(nn, tx, ty, opts, vx, vy); % nntrain takes validation set as last two arguments (optionally)[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');```[![Bitdeli Badge](https://d2weczhvl823v0.cloudfront.net/rasmusbergpalm/deeplearntoolbox/trend.png)](https://bitdeli.com/free "Bitdeli Badge")

WIN10系统网络状态无法查看 右下角通知点击无反应

WIN10系统网络状态无法查看 右下角通知点击无反应 这个问题 我刚开始重启资源管理器等等操作没有起到作用 后来检查服务时候发现 是Windows Push Notifications User Service 该项服务没有启动。

aa735214858的博客 1497

caffe+ CPU + VS2013+win10安装

今年到现在还没有找到工作。凉凉,做图像不懂一点深度学习还真不好找工作,所以决定入坑深度学习,做图像貌似caffe不错,今天用vs2013编译了一下caffe,把编译过程中遇到的坑记录一下, 1.下载微软caffe文件,github较慢,百度云链接如下链接:https://pan.baidu.com/s/12ElrnoknboZZBIqWfqrJZQ 提取码:pvlj 2.解压文件将caffe—ma...

水月寒冰的博客 681

Win10不能禁和不建议禁的服务以及禁用后的影响

Win10不能禁和不建议禁的服务以及禁用后的影响 注意:适用于win10-19042.631版本,在之前的版本与此有一些差别 一、UWP简介 **应用:**即APP或程序,以前称为软件 UWP 即 “Windows通用应用平台”, 说简单一点,就是这种应用(即软件或程序)可以跨设备运行,即一个UWP应用可同时在平板、手机、电脑上运行,但是仅限于安装的win10系统的这些设备,所以,UWP只是跨设备,不能跨平台(即,在不同系统中运行,如可同时在windows、安桌、苹果上运行就叫跨平台), UWP程序

黄邦勇帅的博客 18万+

跨平台应用开发进阶(十二) :uni-app 实现应用桌面图标角标提示及应用跳转

文章目录一、前言二、实现三、遇到的问题及解决措施四、拓展阅读 一、前言 在前期实现un-app集成极光推送并完成状态栏消息通知的基础上,需要实现应用icon角标显示未读信息数量,点击图标后跳转至应用具体页面,阅读某条信息后,角标数字相应减一功能。 待实现功能点: 用户登录后,应用角标显示未读信息数量; 点击含有未读信息的应用角标,跳转至应用具体页面; 阅读某条未读信息后,应用角标数据做减一操作; 二、实现 鉴于uni-app App 端内置 HTML5+ 引擎,让 js 可以直接调用丰富的原生能力。

IT全栈 华强工作室 1万+

终极推送通知测试工具:跨平台iOS Android推送调试利器

Push Notifications是一款专为移动应用开发者设计的免费推送通知测试工具,支持macOS、Windows和Linux三大操作系统。这个跨平台推送测试解决方案让开发者能够轻松验证iOS和Android设备的推送功能,是移动应用推送验证的必备工具。 ## 🚀 核心功能亮点 ### 双平台推送测试全覆盖 Push Notifications支持iOS和Android两大主流平台的推送

gitblog_00110的博客 1171

Win10电脑右下角操作中心打开异常处理方法

计算机\HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\,找到WpnUserService项。主要是因为Windows Push Notifications User Service服务没有正常开启造成。分别对上图的注册表项对应的start的值把“4”改成“2”Win10电脑右下角操作中心点击后无任何反应,日志也报错。由于该服务是系统外壳,直接进入对该服务进行开启,会报错。通过注册表方式进行,定位到。对应找到子项start。

XO的享受博客 2277

windows内存占用过高解决方法

windows内存占用过高解决方法

m0_37616517的博客 4118

Microsoft Push Notifications初体验

从微软官网那下了个Push Notifications的codeSample,放到手机里看看Wp7的Push Notifications是啥样的。下载地址如下:http://create.msdn.com/en-US/education/catalog/sample/push_notifications首先代码分为两个工程,一个客户端(放手机里的),一个发送端(WinForm程序)先启动手机客户端,取得uri。因为uri要从Microsoft Push Notifications Service取得,所以必

chagaostu的专栏 1305

设置App桌面图标上显示的角标数字

原文 手机微信搜索公众号 设置App桌面图标上显示的角标数字原文前言:新的前置条件:正文:代码:文档地址:API参数:平台支持: 前言: 使用uni-app开发苹果应用App和安卓应用App,已经有一年半的时间了,期间一直在记录自己遇到的坑,和对知识的整理,最近有童鞋问到数字角标,那么今天就补发一下吧 新的前置条件: 开发环境:windows 开发框架:uni-app , H5+,nativeJS 编辑器:HbuilderX 兼容版本:安卓已作测试,IOS已测试 正文: 首先需要使用到 nativeJS

TS_144636的博客 6839

windows内存占用过高

Windows开机内存占用超过30%(6G/18G),Come on: Round 1 msconfig->常规:有选择的启动->服务->隐藏所有Microsoft服务,全部禁用。Ban了再说。需要的自己单独启。 发现触摸板配置没了(主要是连接外部设备时禁用触摸板的设置),查找刚刚关闭的服务,是SynTPEnhService(制造商:Synaptics),开启它。设置以后记得点击应用才会生效。 添加虚拟内存,设置->系统->关于->高级系统设置->高级-

Alezan的博客 1万+
上一篇: Hands-On Lab Photo Fun 应用程序
下一篇: Hands-On Lab 在您的应用上使用后台传输服务
funnyfox
博客等级 码龄16年 4粉丝 23原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值