Monday, May 6, 2013

A Fluent Approach to Windows Store Tiles

Windows Store apps have a variety of tricks up their sleeve for engaging the user even when they are not running. Tiles are a perfect example because they provide at-a-glance information to the user from the start screen. Tiles can provide images, text, or a combination of both and support queuing multiple notifications.

Tiles are defined by various pre-built XML templates. The catalog of tiles is available online in the tile template catalog and can be enumerated via TileTemplateType as defined in the Windows.UI.Notifications namespace. The sample apps for Windows 8 from Microsoft include a helper that allows you to create tiles but it requires instantiating specific classes. I wanted to create a more straightforward and fluent method for defining and setting tiles.

It turns out you can get the template for any tile type by calling GetTemplateContent on the TileUpdateManager class with the TileTemplateType you are interested in. Here’s a fast way to get the XmlDocument:

this.xml = TileUpdateManager.GetTemplateContent(templateType);

With the template I can easily inspect how many lines of text and images it is capable of supporting:

this.TextLines = this.xml.GetElementsByTagName("text").Count;
this.Images = this.xml.GetElementsByTagName("image").Count;

Now I can add text and raise an exception if the amount of text the tile can hold is exceeded:

public BaseTile AddText(string text, uint id = 0)
{
     if (string.IsNullOrWhiteSpace(text))
     {
         throw new ArgumentException("text");
     }
     if (id == 0)
     {
         id = this.textIndex++;
     }
     if (id >= this.TextLines)
     {
         throw new ArgumentOutOfRangeException("id");
     }
     var elements = this.xml.GetElementsByTagName("text");
     var node = elements.Item(id);
     if (node != null)
     {
         node.AppendChild(this.xml.CreateTextNode(text));
     }
     return this; }

Notice that the method returns the class instance itself. This sets the class up to support a fluent interface with multiple commands chained together. The same logic is used to add images. When you allow your app to provide both a wide and traditional square tile, you can update both formats with a single call. Therefore, you should be able to merge the definitions of multiple tiles:

public BaseTile WithTile(BaseTile otherTile)
{
     var otherBinding = this.xml.ImportNode(
        otherTile.xml.GetElementsByTagName("visual")[0].LastChild,
        true);
     this.xml.GetElementsByTagName("visual")[0].AppendChild(otherBinding);
     return this; }

Now we can grab the template for a tile, specify text and images, and combine square and rectangular tiles together. The next step is to actually set the tile for the app:

public void Set()

{
    TileUpdateManager.CreateTileUpdaterForApplication().Update(
        new TileNotification(this.xml));
}

I then added an extension method that takes in the type of the tile and returns an instance of my helper class that contains all of the methods described in this post. Putting it all together, the code to set a wide and square tile for an example app I’m building that enumerates all tiles on the system to display the XML looks like this:

// set some default tiles 
TileTemplateType.TileWideText03.GetTile()

                    .AddText("Tile Explorer")
                    .WithTile(TileTemplateType.TileSquareText03.GetTile()
                        .AddText("Tile Explorer")
                        .AddText("A WinRT Example")
                        .AddText("by Jeremy Likness"))
                    .Set();

This certainly makes it easier to update tiles. A complete helper will also include similar methods for badges and notifications of course … that’s all part of the work I’m doing. Full source and examples will be available in my upcoming book and I’ll share more details when I have them.