教程:使用 Azure 通知中心向特定 Windows Phone 设备推送通知Tutorial: Push notifications to specific Windows Phone devices by using Azure Notification Hubs

本教程演示如何使用 Azure 通知中心将推送通知发送到特定 Windows Phone 8 或 Windows Phone 8.1 设备。This tutorial shows you how to use Azure Notification Hubs to send push notifications to specific Windows Phone 8 or Windows Phone 8.1 devices. 如果要以 Windows Phone 8.1(非 Silverlight)为目标,请参阅本教程的 Windows Universal 版本。If you are targeting Windows Phone 8.1 (non-Silverlight), see the Windows Universal version of this tutorial.

在通知中心创建注册时,请通过加入一个或多个标记 来启用此方案。You enable this scenario by including one or more tags when creating a registration in the notification hub. 将通知发送到标记时,已注册该标记的所有设备将接收通知。When notifications are sent to a tag, all devices that have registered for the tag receive the notification. 有关标记的详细信息,请参阅注册中的标记For more information about tags, see Tags in registrations.

Note

通知中心 Windows Phone SDK 不支持将 Windows 推送通知服务 (WNS) 与 Windows Phone 8.1 Silverlight 应用配合使用。The Notification Hubs Windows Phone SDK does not support using the Windows Push Notification Service (WNS) with Windows Phone 8.1 Silverlight apps. 若要将 WNS(而不是 MPNS)与 Windows Phone 8.1 Silverlight 应用配合使用,请遵循使用 REST API 的[通知中心 - Windows Phone Silverlight 教程]。To use WNS (instead of MPNS) with Windows Phone 8.1 Silverlight apps, follow the [Notification Hubs - Windows Phone Silverlight tutorial], which uses REST APIs.

本教程介绍如何执行下列操作:In this tutorial, you learn how to:

  • 向移动应用添加类别选择Add category selection to the mobile app
  • 注册带标记的通知Register for notifications with tags
  • 发送带标记的通知Send tagged notifications
  • 测试应用程序Test the app

先决条件Prerequisites

完成教程:使用 Azure 通知中心向 Windows Phone 应用推送通知Complete the Tutorial: Push notifications to Windows Phone apps by using Azure Notification Hubs. 在本教程中,请更新移动应用程序,以便注册感兴趣的突发新闻类别,只接收这些类别的推送通知。In this tutorial, you update the mobile application so that you can register for breaking news categories you are interested in, and receive only push notifications for those categories.

向移动应用添加类别选择Add category selection to the mobile app

