How to Work with Local Notifications in Windows 8-Style Apps
Learn how to code toast and Windows live tile notifications
October 23, 2012
Download the code for this article
Windows 8-style applications (formerly known as "Metro-style" apps) -- that is, apps built using the API for Windows 8 and Windows Runtime (WinRT) tablet-optimized applications -- have access to a unified notificationsystem that makes it easy for developers to tell users when events happen in and around the application. You might, for example, like to pop up some toastto tell users an activity has finished or update bespoke information on the app's live tile on the Start screen. Or you might want to update a badgeshowing a number or a glyph. In this article, I'll take you through how to issue these types of local notifications in your app. We're going to build anapplication that "brews" randomly selected espresso-based coffee drinks. We'll build a little engine that generates random drink choices, then informs theuser about the availability of his or her drink through toast and by updating tiles.
The Basic BrewNotifier App
We'll start by building a normal XAML/C# Windows 8-style application. To do this, open Visual Studio 2012 and create a new Visual C#, WindowsMetro style, Blank App project. Name it whatever you like; I've named mine BrewNotifier.
I'm proposing a simple engine that uses a random-number generator to create drinks. Because the engine isn't the central point of the application, I'll runthrough that part of the app quite quickly, and we'll go into more detail when building the actual notification stuff. For background, an example of thesort of drink this engine can build would be a "Large double-shot wet skimmed-milk latte with marshmallows, to go." We'll have a set of enumerations thatwill define metrics for the drink; Listing 1 shows the code for this part of the application.
Listing 1: Enumerations for BrewNotifier App
namespace BrewNotifier{ public enum DrinkSize { Tiny = 0, Regular = 1, Large = 2, Massive = 3, } public enum DrinkType { Latte = 0, Mocha = 1, Macchiato = 2, Cappuccino = 3 } public enum MilkType { SkimmedMilk = 0, SemiSkimmedMilk = 1, WholeMilk = 2, Soy = 3 } public enum SprinkleType { None = 0, ChocolateSprinkles = 1, MiniMarshmallows = 2 }}
With the enumerations defined, we can turn to building our Barista and DeliciousDrink classes. Barista will design our drinks for us and return an instanceof DeliciousDrink containing whatever has been "brewed." Listing 2 shows the code for the Barista class; Listing 3 shows the code for the DeliciousDrinkclass.
Listing 2: The Barista Class
namespace BrewNotifier{ public class Barista { internal DeliciousDrink BrewMeAnything() { var rand = new Random(); // create... var drink = new DeliciousDrink(); drink.NumShots = rand.Next(1, 4); drink.Decaff = PickBool(rand); drink.Wet = PickBool(rand); drink.ToGo = PickBool(rand); drink.Size = (DrinkSize)PickEnum(typeof(DrinkSize), rand); drink.Type = (DrinkType)PickEnum(typeof(DrinkType), rand); drink.Milk = (MilkType)PickEnum(typeof(MilkType), rand); drink.Sprinkles = (SprinkleType)PickEnum(typeof(SprinkleType), rand); // return... return drink; } private int PickEnum(Type enumType, Random rand) { var options = Enum.GetValues(enumType); return ((int[])options)[rand.Next(0, options.Length)]; } private bool PickBool(Random rand) { return rand.Next(100) < 50; } } }
Listing 3: The DeliciousDrink Class
namespace BrewNotifier{ public class DeliciousDrink { public int NumShots { get; internal set; } public DrinkSize Size { get; internal set; } public bool Decaf { get; internal set; } public DrinkType Type { get; internal set; } public MilkType Milk { get; internal set; } public bool Wet { get; internal set; } public SprinkleType Sprinkles { get; internal set; } public bool ToGo { get; internal set; } internal DeliciousDrink() { } public override string ToString() { var builder = new StringBuilder(); builder.Append(Size); builder.Append(" "); if (NumShots == 1) builder.Append("single-shot"); else if (NumShots == 2) builder.Append("double-shot"); else if (NumShots == 3) builder.Append("triple-shot"); builder.Append(" "); if (Decaf) builder.Append("decaf "); if (Wet) builder.Append("wet "); builder.Append(Milk); builder.Append(" "); builder.Append(Type); if (Sprinkles != SprinkleType.None) { builder.Append(" with "); builder.Append(Sprinkles); } if (ToGo) builder.Append(", to go."); else builder.Append(", for here."); return builder.ToString(); } }}
The interesting part of the DeliciousDrink class is the code to put together a description of the drink, much like what someone might say whenactually ordering one.
Finally we can now actually make some drinks and complete our review of the background information. We'll put the brewed drink description on the screenusing Windows.UI.Popups.MessageDialog. To do this, in the MainPage page in your XAML app, add a button, give it some text, and double-click it to create anevent handler. (In my case I've created an event handler called HandleBrewClick.) In the event handler we'll use a Barista instance to create a drink, thenrender the drink description on the screen. Listing 4 shows the code that performs these actions.
Listing 4: Displaying the Brewed Drink Description
private async void HandleBrewClick(object sender, RoutedEventArgs e) { // make up some coffee... var barista = new Barista(); var result = barista.BrewMeAnything(); // share the result... var dialog = new MessageDialog(result.ToString()); await dialog.ShowAsync(); }
Run the project and click the button, and you'll see a random drink appear in the pop-up dialog box, as shown in Figure 1. Now that we've handled thebasics of the app, we can do something more interesting.
Figure 1: Pop-up Dialog Showing the Drink Description
Toast Notifications
In the first instance, we'll look at presenting toast. These are the notifications that wind in from the right side of the screen, usually accompanied by alittle "bing-bing" noise. (The name "toast" comes from the fact that in earlier Windows OSs the notifications "popped up" from the bottom right.) Whatwe'll do is change our code so that rather than using MessageDialog, we'll see the result as a toast notification.
All three notification functions in Windows 8-style apps work in the same way. You can ask a given notification "subsystem" (toast, badge, or tile) to giveyou a "template" that you then fill in with your application-specific data. The template is returned to you as XML, helpfully loaded into a Document ObjectModel (DOM) object. You then you use the standard XML APIs in WinRT to insert your data into the template. Next, you return the modified XML to the chosennotification subsystem whereupon it pushes the XML through to the UI.
The toast and tile notification methods both have a number of templates that you can choose from. For example, there's a toast notification template thatcontains a single string wrapped over three lines, another that has that but with an image, and another that has a string of bold text over three lines anda third line that isn't bold. You can find the complete catalog of toast notification templateson MSDN. For our example, we'll use the basic ToastText01 template, which is just one string wrapped over three lines. When we ask for the XML, we'll getsomething that looks like this:
bodyText
Our objective is to replace the contents of the text element with the output from Barista. To do this, we ask the ToastNotificationManager class for theXML, then make our modifications. We then create a new ToastNotification object and pass that back to ToastNotificationManager. Listing 5 shows the codethat does this.
Listing 5: Passing Output from Barista to ToastNotificationManager
private void HandleBrewClickWithToast(object sender, RoutedEventArgs e) { // make up some coffee... var barista = new Barista(); var result = barista.BrewMeAnything(); // get the XML... var xml = ToastNotificationManager.GetTemplateContent(ToastTemplateType.ToastText01); // replace the contents of 'text'... var text = xml.SelectSingleNode("//text"); text.InnerText = string.Format("Here's your drink:r{0}", result); // ask the OS to show it... var notification = new ToastNotification(xml); ToastNotificationManager.CreateToastNotifier().Show(notification); }
However, if you run that code, the button won't do anything. That's because the application isn't marked as "toast capable." This is a specific manifestsetting that tells WinRT that -- obviously -- the application is set up to display toast. You can modify this option on the Application UI tab of theVisual Studio manifest editor, by selecting Yes for the Toast capable option, as shown in Figure 2. (If you're new to editing the manifest,double-click the Package.appxmanifest file in Solution Explorer.)
Figure 2: Making the App Toast Capable
Now when you run the app and click the button, a toast notification will appear. In fact, you can rapidly click the button and a toast will scroll down theright side of the screen, fading in and out elegantly as it goes. Figure 3 shows one "piece" of toast. (Note that I've changed the default background colorfor the app and added an icon. You can see how I've done this in the source download.)
Figure 3: Sample Toast
Badges
We'll look at badges next, because they're less interesting than tiles. We'll save the best for last! Badges are the small notifications that appear ontiles. They're usually used to indicate that n new things are available for you to review: A classic example is that you have n unreademails available. On a tile, you can choose to display no badge, a number between 1 and 99, or a glyph. The glyphs are predefined, and you can find a list of available glyphs on MSDN.
Because badges don't really make any sense in our example, I'll just show you how to put a random number on the tile. By the way, although the maximumrendered number on a tile is 99, you can specify any number you like. You won't get an error if you specify a value of 100 or more – instead, you'll see alittle plus sign (+) next to the number. Any negative number will result in the badge not being shown. The format of the XML is straightforward; it's justone element with an attribute for you to put the value in. Listing 6 shows the code that displays a random number on a badge.
Listing 6: Displaying a Random Number on a Badge
private void HandleBrewClickWithBadge(object sender, RoutedEventArgs e) { var xml = BadgeUpdateManager.GetTemplateContent(BadgeTemplateType.BadgeNumber); // element... var number = xml.SelectSingleNode("//badge"); number.Attributes.GetNamedItem("value").NodeValue = new Random().Next(100).ToString(); // show... var notification = new BadgeNotification(xml); BadgeUpdateManager.CreateBadgeUpdaterForApplication().Update(notification); }
If you run the application and click the button, the badge will be updated, as shown in Figure 4. You won't see any result until you go back to the Startscreen and scroll through until you find your application.
Figure 4: Sample Tile with Badge
Tiles
Finally, we can look at tiles. At this point you should be able to see how the notifications all work in a similar way. Each discrete notificationsubsystem has a manager class that can return XML, which you modify and return to that subsystem for display. Tiles works in exactly the same way. Wheretiles are more interesting, though, is that they're central to the user experience (UX) in Windows 8 and WinRT as well as on Windows Phone. Tiles shouldtherefore be something in your app that you take some care with because they can greatly increase the value that your application provides to its users.This applies to both retail apps and enterprise-style line of business (LOB) apps.
Thus, to make things a little more interesting, in this section we'll add some images to our tiles. Specifically, we'll add some JPEG files to ourapplication package. Ultimately, what will happen is that these files will be included in our AppX files, whereupon they become analogous to embeddingresources into the application. Once the image files are there, various "touch points" in WinRT exist for referring to AppX packaged resources, and we'reabout to see one of those.
Like the other types of notifications, tile notifications work by allowing you to grab some preformed template XML, then manipulate it and return it to thetile. An interesting wrinkle you'll encounter when working with tiles is that although you might want to use a larger tile than the normal small square,the user might prefer the smaller tile. The user can make the tile larger or smaller by selecting the tile and choosing the large or small option from theapp bar. Mostly you have no control over this user preference. So if you want to use a large tile, it's important that you update both small and largetiles at the same time so that you can deliver value to the app user regardless of the user's choice. (Assuming we can make this work from a UXperspective, of course -- the small tile could be too small for the information we want to show.) Updating both types of tiles lets the user see thepertinent information regardless of his or her preference.
We'll look first at updating a small tile, then we'll proceed to updating the large tile. As per the other notification type, you can find a list of available tile templates on MSDN.In the first (small) instance, we'll use TileSquareText04. This takes a single string but wraps it over four lines. I'll just jump straight into the codefor this example, shown in Listing 7, because it essentially follows the same form as the previous code examples.
Listing 7: Updating a Small Tile
private void HandleBrewClickWithTile(object sender, RoutedEventArgs e) { // make up some coffee... var barista = new Barista(); var result = barista.BrewMeAnything(); // update... UpdateLargeTile(result); UpdateSmallTile(result); } private void UpdateLargeTile(DeliciousDrink drink) { // TBD... } private void UpdateSmallTile(DeliciousDrink drink) { // get the XML... var xml = TileUpdateManager.GetTemplateContent(TileTemplateType.TileSquareText04); // replace the contents of 'text'... var tile = xml.SelectSingleNode("//tile"); var text = tile.SelectSingleNode("//text"); text.InnerText = drink.ToString(); // ask the OS to show it... var notification = new TileNotification(xml); TileUpdateManager.CreateTileUpdaterForApplication().Update(notification); }
The only significant difference is havingseparate methods for updating the large and small tiles. The other thing we have to do is wire up a button so that we can run HandleBrewClickWithTile. Ifyou do that and then run the application, you'll see something like Figure 5.
Figure 5: Small Tile with Notification
If you're unfamiliar with large and small tiles, now is a good time to experiment. On the Start screen, select a large tile (e.g., the News app) and bringup the app bar. You'll see an option to make it smaller. Do this, and -- not surprisingly -- the tile becomes smaller. Also unsurprisingly, you can returnthe tile to being large. Another thing you might notice here is that you can turn off notifications for a given tile. This has the result of asking Windowsto only ever show the tile's icon, rather than a live update. You'll need to accommodate this user preference as well -- which in this case means that youshouldn't make decisions to use the live tile as the only place where certain classes of information are presented. Anything you show in a live tile alsomust be within the application.
To complete our discussion of tiles, we'll now turn our attention to the large icon. The only difference here is that we're going to update an image aswell. For this article, I grabbed three images from Flickr that had an appropriate Creative Commons license. (You can find the attribution in the sourcecode; see the beginning of this article for the download link.) All you have to do is place the images that you want into the Assets folder within yourproject, and Visual Studio will do the rest when it deploys your AppX package. That gets the file into the application; referencing the image isn't muchmore difficult.
Listing 8 shows the XML for the tile template HandleBrewClickWithToast.
Listing 8: XML for the Tile Template HandleBrewClickWithToast
You'll notice it has an image element. That element even comes complete with an srcattribute, so it's pretty clear we have to give that some form of Uniform Resource Identifier (URI). In Windows 8-style apps, we can refer to resourceswithin the AppX package using the ms-appx protocol. So ms-appx:///Assets/Latte01.jpg happens to refer to the Latte01.jpg file in my Assets folder.
If you're walking through creation of the app in this article, you'll need to add some images to your application now. Figure 6 shows you what my Assetsfolder looks like.
Figure 6: Assets Folder
In my app, I have three images that I want to use in rotation. I've created a private int property called ImageIndex for this. Listing 9shows the new code for UpdateLargeTile that includes setting the image.
Listing 9: Code for UpdateLargeTile
// updated version of stub presented previously private void UpdateLargeTile(DeliciousDrink drink) { // get the XML... var xml = TileUpdateManager.GetTemplateContent(TileTemplateType.TileWideImageAndText01); // replace the contents of 'text'... var tile = xml.SelectSingleNode("//tile"); var text = tile.SelectSingleNode("//text"); text.InnerText = drink.ToString(); // put an image in... var image = tile.SelectSingleNode("//image"); ImageIndex++; if (ImageIndex == 3) ImageIndex = 0; var url = string.Format("ms-appx:///Assets/Latte{0}.jpg", ImageIndex + 1); image.Attributes.GetNamedItem("src").NodeValue = url; // ask the OS to show it... var notification = new TileNotification(xml); TileUpdateManager.CreateTileUpdaterForApplication().Update(notification); }
There's one last thing that we need to do. In order for the Start menu to show a large tile, you need to present a base image for large tiles. You do thisby adding a new large tile image into the Assets folder and referencing it from the manifest. Figure 7 shows the manifest change.
Figure 7: Wide Icon in the Manifest
If you run the app now,you'll be able to select the tile and access the larger icon. The larger icon, with its image, will look far more attractive, as shown in Figure 8.
Figure 8: Wide Tile with Image
And that's it! We've been through the basic local notifications. All that remains is a short explanation of off-device-originated notifications and finallya wrap-up.
Notifications via Windows Push Notification Service
In this article we've looked solely at creating notifications within our application and pushing them through the WinRT notification subsystems. Anotherway of working with notifications is to accept updates coming in from a cloud server of some description. Windows 8-style apps have first-class support forthis type of notification via the Windows Push Notification Service (WNS). I'm not going to go into detail about WNS in this article because that topic isworthy of an article in its own right. However, to round out your understanding of notifications, we'll take a quick look at WNS.
The WNS-based notifications are roughly the same shape as the local notifications. When you want to use WNS for notifications, you have to register theclient installation with WNS. This works as you'd expect it to: You ask WNS for some sort of token (which happens to be formatted as a URI), and you lodgethis token in your cloud-based database. On the server you'll receive some signal that an activity has taken place -- for example, a new follower has beenadded, or a new message has been received. Upon receipt of that signal, you'll create an XML document of the same format as the local notifications andthen from the cloud-server call the WNS server directly with that XML and the token you received on registration. WNS will then endeavor to deliver themessage to the device, with all the vagaries that implies (e.g., does the device still exist, has the app been uninstalled). Ultimately, though, uponsuccessful receipt of a message, WinRT will display the notification just as if it had been created locally.
Know Your Notifications
In this article we've taken a detailed look at local notifications in Windows 8-style apps. All three of the notification subsystems work in the same way:You ask the individual manager classes to return you some template XML, which you populate and then return to the manager to display. Although all thenotification formats are important, tiles are possibly the most important because effective use of tiles can greatly enhance the UX of your application.
Learn more about how to develop Windows 8 apps:
Matthew Baxter-Reynoldsis an independent software development consultant, speaker, author, and trainer. He's an occasional contributor to the Guardian's technologysection, and his next book, Programming Windows 8 Apps in C# (O'Reilly), is available in December 2012.
Twitter: @mbrit
Website: http://mbrit.com
Read more about:
MicrosoftAbout the Author
You May Also Like