Chapter 22. Toast Notifications

Toast notifications, affectionately called this because they “pop up” like toast in a toaster, are the little messages in rectangles that, on Windows 8 and later, appear in the upper-right corner of the screen (or upper-left when a right-to-left language is used). The toast notification APIs introduced in Windows 8 are a part of the Windows Runtime meant for Windows Store apps rather than desktop apps. However, desktop apps can still use this functionality, and this chapter shows you how.

The neat thing about these toast notifications is that they appear on top of everything. The user doesn’t even need to be currently using the desktop. The Start screen or any Windows Store app could be filling the screen, and the notifications will still appear. If you’ve used Outlook 2013 on Windows 8, you’ve experienced what it’s like when a desktop app uses toast notifications.

Prerequisites

Before using the toast notification APIs, you must do two things:

Image Enable the use of Windows Runtime APIs in your WPF project

Image Add a shortcut with a special ID to the Start screen

Using Windows Runtime APIs in Desktop Apps

To use Windows Runtime APIs in a WPF project, you need to add a few references. However, the process for doing this is a little strange, at least as of Visual Studio 2012.

The first reference you need to add is for the Windows Runtime, but the Reference Manager dialog doesn’t show this without first manually editing your .csproj file in Notepad or your favorite text editor and adding the following setting that tells Visual Studio you wish to target Windows 8:

<PropertyGroup>
  <TargetPlatformVersion>8.0</TargetPlatformVersion>
</PropertyGroup>

After saving the file with that addition, reload your project in Visual Studio. Now, when you select Add Reference to invoke the Reference Manager, a “Windows” category appears on the left. Select it, and then add a reference to the “Windows” item that appears in the list. This is shown in Figure 22.1.

Image

FIGURE 22.1 Desktop apps can reference the Windows Runtime once the project file has been manually tweaked.

To avoid compilation errors with some of the code used in this chapter, you’ll need to reference two more items that still are not shown in the Reference Manager dialog. To do this, open the .csproj file in a text editor again and add the following two items to the list of references:

<Reference Include="System.Runtime"/>
<Reference Include="System.Runtime.InteropServices.WindowsRuntime"/>

Reload your project in Visual Studio, and then you’re ready to start writing code.

Adding a Shortcut with an AppUserModelID to the Start Screen

Windows doesn’t allow desktop apps to show toast notifications unless a shortcut to the app has been added to the Start screen and that shortcut is configured with something known as an AppUserModelID. These IDs were introduced in Windows 7, and were initially used to enable several of the new taskbar features. For example, they enabled Windows to identify separate processes as logically belonging to the same app and therefore correctly group their windows together on the same taskbar button.

Installer technologies for desktop apps provide ways to create such a shortcut, but the source code that accompanies this book accomplishes this with the following code:

// This is the AppUserModelID and the shortcut name:
string appID = "Chapter22 Sample";

void PinToStart()
{
  // Create a shortcut file where the Start screen will see it
  string file = Path.Combine(
    Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData),
    @"MicrosoftWindowsStart MenuPrograms" + appID + ".lnk");

  // If the file exists, we must have already run this code
  if (File.Exists(file))
    return;

  // Make the shortcut invoke this app with no arguments
  IShellLinkW shortcut = (IShellLinkW)new ShellLink();
  shortcut.SetPath(Process.GetCurrentProcess().MainModule.FileName);
  shortcut.SetArguments("");

  // Set the AppUserModelID
  using (PropVariant variant = new PropVariant(appID))
  {
    (shortcut as IPropertyStore).SetValue(
      SystemProperties.System.AppUserModel.ID, variant);
    (shortcut as IPropertyStore).Commit();
  }

  // Save the shortcut
  (shortcut as IPersistFile).Save(file, true);
}

This code makes use of some COM Interop definitions in this chapter’s project. It also references the Windows API Code Pack for Microsoft .NET Framework for the definition of PropVariant and SystemProperties. You can download it at http://archive.msdn.microsoft.com/WindowsAPICodePack.

Once PinToStart is executed at least once, you can see the shortcut on the Start screen, as shown in Figure 22.2.

Image

FIGURE 22.2 The Start screen shortcut internally contains an AppUserModelID that enables toast notifications from this app to be shown.

Sending a Toast Notification

With the prerequisites out of the way, your app can now show toast notifications. A toast notification can contain a mixture of text and an image, but it cannot contain arbitrary XAML. Its appearance is tightly controlled by an XML template. With such a template, you choose one of several predefined layouts and, depending on the choice, your text and/or image elements get treated differently. Although this means your customization possibilities are limited, it also means that it’s easy to give your app a toast notification with a standard look-and-feel, including the entrance/exit animations.

For example, the following method can be invoked at any time to trigger a toast notification:

