Progress UI and data binding inside toast notifications - Windows 10 Creators Update


Note: The new feature(s) covered in this post are available to Windows Insiders running build 15007 or newer. If you are using the UWP Toolkit Notifications library, the feature(s) can be found in version 1.4.0 or higher.

New in Creators Update: In the most recent Windows 10 Creators Update, we added several exciting improvements and new features to toast notifications and Action Center. One of them is allowing developers to show an animated progress indicator inside their toast notifications to show the progress of an ongoing operation, such as file transfer, progress of completing a task, progress of a work-out session, etc.

A progress bar inside a toast can be either "determinate" to show a specific value, or "indeterminate" which can be used when the app does not know the length of the operation. For a "determinate" progress indicator in a toast notification, an app needs to keep on updating the status and value of it using notification data binding, another new feature that will be demonstrated later in the post.

Note: Mobile does not support the progress indicator as part of the Creators Update, but support will come in a future Windows release. Notification data binding as a stand-alone feature will be supported in Mobile in Creators Update so apps can use that to update text content inside toast notifications, which will be covered later in this blog post.

Notification progress indicator and data binding

As you can see in the picture below, here is what a progress indicator in a Windows toast notification looks like:

 

exercise-progress-bar

In this notification, you can see that a progress indicator contains several visual elements, that are either required or optional.

Visual element Type and value Description
title string Title is an optional attribute - it is a custom string that is displayed above the animated progress indicator. It can be used to explain what the progress is indicating;
status string Status is a requried attribute - it is a custom string that app can provide to be displayed below the animated progress indicator. It can be used to explain the status of the progress, such as downloading, uploading, paused, error, etc.
value A double between 0 and 1 or string "indeterminate" Value is a required attribute.

You can provide a double between 0 and 1 (inclusive) that indicates the actual progress - this will directly result in the progress bar being draw with x% full;

Or, you can use the string value "indeterminate", which will result in the "indeterminate" progress bar animation.

Any value other than the above would be considered invalid and will cause undesired behaviors.

valueStringOverride string ValueStringOverride is an optional attribute - it is a custom string value that apps can use to override the default automatic textual representation of their progress value, which is displayed at the bottom right corner below the progress indicator. Apps can use this to clarify the semantic meaning of the progress value (for example, when you are transferring 10 files (5 MB) in total, when you provide a value of 0.2, you can further clarify that whether this means 4/10 files or 1/5 MB transffered using the valueStringOverride property);

If not provided, a default string like 20% will automatically be displayed.

Here is how to specify the above progress indicator inside XML:

ToastContent content = new ToastContent()
{
    Visual = new ToastVisual()
    {
        BindingGeneric = new ToastBindingGeneric()
        {
            Children =
            {
                new AdaptiveText()
                {
                    Text = "Getting closer to this week's exercise goal!"
                },

                new AdaptiveProgressBar()
                {
                    Title = "Ab Ripper",
                    Value = 0.6,
                    ValueStringOverride = "3/5 sessions",
                    Status = "On track"
                }
            }
        }
    }
};
<toast>
    <visual>
        <binding template="ToastGeneric">
            <text>Getting closer to this week's exercise goal!</text>
            <progress
                title="Ab Ripper"
                value="0.6"
                valueStringOverride="3/5 sessions"
                status="On track"/>
        </binding>
    </visual>
</toast>

 

However, you'll need to dynamically update the values of the progress indicator for it to actually be "live". This can be done by updating a toast notification with data-binding.

There are two parts to updating a toast notification - sending an initial toast notification with data bound fields, and updating those fields with new values when there is any update.

Note: In the upcoming Creators Update, all attributes of the <progress> element, and the text contents of top-level <text> elements support data binding inside a toast notification payload.

First, the app needs to construct the initial toast notification payload with data binding fields in it, and use the Data property to provide the corresponding initial data values.

  • Note that you need to assign and keep track of the Tag and Group property in order to target the same notification to update later.
  • Also note the use of SequenceNumber property to prevent updates from occurring out of order.
using Windows.UI.Notifications;
using Microsoft.Toolkit.Uwp.Notifications;