第一步是向现有主页添加 UI 元素,这些元素允许用户选择要注册的类别。The first step is to add the UI elements to your existing main page that enable the user to select categories to register. 用户选择的类别存储在设备上。The categories selected by a user are stored on the device. 应用程序启动时,使用所选类别作为标签在通知中心创建设备注册。When the app starts, a device registration is created in your notification hub with the selected categories as tags.

  1. 打开 MainPage.xaml 文件,并使用以下代码替换名为 TitlePanelContentPanelGrid 元素:Open the MainPage.xaml file, then replace the Grid elements named TitlePanel and ContentPanel with the following code:

    <StackPanel x:Name="TitlePanel" Grid.Row="0" Margin="12,17,0,28">
        <TextBlock Text="Breaking News" Style="{StaticResource PhoneTextNormalStyle}" Margin="12,0"/>
        <TextBlock Text="Categories" Margin="9,-7,0,0" Style="{StaticResource PhoneTextTitle1Style}"/>
    </StackPanel>
    
    <Grid Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
        <Grid.RowDefinitions>
            <RowDefinition Height="auto"/>
            <RowDefinition Height="auto" />
            <RowDefinition Height="auto" />
            <RowDefinition Height="auto" />
        </Grid.RowDefinitions>
        <Grid.ColumnDefinitions>
            <ColumnDefinition />
            <ColumnDefinition />
        </Grid.ColumnDefinitions>
        <CheckBox Name="WorldCheckBox" Grid.Row="0" Grid.Column="0">World</CheckBox>
        <CheckBox Name="PoliticsCheckBox" Grid.Row="1" Grid.Column="0">Politics</CheckBox>
        <CheckBox Name="BusinessCheckBox" Grid.Row="2" Grid.Column="0">Business</CheckBox>
        <CheckBox Name="TechnologyCheckBox" Grid.Row="0" Grid.Column="1">Technology</CheckBox>
        <CheckBox Name="ScienceCheckBox" Grid.Row="1" Grid.Column="1">Science</CheckBox>
        <CheckBox Name="SportsCheckBox" Grid.Row="2" Grid.Column="1">Sports</CheckBox>
        <Button Name="SubscribeButton" Content="Subscribe" HorizontalAlignment="Center" Grid.Row="3" Grid.Column="0" Grid.ColumnSpan="2" Click="SubscribeButton_Click" />
    </Grid>
    
  2. 向项目添加名为 Notifications 的类。Add a class named Notifications to the project. 向类定义添加 public 修饰符。Add the public modifier to the class definition. 然后,向新文件添加以下 using 语句:Then, add the following using statements to the new file:

    using Microsoft.Phone.Notification;
    using Microsoft.WindowsAzure.Messaging;
    using System.IO.IsolatedStorage;
    using System.Windows;
    
  3. 将以下代码复制到新的 Notifications 类中:Copy the following code into the new Notifications class:

    private NotificationHub hub;
    
    // Registration task to complete registration in the ChannelUriUpdated event handler
    private TaskCompletionSource<Registration> registrationTask;
    
    public Notifications(string hubName, string listenConnectionString)
    {
        hub = new NotificationHub(hubName, listenConnectionString);
    }
    
    public IEnumerable<string> RetrieveCategories()
    {
        var categories = (string)IsolatedStorageSettings.ApplicationSettings["categories"];
        return categories != null ? categories.Split(',') : new string[0];
    }
    
    public async Task<Registration> StoreCategoriesAndSubscribe(IEnumerable<string> categories)
    {
        var categoriesAsString = string.Join(",", categories);
        var settings = IsolatedStorageSettings.ApplicationSettings;
        if (!settings.Contains("categories"))
        {
            settings.Add("categories", categoriesAsString);
        }
        else
        {
            settings["categories"] = categoriesAsString;
        }
        settings.Save();
    
        return await SubscribeToCategories();
    }
    
    public async Task<Registration> SubscribeToCategories()
    {
        registrationTask = new TaskCompletionSource<Registration>();
    
        var channel = HttpNotificationChannel.Find("MyPushChannel");
    
        if (channel == null)
        {
            channel = new HttpNotificationChannel("MyPushChannel");
            channel.Open();
            channel.BindToShellToast();
            channel.ChannelUriUpdated += channel_ChannelUriUpdated;
    
            // This is optional, used to receive notifications while the app is running.
            channel.ShellToastNotificationReceived += channel_ShellToastNotificationReceived;
        }
    
        // If channel.ChannelUri is not null, complete the registrationTask here.  
        // If it is null, the registrationTask will be completed in the ChannelUriUpdated event handler.
        if (channel.ChannelUri != null)
        {
            await RegisterTemplate(channel.ChannelUri);
        }
    
        return await registrationTask.Task;
    }
    
    async void channel_ChannelUriUpdated(object sender, NotificationChannelUriEventArgs e)
    {
        await RegisterTemplate(e.ChannelUri);
    }
    
    async Task<Registration> RegisterTemplate(Uri channelUri)
    {
        // Using a template registration to support notifications across platforms.
        // Any template notifications that contain messageParam and a corresponding tag expression
        // will be delivered for this registration.
    
        const string templateBodyMPNS = "<wp:Notification xmlns:wp=\"WPNotification\">" +
                                            "<wp:Toast>" +
                                                "<wp:Text1>$(messageParam)</wp:Text1>" +
                                            "</wp:Toast>" +
                                        "</wp:Notification>";
    
        // The stored categories tags are passed with the template registration.
    
        registrationTask.SetResult(await hub.RegisterTemplateAsync(channelUri.ToString(),
            templateBodyMPNS, "simpleMPNSTemplateExample", this.RetrieveCategories()));
    
        return await registrationTask.Task;
    }
    
    // This is optional. It is used to receive notifications while the app is running.
    void channel_ShellToastNotificationReceived(object sender, NotificationEventArgs e)
    {
        StringBuilder message = new StringBuilder();
        string relativeUri = string.Empty;
    
        message.AppendFormat("Received Toast {0}:\n", DateTime.Now.ToShortTimeString());
    
        // Parse out the information that was part of the message.
        foreach (string key in e.Collection.Keys)
        {
            message.AppendFormat("{0}: {1}\n", key, e.Collection[key]);
    
            if (string.Compare(
                key,
                "wp:Param",
                System.Globalization.CultureInfo.InvariantCulture,
                System.Globalization.CompareOptions.IgnoreCase) == 0)
            {
                relativeUri = e.Collection[key];
            }
        }
    
        // Display a dialog of all the fields in the toast.
        System.Windows.Deployment.Current.Dispatcher.BeginInvoke(() =>
        {
            MessageBox.Show(message.ToString());
        });
    }
    

    此类使用隔离存储区存储此设备要接收的新闻类别。This class uses the isolated storage to store the categories of news that this device is to receive. 它还包含用于通过模板通知注册来注册这些类别的方法。It also contains methods to register for these categories using a template notification registration.

  4. App.xaml.cs 项目文件中,将以下属性添加到 App 类。In the App.xaml.cs project file, add the following property to the App class. <hub name><connection string with listen access> 占位符替换为通知中心名称和前面获取的 DefaultListenSharedAccessSignature 的连接字符串 。Replace the <hub name> and <connection string with listen access> placeholders with your notification hub name and the connection string for DefaultListenSharedAccessSignature that you obtained earlier.

    public Notifications notifications = new Notifications("<hub name>", "<connection string with listen access>");
    

    Note

    由于使用客户端应用程序分发的凭据通常是不安全的,只应使用客户端应用程序分发具有侦听访问权限的密钥。Because credentials that are distributed with a client app are not generally secure, you should only distribute the key for listen access with your client app. 侦听访问权限允许应用程序注册通知,但是无法修改现有注册,也无法发送通知。Listen access enables your app to register for notifications, but existing registrations cannot be modified and notifications cannot be sent. 在受保护的后端服务中使用完全访问权限密钥,以便发送通知和更改现有注册。The full access key is used in a secured backend service for sending notifications and changing existing registrations.

  5. MainPage.xaml.cs 中,添加以下行:In the MainPage.xaml.cs, add the following line:

    using Windows.UI.Popups;
    
  6. 在 MainPage.xaml.cs 项目文件中,添加以下方法:In the MainPage.xaml.cs project file, add the following method:

    private async void SubscribeButton_Click(object sender, RoutedEventArgs e)
    {
        var categories = new HashSet<string>();
        if (WorldCheckBox.IsChecked == true) categories.Add("World");
        if (PoliticsCheckBox.IsChecked == true) categories.Add("Politics");
        if (BusinessCheckBox.IsChecked == true) categories.Add("Business");
        if (TechnologyCheckBox.IsChecked == true) categories.Add("Technology");
        if (ScienceCheckBox.IsChecked == true) categories.Add("Science");
        if (SportsCheckBox.IsChecked == true) categories.Add("Sports");
    
        var result = await ((App)Application.Current).notifications.StoreCategoriesAndSubscribe(categories);
    
        MessageBox.Show("Subscribed to: " + string.Join(",", categories) + " on registration id : " +
            result.RegistrationId);
    }
    

    此方法创建一个类别列表并使用 Notifications 类将该列表存储在本地存储中,将相应的标签注册到通知中心。This method creates a list of categories and uses the Notifications class to store the list in the local storage and register the corresponding tags with your notification hub. 更改类别时,使用新类别重新创建注册。When categories are changed, the registration is recreated with the new categories.

