Building on-the-fly images for WP7 live tiles

28Feb

Windows Phone 7.5 (Mango) lets your app create multiple live tiles on the phone's start screen (with the ShellTile api). This is a very cool way to provide deep links into your application. It's used brilliantly by the the native apps for contacts (you can pin people and see their individual updates), office (you can pin documents), and music & videos (you can pin albums and playlists).
When you create a new ShellTile, you get to specify one or two image URIs (for the front and back). If you use a relative URI, then the shell will look for the image inside your XAP - just like any other image URI in your app. But that means you only get to use images that you ship with your app - how does the music & video app render the album art for each tile? What if you want to take a screenshot of the current game and use that?
One answer is to use or generate images on the internet and use an http-schemed URI, but that approach can cause the tile to unpin when you turn off your phone (I don't know if that's a bug or a feature), and you'll also be relying on an internet connection.
The best option is to save the image to your application's isolated storage, because you are free to save whatever file you like there (with full control of the content and format), and the image will always be available (so your tile won't unpin). If you've already sourced your image from the internet, then you can just save it to iso storage, but what if you want to generate an image on the fly? Have a look at the mail app, and how it tells you how many emails you've got in big letters. If you've got no unread email, it adjusts the position of the email icon too. This image is probably built on the fly by a background process.
But what's a good way of rendering text and images to an image file?
In my opinion, the best way to render custom, dynamic content in a Silverlight app is to leverage the platform: just render some XAML. If you create a 173 by 173 control, you've got full control of how those pixels will look. You can even use databinding. You can use the WriteableBitmap. It's got a constructor that takes in a UIElement (which will immediately render that element into the bitmap) and a SaveJpeg extension method that will write the bitmap to a file.
In my Bus Alert London WP7 app, I used this technique to render custom tile icons with maps in the background:
[caption id="attachment_237" align="alignnone" width="480" caption="Bus Alert Tiles"][/caption]
In the process, I discovered a few very important gotchas:

The UIElement that you render has to be in the application's visual tree when you call the WriteableBitmap constructor. Otherwise, it may not render at all, may lay out incorrectly, may not have access to the correct StaticResources, or databinding may not occur. You can put it in the visual tree, render it and immediately hide it so that the user never sees it if you like. Unfortunately, this rules out the use of XAML to produce live tiles from background agents.

The image must be stored under the special directory \Shared\ShellContent in your isolated storage folder or it will not be accessible by the operating system.

If you don't close the image's filestream before creating the live tile you will see a weird bug where the live tile's image is only visible when you are moving it around the start screen