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 notification system 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 toast to 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 badge showing 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 an application that "brews" randomly selected espresso-based coffee drinks. We'll build a little engine that generates random drink choices, then informs the user 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#, Windows Metro 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 run through 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 the sort 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 that will 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 instance of DeliciousDrink containing whatever has been "brewed." Listing 2 shows the code for the Barista class; Listing 3 shows the code for the DeliciousDrink class.

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 when actually 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 screen using 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 an event 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, then render 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 the basics of the app, we can do something more interesting.


143993_fig1_popup_drink_description-sm
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 a little "bing-bing" noise. (The name "toast" comes from the fact that in earlier Windows OSs the notifications "popped up" from the bottom right.) What we'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 give you 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 Object Model (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 chosen notification 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 that contains 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 and a third line that isn't bold. You can find the complete catalog of toast notification templates on 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 get something that looks like this:

<toast>
    <visual>
        <binding template="ToastText01">
            <text id="1">bodyText</text>
        </binding>
    </visual>
</toast>


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 the XML, then make our modifications. We then create a new ToastNotification object and pass that back to ToastNotificationManager. Listing 5 shows the code that 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\n{0}", result);

            // ask the OS to show it...
            var notification = new ToastNotification(xml);
            ToastNotificationManager.CreateToastNotifier().Show(notification);
        }