应用程序现在可以将一组类别存储在设备的本地存储区中了,每当用户更改所选类别时,会将这些类别注册到通知中心。Your app is now able to store a set of categories in local storage on the device and register with the notification hub whenever the user changes the selection of categories.

注册通知Register for notifications

这些步骤用于在启动时会在本地存储区中存储的类别注册到通知中心。These steps register with the notification hub on startup using the categories that have been stored in local storage.

Note

由于 Microsoft 推送通知服务 (MPNS) 分配的通道 URI 随时可能更改,因此你应该经常注册通知以避免通知失败。Because the channel URI assigned by the Microsoft Push Notification Service (MPNS) can change at any time, you should register for notifications frequently to avoid notification failures. 此示例在每次应用程序启动时注册通知。This example registers for notifications every time that the app starts. 对于经常运行(一天一次以上)的应用程序,如果每次注册间隔时间不到一天,可以跳过注册来节省带宽。For apps that are run frequently, more than once a day, you can probably skip registration to preserve bandwidth if less than a day has passed since the previous registration.

  1. 打开 App.xaml.cs 文件,将 async 修饰符添加到 Application_Launching 方法,并将在通知中心入门中添加的通知中心注册代码替换为以下代码:Open the App.xaml.cs file and add the async modifier to Application_Launching method and replace the Notification Hubs registration code that you added in Get started with Notification Hubs with the following code:

    private async void Application_Launching(object sender, LaunchingEventArgs e)
    {
        var result = await notifications.SubscribeToCategories();
    
        if (result != null)
            System.Windows.Deployment.Current.Dispatcher.BeginInvoke(() =>
            {
                MessageBox.Show("Registration Id :" + result.RegistrationId, "Registered", MessageBoxButton.OK);
            });
    }
    

    此代码确保每次应用程序启动时,它从本地存储检索类别并请求注册这些类别。This code makes sure that every time the app starts it retrieves the categories from local storage and requests a registration for these categories.

  2. 在 MainPage.xaml.cs 项目文件中,添加实现 OnNavigatedTo 方法的以下代码:In the MainPage.xaml.cs project file, add the following code that implements the OnNavigatedTo method:

    protected override void OnNavigatedTo(NavigationEventArgs e)
    {
        var categories = ((App)Application.Current).notifications.RetrieveCategories();
    
        if (categories.Contains("World")) WorldCheckBox.IsChecked = true;
        if (categories.Contains("Politics")) PoliticsCheckBox.IsChecked = true;
        if (categories.Contains("Business")) BusinessCheckBox.IsChecked = true;
        if (categories.Contains("Technology")) TechnologyCheckBox.IsChecked = true;
        if (categories.Contains("Science")) ScienceCheckBox.IsChecked = true;
        if (categories.Contains("Sports")) SportsCheckBox.IsChecked = true;
    }
    

    此代码基于以前保存的类别状态更新主页。This code updates the main page based on the status of previously saved categories.