using Windows.Data.Xml.Dom;
using Windows.UI.Notifications;
...
void SendToast()
{
  // create a string with the toast template xml
  string xmlString = @"
<toast>
  <visual>
    <binding template='ToastText01'>
      <text id='1'>Alert!</text>
    </binding> 
  </visual>
</toast>";

  // Load the content into an XML document
  XmlDocument document = new XmlDocument();
  document.LoadXml(xmlString);

  // Create a toast notification and show it
  ToastNotification notification = new ToastNotification(document);
  // The appID is the same AppUserModelID shown earlier
  ToastNotificationManager.CreateToastNotifier(appID).Show(notification);
}

Here, the chosen template is called ToastText01. This template expects one piece of text with an id of 1. Once you load an XmlDocument from the string, you can create a ToastNotification that is passed to ToastNotifier’s Show method. ToastNotifier also exposes a Hide method in case you want to hide the toast early, presumably due to some user action inside your app.


Warning: You must pass your app’s AppUserModelID to CreateToastNotifier!

Although there’s a parameterless overload of CreateToastNotifier, this is only appropriate for Windows Store apps. Desktop apps need to specify the appropriate ID, otherwise the toast notification will not be shown.


This produces the result in Figure 22.3 in the top corner of the screen. The toast notification automatically picks up the icon from your app.

Image

FIGURE 22.3 A simple toast notification


Warning: How can I change the colors of a toast notification?

Windows Store apps are able to choose custom colors for their live tiles in the app manifest, and these colors are used for their toast notifications as well. Unfortunately, desktop apps have no way to change the colors used by their toast notifications, just like they have no way to change the colors used by their Start screen shortcuts.


Toast Templates

You’ve seen the simple ToastText01 template, but there are eight to choose from. Four are text-only, and the other four are the same as the first four but with an added image. Figure 22.4 demonstrates them all, with the IDs of the text elements included for demonstration purposes. When used with a right-to-left language, the alignment of text is flipped accordingly.

Image

FIGURE 22.4 Each of the toast notification templates

The templates expect 1–3 text elements with ids of 1, 2, and 3, respectively. The templates with an image expect an image element with an id of 1. (It’s okay that a text and image element use the same ID.) For example, the following template is used to create the ToastImageAndText02 example in Figure 22.4:

<toast>
  <visual>
   <binding template="ToastImageAndText02">
     <text id="1">id='1': A single-line header</text>
     <text id="2">id='2': The main text that can wrap onto multiple lines</text>
     <image id="1" src="file:///C:/image.jpg" alt="A description"/>
   </binding>
  </visual>
</toast>

The URI used for image’s src attribute can point to the local file system or it can fetch an image from the Web (http or https). The alt text is not used in a tooltip for the image, but it is used by assistive technologies such as screen readers.


Warning: Toast notification images can be at most 1024x1024 pixels!

They also must be no more than 200 KB, and must be a PNG, JPEG, or GIF.



Tip

You can provide multiple text elements simultaneously with the same ids and the text translated into different languages. Windows uses the correct one as long as each element’s lang attribute is set to an appropriate BCP 47 language code.



Tip

If using XML strings inside your code leaves a bad taste in your mouth, there are two other options. One is to use the ToastNotificationManager.GetTemplateContent method, which requires a value of the ToastTemplateType enumeration that lists all possible templates. This returns an XmlDocument, which you can modify before constructing a ToastNotification with it. For example, the following code shows a toast notification using the ToastImageAndText03 template:

void SendToast()
{
  // Choose ToastImageAndText03
  XmlDocument document = ToastNotificationManager.GetTemplateContent(
    ToastTemplateType.ToastImageAndText03);

  // Update the three text elements
  XmlNodeList textElements = document.GetElementsByTagName("text");
  textElements[0].AppendChild(document.CreateTextNode("one"));
  textElements[1].AppendChild(document.CreateTextNode("two"));
  textElements[2].AppendChild(document.CreateTextNode("three"));

  // Update the image element
  XmlNodeList imageElements = document.GetElementsByTagName("image");
  XmlNamedNodeMap attributes = imageElements[0].Attributes;
  attributes.GetNamedItem("src").NodeValue = "file:///C:/image.jpg";
  attributes.GetNamedItem("alt").NodeValue = "A description";

  // Create a toast notification and show it
  ToastNotification notification = new ToastNotification(document);
  // The appID is the same AppUserModelID shown earlier
  ToastNotificationManager.CreateToastNotifier(appID).Show(notification);
}

The other option is to use code from the NotificationsExtensions project that is included in the Windows SDK sample at http://bit.ly/I8Bpga.



Tip

By default, a toast notification stays on the screen for seven seconds unless the user interacts with it. However, by setting the toast element’s duration attribute to long, you can make it remain on the screen for twenty-five seconds! For example:

<toast duration="long">
  <visual>
    ...
  </visual>
  <audio .../>
</toast>

This is meant for situations in which a person is waiting for you to respond, such as in a chat session.



Tip

You can customize the audio that gets played by a toast notification by including an audio element as a child of the toast element in the XML template. For example:

<toast>
  <visual>
    <binding template="ToastText01">
      <text id="1">Alert!</text>
    </binding>
  </visual>
  <audio src="ms-winsoundevent:Notification.Mail"/>
</toast>

You have only eight sounds to choose from (in addition to the default mswinsoundevent:Notification.Default sound):

Image ms-winsoundevent:Notification.IM

Image ms-winsoundevent:Notification.Mail

Image ms-winsoundevent:Notification.Reminder

Image ms-winsoundevent:Notification.SMS

Image ms-winsoundevent:Notification.Looping.Alarm

Image ms-winsoundevent:Notification.Looping.Alarm2

Image ms-winsoundevent:Notification.Looping.Call

Image ms-winsoundevent:Notification.Looping.Call2

The sounds with “Looping” in the name support looping if the toast element’s duration is set to long and audio’s loop attribute is set to true. You can also mute the sound altogether by setting audio’s silent attribute to true.


Notification Events

If you’re showing a toast notification, presumably you want to act upon the user clicking/tapping it. This is done with ToastNotification’s Activated event:

ToastNotification notification = new ToastNotification(document);
notification.Activated += OnActivated;
...
void OnActivated(ToastNotification sender, object args)
{
  Dispatcher.Invoke(() =>
  {
    MessageBox.Show("Thank you for clicking the toast notification!");
  });
}

This mechanism shows that your app must be running in order to act upon the toast notification, which is different from Windows Store apps. If they aren’t already running, Windows Store apps are launched when their toast notifications are clicked. Of course, as a desktop app, you can run “in the background” (with no visible user interface showing) and still handle this event. If the Windows desktop isn’t visible when the toast notification is activated, Windows automatically switches to the desktop, and then to your app.

ToastNotification also exposes a Dismissed event. This is raised when the toast notification disappears for any reason other than the user clicking it. In a Dismissed event handler, you can determine why it was dismissed with a value from the ToastDismissalReason passed through ToastDismissedEventArgs:

Image ApplicationHidden means the app called ToastNotifier.Hide

Image TimedOut means the seven (or twenty-five) seconds elapsed with no interaction

Image UserCanceled means that the user clicked the little X button on the notification

Finally, ToastNotification’s Failed event is raised for any reason that prevents the toast notification from being shown, such as an invalid or missing AppUserModelID, or invalid XML. The Show method does not throw exceptions for these failures, so you must handle Failed to help diagnose such errors.


Warning: ToastNotification events are raised on a background thread!

Therefore, if you plan on updating your user interface, you’ll have to take advantage of Dispatcher to run code on the UI thread.


Scheduled Notifications

Although in many cases you want to show a toast notification instantly in response to some event (like an incoming email message), you can leverage a ToastNotifier method for scheduling it for a later date and time. You do this by calling AddToSchedule method instead of Show.

AddToSchedule requires a ScheduledToastNotification object that must be constructed with a DateTimeOffset representing the delivery time (along with the XmlDocument defining the content). Here’s an example:

// Create a toast notification and show it 5 seconds from now
ScheduledToastNotification notification =
  new ScheduledToastNotification(document,
  DateTimeOffset.Now + TimeSpan.FromSeconds(5));

ToastNotificationManager.CreateToastNotifier(appID).AddToSchedule(notification);

If you want to cancel a pending notification, you can call ToastNotifier’s RemoveFromSchedule method.

ScheduledToastNotification provides a slick additional feature. Via an overloaded constructor, you can specify a snooze interval (a nullable TimeSpan that must be between 60 seconds and 60 minutes) and maximum snooze count (a uint from 1–5). This enables you to easily support alarm-style functionality.


Image FAQ: What’s the difference between the DateTime data type and DateTimeOffset?

DateTime refers to a logical point in time that is independent of any time zone, whereas DateTimeOffset is a real point in time with an offset relative to the UTC time zone. DateTimeOffset is the most appropriate choice for describing something like the date a photo was taken because that point in time should never be reinterpreted even if you are now in a different time zone. An alarm clock, however, should use DateTime for the alarm time. If you had set your alarm for 8:00 AM while travelling, you probably expect it to go off at 8:00 AM no matter what time zone you happen to be in at the time. This is important to consider if you’re planning on using a scheduled toast notification as something resembling an alarm!

For most scenarios, using DateTimeOffset is preferable to DateTime. However, it was introduced into the .NET Framework years after DateTime, so the better name was already taken. (Designers of the class rejected calling it DateTime2 or DateTimeEx.) Fortunately, consumers of these data types can pretty much use them interchangeably.


Summary

With the ability to use toast notifications on Windows 8 and later, desktop apps can feel right at home to users alongside Windows Store apps. In fact, with the Start screen shortcut and AppUserModelID assigned, users can even configure toast notifications for your WPF app from within the PC Settings app. This is shown in Figure 22.5.

Image

FIGURE 22.5 Desktop apps that follow the steps in this chapter appear alongside Windows Store apps in the page for controlling notifications in the PC Settings app.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset