Windows Store app Development Snack: Secondary tiles with text

The call to pin a secondary tile looks like this:

SecondaryTile(
    string tileId,
    string shortName,
    string displayName,
    string arguments,
    TileOptions tileOptions,
    Uri logoReference
);

The important part for this post is the last parameter: Uri logoReference. This is the path to the image you want to show on the tile—but I had a problem: I didn’t want to show an image! I just had some text I wanted to display on the tile. After a lot of digging, the solution was non-trivial—generate an image at runtime. This was made even harder because the Render method in WPF does not exist in the XAML implementation used in WinRT.

WinRT does include a WritableBitmap class, which allows you to create an in-memory bitmap, manipulate the pixels, and save to a file format using BitmapEncoder classes. The problem for me was that I did not want to fiddle with pixels manually—this led me to WritableBitmapEx, which is a great library for having primitives (fill, line, circle, etc.). The only downside was that I wanted text, not just graphic primitives.

title (original)

More searching led me to two posts on Stack Overflow which provided a solution:

  1. Create a sprite map using a free tool called SpriteFont201
  2. Use the code provided in the answers with WritableBitmapEx to extract the sprites and combine them with a WritableBitmap.

I took the code and adjusted it slightly so text would always be centered and allowed me to play with font scaling. I’ve attached the modified code below.

In the end, the code I used looks like this:

public async Task<StorageFile> CreateImage()
{
    uint width = 512;
    uint height = 512;
    var writableBitmap = BitmapFactory.New((int)width, (int)height);
    writableBitmap.Clear((App.Current.Resources["SecondTileColour"] as SolidColorBrush).Color);

    writableBitmap.DrawStringHorizontallyCentered(this.DisplayPostalCode, 50, "title", Colors.White, 4);
    writableBitmap.DrawStringHorizontallyCentered(this.Town, 175, "title", Colors.White, 2);
    writableBitmap.DrawStringHorizontallyCentered(this.City, 275, "title", Colors.White, 2);
    writableBitmap.DrawStringHorizontallyCentered(string.Format("box code: {0}", this.BoxCode), 375, "title", Colors.White, 2);
    writableBitmap.DrawStringHorizontallyCentered(string.Format("street code: {0}", this.StreetCode), 450, "title", Colors.White, 2);

    var file = await Windows.Storage.ApplicationData.Current.LocalFolder.CreateFileAsync(
        Guid.NewGuid().ToString("N"),
        Windows.Storage.CreationCollisionOption.ReplaceExisting
    );

    using (var fileStream = await file.OpenAsync(Windows.Storage.FileAccessMode.ReadWrite))
    {
        var encoder = await BitmapEncoder.CreateAsync(BitmapEncoder.PngEncoderId, fileStream);

        encoder.SetPixelData(
            BitmapPixelFormat.Bgra8,
            BitmapAlphaMode.Straight,
            width,
            height,
            96,
            96,
            writableBitmap.ToByteArray()
        );

        await encoder.FlushAsync();
    }

    return file;
}