public void SendUpdatableToastWithProgress()
{
    // Define a tag value and a group value to uniquely identify a notification, in order to target it to apply the update later;
    string toastTag = "pikachu_0132";
    string toastGroup = "egg_fertilization";

    // Construct the toast content with updatable data fields inside;
    var content = new ToastContent()
    {
        Visual = new ToastVisual()
        {
            BindingGeneric = new ToastBindingGeneric()
            {
                Children =
                {
                    new AdaptiveText()
                    {
                        Text = "Keep walking! A cute Pikachu will be your reward!"
                    },

                    new AdaptiveProgressBar()
                    {
                        Title = "Egg - Pikachu",
                        Value = new BindableProgressBarValue("progressValue"),
                        ValueStringOverride = new BindableString("progressString"),
                        Status = "progressStatus"
                    }
                }
            }
        }
    };

    // Generate the toast notification;
    var toast = new ToastNotification(content.GetXml());

    // Assign the tag and group properties;
    toast.Tag = toastTag;
    toast.Group = toastGroup;

    // Define NotificationData property and add it to the toast notification to bind the initial data;
    // Data.Values are assigned with string values;
    toast.Data = new NotificationData();
    toast.Data.Values["progressValue"] = "0.01";
    toast.Data.Values["progressString"] = "0.1/10 miles";
    toast.Data.Values["progressStatus"] = "Just getting started";

    // Provide sequence number to prevent updating out-of-order or assign it with value 0 to indicate "always update";
    toast.Data.SequenceNumber = 1;

    // Show the toast notification to the user;
    ToastNotificationManager.CreateToastNotifier().Show(toast);
}
using Windows.UI.Notifications;

public void SendUpdatableToastWithProgress()
{
    // Define a tag value and a group value to uniquely identify a notification, in order to target it to apply the update later;
    string toastTag = "pikachu_0132";
    string toastGroup = "egg_fertilization";

    // Construct the toast content with updatable data fields inside;
    string toastXmlString =
    @"<toast>
              <visual>
		  <binding>
			<text>Keep walking! A cute Pikachu will be your reward!</text>
			<progress title="Egg - Pikachu" value="{progressValue}" valuestringoverride="{progressString}" status="{progressStatus}"></progress>
		  </binding>
              </visual>
          </toast>";

    XmlDocument toastXml = new XmlDocument();
    toastXml.LoadXml(toastXmlString);

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

    // Assign the tag and group properties;
    toast.Tag = toastTag;
    toast.Group = toastGroup;

    // Define NotificationData property and add it to the toast notification to bind the initial data;
    // Data.Values are assigned with string values;
    toast.Data = new NotificationData();
    toast.Data.Values["progressValue"] = "0.01";
    toast.Data.Values["progressString"] = "0.1/10 miles";
    toast.Data.Values["progressStatus"] = "Just getting started";

    // Provide sequence number to prevent updating out-of-order or assign it with value 0 to indicate "always update";
    toast.Data.SequenceNumber = 1;

    // Show the toast notification to the user;
    ToastNotificationManager.CreateToastNotifier().Show(toast);
}

Then, when there is new content or update you want to show to the user, simply provide the new data to update the toast without re-constructing the entire payload by calling the Update() method.

using Windows.UI.Notifications;

public void UpdateProgress()
{
	// Construct a NotificationData object;
	string toastTag = "LongRunningOperationXYZ";
	string toastGroup = "LongRunningOperations";

        // Create NotificationData with new values;
        // Make sure that sequence number is incremented since last update, or assign with value 0 for updating regardless of order;
        var data = new NotificationData { SequenceNumber = 2 };
	data.Values["progressValue"] = "0.5";
	data.Values["progressStatus"] = "running";

        // Updating a previously sent toast with tag, group, and new data;
	NotificationUpdateResult updateResult = ToastNotificationManager.CreateToastNotifier().Update(data, tag, group);
}

New Class, Property, and Method Definitions

Here are some more details about the newly added class and properties:

NotificationData Class

NotificationData contains the data needed to update a notification.

Constructor
NotificationData (IIterable<IKeyValuePair<HSTRING, HSTRING>>, ULONG)
NotificationData (IIterable<IKeyValuePair<HSTRING, HSTRING>>)
NotificationData()

 

Property Description
Values IMap<string, string> The actual values of the data in key-value pairs;
SequenceNumber ULONG The SequenceNumber of the update to prevent false update caused by race conditions
Note that the value 0 means “always update.”

Both ToastNotification and ScheduledToastNotification have added new Data property.

Property and Type Description
ToastNotification.Data

NotificationData

Define the data property to provide initial values of data when a toast notification is using data-binding and is expected to be updated afterwards.

 

Property and Type Description
ToastNotification.Data

NotificationData

Define the data property to provide initial values of data when a toast notification is using data-binding and is expected to be updated afterwards.Define the data property to provide initial values of a scheduled toast notification when this notification is using data-binding and is expected to be updated afterwards.

ToastNotifier.Update()

Method Syntax Description
ToastNotifier.Update(Tag, Group, NotificationData) public enum Update(
string tag
string group
NotificationData data
)
Call this method to update on an existing toast notification based on the tag and group properties, when the original toast has tag and group values provided. It returns an enum which indicates the result of the update.
ToastNotifier.Update(Tag, NotificationData) public enum Update(
string tag
NotificationData data
)
Call this method to update on an existing toast notification based on the tag property, when the original toast has tag value provided. It returns an enum which indicates the result of the update.

Here are details on NotificationUpdateResult enum:

Member Value Description
Succeeded 0 Notification is successfully updated
Failed 1 Notification failed to be updated for unknown reason
NotificationNotFound 2 Notification failed to be updated because the original notification was missing

Note: If the time interval between your initial notification delivery and your notification update is long, there is a good chance that you will get NotificationNotFound result because the user has already dismissed the notification. This could potentially mean that he/she is not interested in further updates on this content either. We recommend you to think carefully based on your specific scenario before popping another toast for the same content.

Updating vs. Replacing a Toast Notification

Some developers may already know that you can replace a previously sent notification by re-sending a new toast with the same Tag and Group values. So what exactly is the difference between updating and replacing a notification, and how to decide on when to use one or the other?

  • Updating a notification is recommended for updating real-time information/content in a notification you recently sent;
  • Update the toast to show the change if you think knowing this change provides additional benefits to the user - the canonical example on this is change the progress in a progress indicator;
  • Updating a toast works best when there are only subtle changes to your content - if the content or layout of the toast notification is entirely changed, then you may consider popping a new toast notification or using replacement;
  • An update will return failure when the notification being targeted to update is no longer present (dismissed by user, expired, pushed out of the notification queue, etc.). Replacement of a notification on the other hand, will always be presented to the user regardless of whether the previous notification being targeted to replace is still present or not.
  • When a notification is updated, the position of it inside Action Center is maintained. On the other hand, when a notification is replaced, the replacing toast will always show up on top as the most recent notification from your app.
  • Updating a toast notification will not cause a toast to re-pop the banner UI. When replacing a toast, you can choose to re-pop the new notification or silently put it in Action Center by setting different value on the SuppressPopUp property.

Resources


Comments (6)

  1. rseiler says:

    Speaking of the Win10 desktop, will "toast" ever once again be capable enough such that toast from Outlook is actionable, as in allowing the user to delete the message then and there? This used to be a thing, but Win10 took it away. It's been a couple years now, and a lot of people miss it.

    1. Daniel says:

      I believe they could put this into Outlook if they wanted. Should be able to pop a toast notification from non-UWP apps just fine. I think it would be a question for the Outlook team. The Windows 10 Mail app supports what you talk about with the option to Flag or Delete from a toast.

  2. The example of “signing XML with SHA-256” and “new fields” is missing.

  3. Dev says:

    This guy's English is terrible, and this is a very poorly written article. Unhelpful.

  4. ivan says:

    You could really try to run the code before publishing it.

  5. SkaCh says:

    So i have a notification that includes buttons with icons.
    I added a progress bar like shown here, it works well. However, whenever there is an update of the progress bar the buttons flicker.
    Any idea how to solve this problem ?

Skip to main content