发送本地 toast 通知Send a local toast notification

Toast 通知是用户当前未在应用内部时应用可构造并发送给用户的消息。A toast notification is a message that an app can construct and deliver to the user while they are not currently inside your app. 此快速入门指南将指导你借助新自适应模板和交互式操作完成创建、交付并显示 Windows 10 toast 通知的步骤。This Quickstart walks you through the steps to create, deliver, and display a Windows 10 toast notification with the new adaptive templates and interactive actions. 通过本地通知对这些操作进行说明,本地通知是实现起来最简单的通知。These actions are demonstrated through a local notification, which is the simplest notification to implement.

重要

桌面应用程序(包括桌面桥和经典 Win32)发送通知和处理激活的步骤不同。Desktop applications (both Desktop Bridge and classic Win32) have different steps for sending notifications and handling activation. 请参阅桌面 app 文档,了解如何实现 toast。Please see the Desktop apps documentation to learn how to implement toasts.

我们会演示以下内容:We will go through the following things:

发送 toastSending a toast

  • 构造通知的可视部分(文本和图像)Constructing the visual part (text and image) of the notification
  • 将操作添加到通知Adding actions to the notification
  • 设置的 toast 的过期时间Setting an expiration time on the toast
  • 设置标记/组,以便你在以后替换/删除 toastSetting tag/group so you can replace/remove the toast at a later time
  • 使用本地 API 发送你的 toastSending your toast using the local APIs

处理激活Handling activation

  • 正文或按钮被单击时处理激活Handling activation when the body or buttons are clicked
  • 处理前台激活Handling foreground activation
  • 处理后台激活Handling background activation

重要 APIToastNotification 类ToastNotificationActivatedEventArgs 类Important APIs: ToastNotification Class, ToastNotificationActivatedEventArgs Class

系统必备Prerequisites

若要完全理解此主题,事先掌握以下内容会很有用...To fully understand this topic, the following will be helpful...

备注

与 Windows 8/8.1 不同,无需在应用清单中声明应用能够显示 toast 通知。Unlike Windows 8/8.1, you no longer need to declare in your app's manifest that your app is capable of showing toast notifications. 所有应用都能发送和显示 toast 通知。All apps are capable of sending and displaying toast notifications.

备注

Windows 8/8.1 应用:请使用存档的文档Windows 8/8.1 apps: Please use the archived documentation.

安装 NuGet 程序包Install NuGet packages

我们建议你对你的项目安装以下两个 NuGet 程序包。We recommend installing the two following NuGet packages to your project. 代码示例将使用这些程序包。Our code sample will use these packages. 文章结尾部分将提供不使用任何 NuGet 程序包的“Vanilla”代码片段。At the end of the article we'll provide the "Vanilla" code snippets that don't use any NuGet packages.

添加命名空间声明Add namespace declarations

Windows.UI.Notifications 包含 toast API。includes the toast APIs.

using Windows.UI.Notifications;
using Microsoft.Toolkit.Uwp.Notifications; // Notifications library
using Microsoft.QueryStringDotNET; // QueryString.NET

发送 toastSend a toast

在 Windows 10 中,你的 toast 通知内容是使用对于你的通知外观给予了最大程度灵活性的自适应语言描述的。In Windows 10, your toast notification content is described using an adaptive language that allows great flexibility with how your notification looks. 有关详细信息,请参阅 toast 内容文档See the toast content documentation for more information.

构造内容的可视部分Constructing the visual part of the content

现在先来构造内容的可视部分,其中包括希望用户看到的文本和图像。Let's start by constructing the visual part of the content, which includes the text and images you want the user to see.

通知库使得 XML 内容的生成变得十分简单。Thanks to the the Notifications library, generating the XML content is straightforward. 如果不从 NuGet 安装通知库,则需要手动构造 XML,这样就可能出错。If you don't install the Notifications library from NuGet, you have to construct the XML manually, which leaves room for errors.

备注

图像可来自于应用包、应用的本地存储或来自 Web。Images can be used from the app's package, the app's local storage, or from the web. 自 Fall Creators Update 起,正常连接上的 Web 图像的大小限制提升至 3 MB,按流量计费的连接上的限制提升至 1 MB。As of the Fall Creators Update, web images can be up to 3 MB on normal connections and 1 MB on metered connections. 在尚未运行 Fall Creators Update 的设备上,Web 图像的大小不得超过 200 KB。On devices not yet running the Fall Creators Update, web images must be no larger than 200 KB.

// In a real app, these would be initialized with actual data
string title = "Andrew sent you a picture";
string content = "Check this out, Happy Canyon in Utah!";
string image = "https://picsum.photos/360/202?image=883";
string logo = "ms-appdata:///local/Andrew.jpg";

// Construct the visuals of the toast
ToastVisual visual = new ToastVisual()
{
    BindingGeneric = new ToastBindingGeneric()
    {
        Children =
        {
            new AdaptiveText()
            {
                Text = title
            },

            new AdaptiveText()
            {
                Text = content
            },

            new AdaptiveImage()
            {
                Source = image
            }
        },

        AppLogoOverride = new ToastGenericAppLogo()
        {
            Source = logo,
            HintCrop = ToastGenericAppLogoCrop.Circle
        }
    }
};

构造内容的操作部分Constructing actions part of the content

现在向内容中添加操作。Now let's add actions to the content.

在以下示例中,包含了允许用户输入文本的输入元素,当用户单击其中一个按钮或 toast 本身时,文本会返回到应用。In the below example, we included an input element that allows the user to input text, which is returned to the app when the user clicks one of the buttons or the toast itself.

然后,添加了两个按钮,每个按钮具有自己的激活类型、内容和参数。We then added two buttons, each with its own activation type, content, and arguments.

  • ActivationType 用于指定用户执行激活操作时要如何激活应用。ActivationType is used to specify how your app wants to be activated when this action is performed by the user. 可选择在前台启动应用、启动后台任务或通过协议启动另一个应用。You can choose to launch your app in the foreground, launch a background task, or protocol launch another app. 无论应用选择前台或后台,你始终会收到用户输入和由你指定的参数,以便应用可以执行正确的操作,如发送消息或打开对话。Whether your app chooses foreground or background, you will always receive the user input and the arguments you specified, so your app can perform the correct action, like sending the message or opening a conversation.
// In a real app, these would be initialized with actual data
int conversationId = 384928;

// Construct the actions for the toast (inputs and buttons)
ToastActionsCustom actions = new ToastActionsCustom()
{
    Inputs =
    {
        new ToastTextBox("tbReply")
        {
            PlaceholderContent = "Type a response"
        }
    },

    Buttons =
    {
        new ToastButton("Reply", new QueryString()
        {
            { "action", "reply" },
            { "conversationId", conversationId.ToString() }

        }.ToString())
        {
            ActivationType = ToastActivationType.Background,
            ImageUri = "Assets/Reply.png",

            // Reference the text box's ID in order to
            // place this button next to the text box
            TextBoxId = "tbReply"
        },

        new ToastButton("Like", new QueryString()
        {
            { "action", "like" },
            { "conversationId", conversationId.ToString() }

        }.ToString())
        {
            ActivationType = ToastActivationType.Background
        },

        new ToastButton("View", new QueryString()
        {
            { "action", "viewImage" },
            { "imageUrl", image }

        }.ToString())
    }
};

结合上述内容构造完整内容Combining the above to construct the full content

内容构造现已完成,可以使用它来实例化 ToastNotification 对象。The construction of the content is now complete, and we can use it to instantiate your ToastNotification object.

注意:你还可以在根元素内提供激活类型,指定用户点击 toast 通知的正文时需要进行哪种类型的激活。Note: you can also provide an activation type inside the root element, to specify what type of activation needs to happen when the user taps on the body of the toast notification. 通常情况下,点击 toast 的正文应该会在前台启动你的应用,以提供一致的用户体验,但你也可以使用适合你的特定应用场景的其他激活类型,以便对用户更有意义。Normally, tapping the body of the toast should launch your app in the foreground to create a consistent user experience, but you can use other activation types to fit your specific scenario where it makes most sense to the user.

应始终设置 Launch 属性,以便用户点击 toast 正文且应用启动时,应用能够知道应显示的内容。You should always set the Launch property, so when user taps the body of the toast and your app is launched, your app knows what content it should display.

// Now we can construct the final toast content
ToastContent toastContent = new ToastContent()
{
    Visual = visual,
    Actions = actions,

    // Arguments when the user taps body of toast
    Launch = new QueryString()
    {
        { "action", "viewConversation" },
        { "conversationId", conversationId.ToString() }

    }.ToString()
};

// And create the toast notification
var toast = new ToastNotification(toastContent.GetXml());

设置过期时间Set an expiration time

在 Windows 10 中,所有 toast 通知被用户消除或忽略后将转到操作中心,以便在弹出窗口消失后,用户仍可查看通知。In Windows 10, all toast notifications go in Action Center after they are dismissed or ignored by the user, so users can look at your notification after the popup is gone.

但是,如果你的通知中的消息仅在一段时间内相关,则应对 toast 通知设置过期时间,让用户不至于看到来自应用的过时信息。However, if the message in your notification is only relevant for a period of time, you should set an expiration time on the toast notification so the users do not see stale information from your app. 例如,如果升级的有效时间仅为 12 个小时,则将过期时间设置为 12 个小时。For example, if a promotion is only valid for 12 hours, set the expiration time to 12 hours. 下面的代码中将过期时间设置为 2 天。In the code below, we set the expiration time to be 2 days.

备注

本地 toast 通知的默认和最长过期时间为 3 天。The default and maximum expiration time for local toast notifications is 3 days.

toast.ExpirationTime = DateTime.Now.AddDays(2);

为 toast 提供主键Provide a primary key for your toast

如要以编程方式删除或替换发送的通知,需使用 Tag 属性(还可选择使用 Group 属性)来为通知提供主键。If you want to programmatically remove or replace the notification you send, you need to use the Tag property (and optionally the Group property) to provide a primary key for your notification. 然后,你可以在以后使用此主键来删除或替换该通知。Then, you can use this primary key in the future to remove or replace the notification.

要查看有关替换/删除已发送的 toast 通知的更多详细信息,请参阅快速入门:在操作中心 (XAML) 中管理 toast 通知To see more details on replacing/removing already delivered toast notifications, please see Quickstart: Managing toast notifications in action center (XAML).

Tag 和 Group 组合充当复合主键。Tag and Group combined act as a composite primary key. Group 是两者中较为通用的标识符,你可以用它来分配如“wallPosts”、“messages”、“friendRequests”等组。而 Tag 应该唯一标识组中的通知本身。Group is the more generic identifier, where you can assign groups like "wallPosts", "messages", "friendRequests", etc. And then Tag should uniquely identify the notification itself from within the group. 使用通用组时,可以使用 RemoveGroup API 删除该组中的所有通知。By using a generic group, you can then remove all notifications from that group by using the RemoveGroup API.

toast.Tag = "18365";
toast.Group = "wallPosts";

发送通知Send the notification

初始化 toast 之后,只需创建 ToastNotifier 并调用 Show(),传入 toast 通知。Once you have initialized your toast, simply create a ToastNotifier and call Show(), passing in your toast notification.

ToastNotificationManager.CreateToastNotifier().Show(toast);

清除你的通知Clear your notifications

UWP 应用负责删除和清除它们自己的通知。UWP apps are responsible for removing and clearing their own notifications. 当你的应用启动时,我们不会自动清除你的通知。When your app is launched, we do NOT automatically clear your notifications.

仅当用户显式单击通知时,Windows 才会自动删除该通知。Windows will only automatically remove a notification if the user explicitly clicks the notification.

下面是消息传递应用应执行的操作的示例...Here's an example of what a messaging app should do…

  1. 用户收到关于对话中新消息的多个 toastUser receives multiple toasts about new messages in a conversation
  2. 用户点击其中一个 toast 以打开该对话User taps one of those toasts to open the conversation
  3. 应用打开该对话,然后清除该对话的所有 toast(方法是对该对话的应用提供的组使用 RemoveGroupThe app opens the conversation and then clears all toasts for that conversation (by using RemoveGroup on the app-supplied group for that conversation)
  4. 用户的操作中心现在能正确反映通知状态,因为操作中心未留有该对话的过期通知。User's Action Center now properly reflects the notification state, since there are no stale notifications for that conversation left in Action Center.

若要了解有关清除所有通知或删除特定通知的信息,请参阅快速入门:在操作中心 (XAML) 中管理 toast 通知To learn about clearing all notifications or removing specific notifications, see Quickstart: Managing toast notifications in action center (XAML).

处理激活Handling activation

在 Windows 10 中,当用户单击你的 toast 时,你可以用以下两种不同方式激活你的应用...In Windows 10, when the user clicks on your toast, you can have the toast activate your app in two different ways...

  • 前台激活Foreground activation
  • 后台激活Background activation

备注

如果使用 Windows 8.1 提供的旧版 toast 模板,则将调用 OnLaunched 而不是 OnActivatedIf you are using the legacy toast templates from Windows 8.1, OnLaunched will be called instead of OnActivated. 以下文档仅适用于使用通知库的新式 Windows 10 通知(或者,如果使用原始 XML,则使用 ToastGeneric 模板)。The following documentation only applies to modern Windows 10 notifications utilizing the Notifications library (or the ToastGeneric template if using raw XML).

处理前台激活Handling foreground activation

在 Windows 10 中,当用户单击新式 toast(或 toast 上的按钮)时,将使用新激活类型 ToastNotification 调用 OnActivated 而不是 OnLaunchedIn Windows 10, when a user clicks a modern toast (or a button on the toast), OnActivated is invoked instead of OnLaunched, with a new activation kind – ToastNotification. 因此,开发人员能够轻松区分 toast 激活并相应地执行任务。Thus, the developer is able to easily distinguish a toast activation and perform tasks accordingly.

在下面的示例中,你可以检索最初在 toast 内容中提供的参数字符串。In the example you see below, you can retrieve the arguments string you initially provided in the toast content. 还可以检索用户在文本框和选择框中输入的内容。You can also retrieve the input the user provided in your text boxes and selection boxes.

重要

必须按 OnLaunched 代码那样初始化框架和激活窗口。You must initialize your frame and activate your window just like your OnLaunched code. 如果用户单击你的 toast ,则不会调用 OnLaunched,即使你的应用已关闭并是首次启动也是如此。OnLaunched is NOT called if the user clicks on your toast, even if your app was closed and is launching for the first time. 通常建议将 OnLaunchedOnActivated 合并到你自己的 OnLaunchedOrActivated 方法中,因为二者中均需执行相同的初始化。We often recommend combining OnLaunched and OnActivated into your own OnLaunchedOrActivated method since the same initialization needs to occur in both.

protected override void OnActivated(IActivatedEventArgs e)
{
    // Get the root frame
    Frame rootFrame = Window.Current.Content as Frame;

    // TODO: Initialize root frame just like in OnLaunched

    // Handle toast activation
    if (e is ToastNotificationActivatedEventArgs)
    {
        var toastActivationArgs = e as ToastNotificationActivatedEventArgs;

        // Parse the query string (using QueryString.NET)
        QueryString args = QueryString.Parse(toastActivationArgs.Argument);

        // See what action is being requested 
        switch (args["action"])
        {
            // Open the image
            case "viewImage":

                // The URL retrieved from the toast args
                string imageUrl = args["imageUrl"];

                // If we're already viewing that image, do nothing
                if (rootFrame.Content is ImagePage && (rootFrame.Content as ImagePage).ImageUrl.Equals(imageUrl))
                    break;

                // Otherwise navigate to view it
                rootFrame.Navigate(typeof(ImagePage), imageUrl);
                break;


            // Open the conversation
            case "viewConversation":

                // The conversation ID retrieved from the toast args
                int conversationId = int.Parse(args["conversationId"]);

                // If we're already viewing that conversation, do nothing
                if (rootFrame.Content is ConversationPage && (rootFrame.Content as ConversationPage).ConversationId == conversationId)
                    break;

                // Otherwise navigate to view it
                rootFrame.Navigate(typeof(ConversationPage), conversationId);
                break;
        }

        // If we're loading the app for the first time, place the main page on
        // the back stack so that user can go back after they've been
        // navigated to the specific page
        if (rootFrame.BackStack.Count == 0)
            rootFrame.BackStack.Add(new PageStackEntry(typeof(MainPage), null, null));
    }

    // TODO: Handle other types of activation

    // Ensure the current window is active
    Window.Current.Activate();
}

处理后台激活Handling background activation

当你对你的 toast(或 toast 内的按钮)指定后台激活时,将执行后台任务而不是激活前台应用。When you specify background activation on your toast (or on a button inside the toast), your background task will be executed instead of activating your foreground app.

有关后台任务的详细信息,请参阅使用后台任务支持应用For more information on background tasks, please see Support your app with background tasks.

如果你的目标版本是 14393 或更高版本,则可以使用进程内后台任务,这样可大大简化操作。If you are targeting build 14393 or higher, you can use in-process background tasks, which greatly simplify things. 请注意,无法在较旧版本的 Windows 上运行进程内后台任务。Note that in-process background tasks will fail to run on older versions of Windows. 在此代码示例中,将使用进程内后台任务。We'll use an in-process background task in this code sample.

const string taskName = "ToastBackgroundTask";

// If background task is already registered, do nothing
if (BackgroundTaskRegistration.AllTasks.Any(i => i.Value.Name.Equals(taskName)))
    return;

// Otherwise request access
BackgroundAccessStatus status = await BackgroundExecutionManager.RequestAccessAsync();

// Create the background task
BackgroundTaskBuilder builder = new BackgroundTaskBuilder()
{
    Name = taskName
};

// Assign the toast action trigger
builder.SetTrigger(new ToastNotificationActionTrigger());

// And register the task
BackgroundTaskRegistration registration = builder.Register();

然后,与前台激活一样,在你的 App.xaml.cs 中覆盖你可检索预定义参数和用户输入的 OnBackgroundActivated。Then in your App.xaml.cs, override the OnBackgroundActivated method you can retrieve the pre-defined arguments and user input, similar to the foreground activation.

protected override async void OnBackgroundActivated(BackgroundActivatedEventArgs args)
{
    var deferral = args.TaskInstance.GetDeferral();

    switch (args.TaskInstance.Task.Name)
    {
        case "ToastBackgroundTask":
            var details = args.TaskInstance.TriggerDetails as ToastNotificationActionTriggerDetail;
            if (details != null)
            {
                string arguments = details.Argument;
                var userInput = details.UserInput;

                // Perform tasks
            }
            break;
    }

    deferral.Complete();
}

纯“Vanilla”代码段Plain "Vanilla" code snippets

如果不从 NuGet 中使用通知库,可按如下所示的方法手动构造 XML,以创建 ToastNotificationIf you're not using the Notifications library from NuGet, you can manually construct your XML as seen below to create a ToastNotification.

using Windows.UI.Notifications;
using Windows.Data.Xml.Dom;

// In a real app, these would be initialized with actual data
string title = "Andrew sent you a picture";
string content = "Check this out, Happy Canyon in Utah!";
string image = "http://blogs.msdn.com/cfs-filesystemfile.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-71-81-permanent/2727.happycanyon1_5B00_1_5D00_.jpg";
string logo = "ms-appdata:///local/Andrew.jpg";

// TODO: all values need to be XML escaped

// Construct the visuals of the toast
string toastVisual =
$@"<visual>
  <binding template='ToastGeneric'>
    <text>{title}</text>
    <text>{content}</text>
    <image src='{image}'/>
    <image src='{logo}' placement='appLogoOverride' hint-crop='circle'/>
  </binding>
</visual>";

// In a real app, these would be initialized with actual data
int conversationId = 384928;

// Generate the arguments we'll be passing in the toast
string argsReply = $"action=reply&conversationId={conversationId}";
string argsLike = $"action=like&conversationId={conversationId}";
string argsView = $"action=viewImage&imageUrl={Uri.EscapeDataString(image)}";

// TODO: all args need to be XML escaped

string toastActions =
$@"<actions>

  <input
      type='text'
      id='tbReply'
      placeHolderContent='Type a response'/>

  <action
      content='Reply'
      arguments='{argsReply}'
      activationType='background'
      imageUri='Assets/Reply.png'
      hint-inputId='tbReply'/>

  <action
      content='Like'
      arguments='{argsLike}'
      activationType='background'/>

  <action
      content='View'
      arguments='{argsView}'/>

</actions>";

// Now we can construct the final toast content
string argsLaunch = $"action=viewConversation&conversationId={conversationId}";

// TODO: all args need to be XML escaped

string toastXmlString =
$@"<toast launch='{argsLaunch}'>
    {toastVisual}
    {toastActions}
</toast>";

// Parse to XML
XmlDocument toastXml = new XmlDocument();
toastXml.LoadXml(toastXmlString);

// Generate toast
var toast = new ToastNotification(toastXml);

资源Resources