Archived: Quickstart: Sending a local toast notification and handling activations from it (Windows 10) (10586)


A toast notification is a message that an app can construct and deliver to the user while he/she is not currently inside your app. 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. We will go through the following things:

Sending a toast

  • Specifying the new flexible template for your notification;
  • Constructing the visual part (text and image) of the notification;
  • Adding actions to the notification to make it interactive, and providing activation type, and contextual information used when your app is activated by these actions;
  • Setting an expiration time on the toast so it no longer shows up in user’s action center once gone stale;
  • Providing identity to your toast so it can be replaced/removed at a later time;
  • Sending your toast as a local notification.

Handling activation from a toast

  • Handling activation when the body of the toast is tapped;
  • Handling foreground activation for interactive toast notification;
  • Handling background activation for interactive toast notification;

Keep toast and tile/badge in sync, and more

  • Getting notified when your app's toast notifications are removed by user, expired by system, or added from your app server;
  • Using the History property to get the list of toast notifications from your app that is currently displayed in action center;
  • Updating your tile information to match what's inside action center, and more;

Prerequisites

To fully understand the content and some context of this topic, the below blog posts will be helpful:

Note: Different from Windows 8/8.1, you no longer need to declare in your app's manifest that your app is capable of showing toast notifications. All apps are capable of sending and displaying toast notifications.

Note: The example below will be using C# as the programming language.

Sending a toast

1. Install NuGet packages

We recommend installing the two following NuGet packages to your project. Our code sample will use these packages. We'll also provide the "Vanilla" code snippets that don't use any NuGet packages.

2. Add namespace declarations

Windows.UI.Notifications includes the toast APIs.

using Windows.UI.Notifications;
using NotificationsExtensions.Toasts; // NotificationsExtensions.Win10
using Microsoft.QueryStringDotNET; // QueryString.NET
using Windows.UI.Notifications;
using Windows.Data.Xml.Dom;

3. Construct the notification payload

In Windows 8/8.1, you used to be required to choose a template from the system-provided template catalog that fits the needs of your content – one of the templates in ToastTemplateType enumeration. In Windows 10, that’s not the case anymore. The number of text lines, optional profile picture that replaces the logo, and optional inline picture, can all be managed by using the new flexible and adaptive toast templates – ToastGeneric. See the adaptive and interactive toast documentation for more information.

Constructing the visual part of the payload

Let’s start by constructing the visual part of the payload, which includes the text and image content you want the user to see.

Thanks to NotificationsExtensions, generating the XML payload is much easier. If you don't install NotificationsExtensions.Win10 from NuGet, you have to construct the XML manually, which leaves room for errors.

Note: Images can be used from the app's package, the app's local storage, or from the web. Images must be less than 200 KB in size and smaller than 1024 x 1024 pixels. 

// 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";

// Construct the visuals of the toast
ToastVisual visual = new ToastVisual()
{
    TitleText = new ToastText()
    {
        Text = title
    },

    BodyTextLine1 = new ToastText()
    {
        Text = content
    },

    InlineImages =
    {
        new ToastImage()
        {
            Source = new ToastImageSource(image)
        }
    },

    AppLogoOverride = new ToastAppLogo()
    {
        Source = new ToastImageSource(logo),
        Crop = ToastImageCrop.Circle
    }
};
// 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>";

Constructing actions part of the payload

Now let’s add interactions to the payload.

In the below example, we included an input element that allows the user to input text, which can then be retrieved by the app using its id, once it is activated in the foreground or background – depending on how the interactions are defined for each of the actions.

We then created 2 action elements, each specifying its own activation type, content, and arguments.

  • Content attribute is used to specify the text on the button, when they are rendered to represent the actions;
  • activationType is used to specify how the 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 the activation type to be foreground or background, there is always a way for you to retrieve the user input, as well as the args you pre-defined in the arguments attribute, so you have the full context of what the user did to the notification. 
// 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())
    }
};
// 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>";

Combining the above to construct the full payload

The construction of the XML payload is now complete, and we can use it to instantiate your ToastNotification object.

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. 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.

Just like before, you can and should always add a launch parameter to the root element, so when user taps the body of the toast, your app can still be launched with a view that relates to the content of the notification.  

// 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());
// 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);

4. Providing expiration time to toast notification

In Windows 10, all toast notifications go in action center (which was previously only available on phone, but now available on all Windows devices) after they are dismissed or ignored by the user when it pops up on the screen for the first time, so the users no longer misses notifications from your app.

However, if the message in your notification is only available, or 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. In the code below I set the expiration time to be 2 days.

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

5. Providing identity and group id to toast notification

Just like in Windows Phone 8.1, you can provide identity to a toast notification, in order to target it at a later time, for the purposes of replacing or removing it, as shown below.

To see more details on replacing/removing already delivered toast notifications, please see Quickstart: Managing toast notifications in action center (XAML).

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

6. Send the toast notification

ToastNotificationManager.CreateToastNotifier().Show(toast);

7. IMPORTANT: Clearing your notifications

UWP apps are responsible for removing and clearing their own notifications. When your app is launched, we do NOT automatically clear your notifications.

The only times that Windows will automatically remove notification(s) are...

  • When a user taps a notification: that specific single notification is removed and activation is performed (but all the others still remain in Action Center).
  • When the user taps your app's header in Action Center: all of your app's notifications will be cleared while your app is launched.

Here's an example of what a messaging app should do...

  1. User receives multiple toasts about new messages in a conversation
  2. User taps one of those toasts to open the conversation
  3. The app opens the conversation and then clears all toasts for that conversation (by using Remove on the app-supplied tag 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.

To learn about clearing all notifications or removing specific notifications, see Quickstart: Managing toast notifications in action center.

Handling activation from a toast notification

Since Windows 8/8.1, applications has always been expected to handle activations in response to a user clicking on a toast notification from this app – the app should respond by performing navigation and displaying UI specific to the toast. This is accomplished through an activation string that you include in the toast payload, which is then passed to your app as an argument in the activation event.

In Windows 10, the data flow is very similar, but with the addition of adaptive templates and custom actions, there are 3 different kind of activations that the app might be expected to handle.

  1. Foreground activation from a toast notification using Windows 10 adaptive template;
  2. Background activation from a toast notification using Windows 10 adaptive template;
  3. Legacy: Foreground activation from a toast notification using legacy template.

Note: If you are developing a new Windows 10 universal app, I highly recommend using the new adaptive template. All legacy toast templates can be easily achieved by using the new adaptive template. By using the new adaptive template, you can have a consistent way and place to handle toast activation, which I will explain in details below.

1. Handling foreground activation from a toast notification using Windows 10 adaptive template

In Windows 10, we updated the toast activation behavior so that when a toast (or an action inside toast) triggers a foreground activation, OnActivated is invoked instead of OnLaunched, with a new activation kind – ToastNotification. Thus, the developer is able to easily distinguish a toast activation and perform tasks accordingly.

In the example you see below, you can retrieve the developer pre-defined arguments and the user generated input from the activation event.

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
        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();
}
protected override void OnActivated(IActivatedEventArgs args)
{
    // TODO: Initialize root frame just like in OnLaunched

    // Handle toast activation
    if (args.Kind == ActivationKind.ToastNotification)
    {
        var toastArgs = args as ToastNotificationActivatedEventArgs;
        
        // Get arguments corresponding to this activation;
        // When tapping the body of the toast caused this activation, the app receives the value of “launch” property of ;
        // When the activation is caused by using tapping on an action inside the toast, the app receives the value of “arguments” property of ; 
        var arguments = toastArgs.Arguments;

        // User input from <input> can be retrieved using the UserInput property. The UserInput is a ValueSet and the key is the pre-defined id attribute in the <input> element in the payload.
        var input = toastArgs.UserInput["1"];

        // Navigate accordingly
    }

    // TODO: Handle other types of activation
}

2. Handling background activation from a toast notification using Windows 10 adaptive template

When you specify the activation type of the toast notification (or an action inside the toast) to be background, a trigger will be fired to invoke the execution of your background task instead of activating your foreground app.

Please see this MSDN doc on how to register and create a background task as a Windows Runtime Component - Quickstart: Create and register a background task (XAML).

First, you need to declare your background task in your manifest by inserting the code below inside <Extensions>, which is a child element of <Application>...

...
  
  <Application ...>

    ...
    
    <Extensions>
    
      <Extension
        Category="windows.backgroundTasks"
        EntryPoint="RuntimeComponent1.NotificationActionBackgroundTask">
      
        <BackgroundTasks>
          <Task Type="systemEvent" />
        </BackgroundTasks>
    
      </Extension>
  
    </Extensions>

  </Application>

...

Then, you need to register a background task with the correct trigger. This is done programmatically. We recommend registering the background task in your app's OnLaunched and/or OnActivated code.

BackgroundAccessStatus status = await BackgroundExecutionManager.RequestAccessAsync();

BackgroundTaskBuilder builder = new BackgroundTaskBuilder()
{
    Name = "MyToastTask",
    TaskEntryPoint = "RuntimeComponent1.NotificationActionBackgroundTask"
};

builder.SetTrigger(new ToastNotificationActionTrigger());

BackgroundTaskRegistration registration = builder.Register();

When the task runs, your app is able to retrieve pre-defined arguments and user generated inputs from ToastNotificationActionTriggerDetail, very similar to how they are retrieved from the foreground activation EventArgs.

public sealed class NotificationActionBackgroundTask : IBackgroundTask
{
    public void Run(IBackgroundTaskInstance taskInstance)
    {
        var details = taskInstance.TriggerDetails as ToastNotificationActionTriggerDetail;

        if (details != null)
        {
            string arguments = details.Argument;
            var userInput = details.UserInput;

            // Perform tasks
        }
    }
}

3. Legacy: Handling foreground activation from a toast notification using legacy template

When the user taps on a toast constructed with one of the legacy templates, the associated app is launched or brought to the foreground, firing its OnLaunched method.

This example shows the syntax for the override of the OnLaunched event, in which you can retrieve and act on the Arguments provided by the LaunchActivatedEventArgs object (which comes from the toast's launch attribute).

protected override void OnLaunched(LaunchActivatedEventArgs args)
{
    string launchString = args.Arguments

    ....
}

Keep toast and tile/badge in sync using ToastNotificationHistoryChangedTrigger

For a lot of apps, especially social networking or messaging apps, the toast notification is often corresponding to the badge/tile content – when a new toast notification is available and delivered, the app in this case would update the badge, to show the number of unseen toast notifications. When a toast notification is removed by the user, or expired by the system, the app is also expected to update the badge/tile to show the total number of toast notification left. This can now be done through a new trigger we introduce in Windows 10 – ToastNotificationHistoryChangedTrigger. As hinted by the name, this trigger is fired whenever the collection of toast notification history from your app changes on the device. Subscribing this trigger, will allow your app to get notified when:

  • A toast notification is removed by the user from action center;
  • A toast notification is expired and removed by the system from action center;
  • A toast notification is delivered form your app, via push;

Note: the only thing your app wouldn’t be notified for, is a local toast being popped/added, since it is not necessary to notify your app client about something it just did.

Here is how to register the background task in manifest by inserting below code inside <Extensions> which is a child element of <Application>:

<Extension Category="windows.backgroundTasks" EntryPoint="RuntimeComponent1.NotificationHistoryBackgroundTask">
  <BackgroundTasks>
    <Task Type="systemEvent" />
  </BackgroundTasks>
</Extension>

Please see this MSDN doc on how to register and create a background task as a Windows Runtime Component - Quickstart: Create and register a background task (XAML).

When the task runs, the app is able to retrieve information on whether toast was added or removed from the collection of existing toasts using TriggerDetails and do different things accordingly. In the sample below, you can see that we access ToastNotificationHistory to see how many toast notifications are there from my app in action center, then update the badge count with that number, when toast notifications are added or removed.

public sealed class NotificationHistoryBackgroundTask : IBackgroundTask
{
        public void Run(IBackgroundTaskInstance taskInstance)
        {
            var details = taskInstance.TriggerDetails as ToastNotificationHistoryChangedTriggerDetail;
            if (details != null)
            {
                switch (details.ChangeType)
                {
                    case ToastHistoryChangedType.Added:
                        UpdateBadge();
                        break;
                    case ToastHistoryChangedType.Removed:
                        UpdateBadge();
                        break;
                    default:
                        break;
                }
            }
            else
            {
                //Do nothing;
            }
        }

        private void UpdateBadge()
        {
            int badgeInt = ToastNotificationManager.History.GetHistory().Count;
            string badge = badgeInt.ToString();

            XmlDocument badgeXml = BadgeUpdateManager.GetTemplateContent(BadgeTemplateType.BadgeNumber);

            XmlElement badgeElement = (XmlElement)badgeXml.SelectSingleNode("/badge");
            badgeElement.SetAttribute("value", badge);

            BadgeNotification badgeNotification = new BadgeNotification(badgeXml);
            BadgeUpdateManager.CreateBadgeUpdaterForApplication().Update(badgeNotification);
        }
}

Resources

Skip to main content