应用程序现在已完成,可以在设备的本地存储区中存储一组类别了,每当用户更改所选类别时会使用这些类别注册到通知中心。The app is now complete and can store a set of categories in the device local storage used to register with the notification hub whenever the user changes the selection of categories. 接下来,请定义一个后端,以便将类别通知发送到此应用。Next, define a backend that can send category notifications to this app.

发送带标记的通知Send tagged notifications

本部分说明如何从 .NET 控制台应用以标记模板通知的形式发送突发新闻。In this section, you send breaking news as tagged template notifications from a .NET console app.

  1. 在 Visual Studio 中创建新的 Visual C# 控制台应用程序:a.In Visual Studio, create a new Visual C# console application: a. 在菜单中,选择“文件” > “新建” > “项目” 。On the menu, select File > New > Project. b.b. 展开“Visual C#”,然后选择“Windows 桌面” 。Expand Visual C#, and select Windows Desktop. c.c. 在模板列表中选择“控制台应用(.NET Framework)” 。Select Console App (.NET Framework) in the list of templates. d.d. 输入应用的名称Enter a name for the app. e.e. 为应用选择文件夹Select a folder for the app. f.f. 选择“确定”创建该项目。 Select OK to create the project.

  2. 在 Visual Studio 主菜单中,选择“工具” > “NuGet 包管理器” > “包管理器控制台”,并在控制台窗口中输入以下字符串 :On the Visual Studio main menu, select Tools > NuGet Package Manager > Package Manager Console and then, in the console window, enter the following string:

    Install-Package Microsoft.Azure.NotificationHubs
    
  3. EnterSelect Enter.
    此操作会使用 Microsoft.Azure.Notification Hubs NuGet 包添加对 Azure 通知中心 SDK 的引用。This action adds a reference to the Azure Notification Hubs SDK by using the Microsoft.Azure.Notification Hubs NuGet package.

  4. 打开文件 Program.cs 并添加以下 using 语句:Open the Program.cs file, and add the following using statement:

    using Microsoft.Azure.NotificationHubs;
    
  5. Program 类中,添加以下方法,或替换此方法(如果已存在):In the Program class, add the following method, or replace it if it already exists:

    private static async void SendTemplateNotificationAsync()
    {
        // Define the notification hub.
        NotificationHubClient hub = NotificationHubClient.CreateClientFromConnectionString("<connection string with full access>", "<hub name>");
    
        // Create an array of breaking news categories.
        var categories = new string[] { "World", "Politics", "Business", "Technology", "Science", "Sports"};
    
        // Send the notification as a template notification. All template registrations that contain
        // "messageParam" and the proper tags will receive the notifications.
        // This includes APNS, WNS, and MPNS template registrations.
    
        Dictionary<string, string> templateParams = new Dictionary<string, string>();
    
        foreach (var category in categories)
        {
            templateParams["messageParam"] = "Breaking " + category + " News!";
            await hub.SendTemplateNotificationAsync(templateParams, category);
        }
    }
    

    此代码针对字符串数组中的所有 6 个标记发送模板通知。This code sends a template notification for each of the six tags in the string array. 使用标记是为了确保设备仅接收已注册类别的通知。The use of tags ensures that devices receive notifications only for the registered categories.

  6. 在前面的代码中,将 <hub name><connection string with full access> 占位符替换为通知中心名称和从通知中心仪表板获取的 DefaultFullSharedAccessSignature 的连接字符串。In the preceding code, replace the <hub name> and <connection string with full access> placeholders with your notification hub name and the connection string for DefaultFullSharedAccessSignature from the dashboard of your notification hub.

  7. Main 方法中添加以下行:In the Main method, add the following lines:

    SendTemplateNotificationAsync();
    Console.ReadLine();
    
  8. 生成控制台应用。Build the console app.

测试应用程序Test the app

  1. 在 Visual Studio 中,按 F5 编译并启动应用程序。In Visual Studio, press F5 to compile and start the app.

    包含类别的移动应用

    应用 UI 提供了一组开关,可以使用它们选择要订阅的类别。The app UI provides a set of toggles that lets you choose the categories to subscribe to.

  2. 启用一个或多个类别开关,然后单击“订阅” 。Enable one or more categories toggles, then click Subscribe.

    应用程序将所选类别转换为标签并针对所选标签从通知中心请求注册新设备。The app converts the selected categories into tags and requests a new device registration for the selected tags from the notification hub. 返回注册的类别并显示在对话框中。The registered categories are returned and displayed in a dialog.

    订阅的消息

  3. 在接收类别已完成订阅的确认后,运行控制台应用以发送每个类别的通知。After receiving a confirmation that your categories were subscription completed, run the console app to send notifications for each category. 确认你只会收到订阅的类别的通知。Verify you only receive a notification for the categories you have subscribed to.

    通知消息

后续步骤Next steps

本教程介绍了如何向其标记与注册相关联的特定设备推送通知。In this tutorial, you learned how to push notifications to specific devices that have tags associated with their registrations. 若要了解如何向可能会使用多个设备的特定用户推送通知,请转到以下教程:To learn how to push notifications to specific users who may be using multiple devices, advance to the following tutorial: