Custom live tiles in Windows Runtime apps – in Azure and Local

Custom live tiles
Standard

Live tiles have long been a key differentiator for the modern Windows platform. Since their introduction in Windows Phone 7 Microsoft sold live tiles as “dynamically updated… breaking the mould of static icons” (source). The hope from many developers was to be able to create customised panels of information that would draw users into the app or present them with a simple overview of the information that mattered. The reality is that using Microsoft’s standard tools presents a very simple experience whereby some text, a number of images, and possibly a count can be displayed on a live tile at any one point.

Even with the expansion of live tiles in to the Windows 8 platform the standard templates available to developers often lead to a start screen of similar tiles that do not differ visually from one another. There’s nothing inherently wrong with that similarity, but there are a lot of situations the normal tile templates do not account for. For instance showing a graph of information, or a customised layout to avoid having white text over a light background image cannot be done in with the normal templates.

In Windows Phone 7 and 8 apps, based on the Silverlight framework, developers could use an image rendering method to transform XAML into a locally stored image, then update the tile in a background task. In Windows Runtime apps (or universal apps if you prefer) this is no longer supported. So how can we create customised live tiles to match a user’s expectations?

This post will explore the 3 methods of updating tiles with custom images, through foreground C# code, through background native C++ tasks, and by using remotely store tiles in an Azure component.

MyFitnessPal live tile

The custom nature of the MyFitnessPal live tile makes it one of the most useful tiles on my start screen

C# – foreground only

I’m going to start with the one that doesn’t work all that well. If you want to render to your live tile only when your app is launched (or general in the foreground), then you can do the update using a little trick in managed C# code.

Step 1: create a User Control with a XAML front end and use that XAML to construct however you want your tile to look

Step 2: put your control somewhere on your page, but have it render behind your main grid so that the user never sees it

Step 3: once the control is loaded, use the RenderTargetBitmap class to convert it into an image, as demonstrated below:


RenderTargetBitmap renderTargetBitmap = new RenderTargetBitmap();
await renderTargetBitmap.RenderAsync(MyControlName);
var pixelBuffer = await renderTargetBitmap.GetPixelsAsync();
var file = await ApplicationData.Current.LocalFolder.CreateFileAsync("NameTile.png", CreationCollisionOption.ReplaceExisting);
using (var stream = await file.OpenAsync(FileAccessMode.ReadWrite))
{
      var encoder = await BitmapEncoder.CreateAsync(BitmapEncoder.PngEncoderId, stream);
      encoder.SetPixelData(
                    BitmapPixelFormat.Bgra8,
                    BitmapAlphaMode.Ignore,
                    (uint)renderTargetBitmap.PixelWidth,
                    (uint)renderTargetBitmap.PixelHeight, 96d, 96d,
                    pixelBuffer.ToArray());
      await encoder.FlushAsync();
}

You can now access the above image when creating a tile by setting it’s source to:
"ms-appdata:///local/NameTile.png"
If you’d like more information on how to create a tile programmatically in C#, I highly recommend using the Notifications Extensions library, you can read all about how to do that here: Quickstart: Using the NotificationsExtensions library in your code.

For updating from the foreground this works well, however you will not be able to use this method in background tasks. Even using a XAMLRenderingBackgroundTask, if you’re lucky you may render one image before you hit the memory limitations now imposed on Windows Runtime apps. I advise you avoid it completely.

C++ native updater

Now we’re into the more tricky part. It is possible to use a C++/CX component to natively update a live tile from a background task. There is a full sample from Microsoft available right here which you should immediately download and open up before reading any further, as it illustrates what is required better than I will be able to.

I’ll break down the good and bad of this method shortly but first I must warn you that there is a bug in Microsoft’s sample that you should fix first:

The sample has you use the InstalledLocation of your app for storing the temporary image. This works in side loaded apps but will fail when downloaded through the store. So, change:

auto resultStorageFolder = Windows::ApplicationModel::Package::Current->InstalledLocation;

To:

auto resultStorageFolder = Windows::Storage::ApplicationData::Current->LocalFolder;

Then change:

UpdateTile(“updatedTile.png”);

To:

UpdateTile(“ms-appdata:///local/updatedTile.png”);

Which ensures the variable “tileUpdateImagePath” will be set with the correct URI to the local path.

With that out of the way, let’s break down how this code works:

  •  XAML describing the tile layout is stored in the C# project’s ‘Assets’ folder, defined as an XML file (this can be copied to a user control if you find that easier to use with the design view)
  •  ‘BackgroundTaskCX.AppTileUpdater’ is registered in the ‘Sceanrio1_Register.xaml.cs’ page
  •  The task trigger is set to occur on a system time zone change, you will want to change to a TimeTrigger set to the desired update frequency (note that 15 is the most frequent you can set)
  •  The C++ executes, loads the XML into a XamlReader and then updates the text in the XAML document
  •  An image is generated and stored locally, then pushed with a local notification to update the tile
  • This particular sample also sets the background to a random colour

You’ll notice that this sample provides no information on how you might go about retrieving information from a web feed in C++, so I’ve provided a small sample below you could try using


HttpClient^ client = ref new HttpClient();
Uri^ uri = ref new Uri("http://someaddress.com");
concurrency::cancellation_token_source cts = cancellation_token_source();
return create_task(client->GetAsync(uri), cts.get_token()).then([this, client, count](HttpResponseMessage^ response)
{
 return create_task(response->Content->ReadAsStringAsync()).then([this, response, client, count](String^ content)
 {
  if (response->IsSuccessStatusCode)
  {
   JsonObject^ json = JsonObject::Parse(content);
   // Navigate to main node
   auto resp = json->GetNamedObject("response");
   return;
  }
 });
});

I can’t claim in any way to be a C++ expert, something I hope to change over time, but you’ll be able to use the Json object above (which assumes the root node is called “response”) to retrieve simple information to be entered into your tile.

At UI Centric we have been able to utilise this C++ method successfully to render up to 2 images in a background task. Any more than that and we begin to hit memory limitations on the lower end 512mb devices.

So there is a working method for custom background updates, there are some circumstances where the render will not fully complete and you end up with a blank tile, but those are thankfully the minority. Unfortunately, this will only work on Windows Phone 8.1, as Windows 8.1 has no XamlRenderingBackgroundTask class to use. That means we might be back to square one if we want a universal solution.

Azure remote tile generation

Enter the option to render images on a server, use the full tile image template and style your images to look like custom live tiles.

This approach has been discussed as possible around the web, but it is surprisingly difficult to find real examples on how it may be achieved. In the interest of keeping this post to a readable length I’m going to summarise how we architected this service and provide a few samples that will hopefully show the path.

Azure VM tile solution

Diagram of live tiles generated on a server

Windows Service running on an Azure VM

Whilst it’s not the most powerful way to use the capabilities of Azure, generating the images from a WPF XAML control required some form of System.Windows.Window to render the UI and then capture the rendered item to a file. So we’re running this code on a virtual machine using Windows Server 2012, with a service installed to do the actual rendering work every 1-2 minutes.

For this purpose I created a base class which all of my tile user controls could then inherit from, making each one “renderable”. The source code for that base class is available in full below:


 
 
    public class TileBase : UserControl
    {
        public void Render(string outputPath)
        {
            Window wind = new Window();
            wind.WindowStyle = WindowStyle.None;
            wind.ShowInTaskbar = false;
            wind.ShowActivated = false;
            wind.AllowsTransparency = false;
            wind.WindowState = WindowState.Minimized;
            wind.SizeToContent = SizeToContent.WidthAndHeight;
            this.InvalidateVisual();
            this.InvalidateArrange();
            this.UpdateLayout();
            wind.Content = this;
            wind.Show();
            BitmapSource bitmapSrc = CaptureScreen(wind, Constants.dpiX, Constants.dpiY);
            PngBitmapEncoder encoder = new PngBitmapEncoder();
            encoder.Frames.Clear();
            encoder.Frames.Add(BitmapFrame.Create(bitmapSrc));
            using (Stream outputStream = File.Create(outputPath))
            {
                encoder.Save(outputStream);
                outputStream.Flush();
                outputStream.Close();
            }
            bitmapSrc = null;
            wind.Content = null;
            wind.Hide();
            wind.Close();
        }

        private BitmapSource CaptureScreen(Visual target, double dpiX, double dpiY)
        {
            if (target == null)
            {
                return null;
            }
            Rect bounds = VisualTreeHelper.GetDescendantBounds(target);
            RenderTargetBitmap rtb = new RenderTargetBitmap((int)(bounds.Width * dpiX / 96.0),
                                                            (int)(bounds.Height * dpiY / 96.0),
                                                            dpiX,
                                                            dpiY,
                                                            PixelFormats.Pbgra32);
            DrawingVisual dv = new DrawingVisual();
            using (DrawingContext ctx = dv.RenderOpen())
            {
                VisualBrush vb = new VisualBrush(target);
                ctx.DrawRectangle(vb, null, new Rect(new Point(), bounds.Size));
            }
            rtb.Render(dv);
            return rtb;
        }
    }

Note that the values used in the Constants class for dpiX and dpiY are both 96.0.

From this base control, a usercontrol can be created and defined in XAML. One call to the Render(string outputPath) method will create a tile image from the XAML. Remember when designing live tiles the maximum resolution of each tile type:

  • Square: 360px x 360px
  • Wide: 744px x 360px
  • Large: 558px x 558px (Windows 8.1 only, not phone)

Which is great, but the actual tile size at normal 100 scale is:

  • Square: 150px x 150px
  • Wide: 310px x 150px
  • Large: 310px x 310px (Windows 8.1 only, not phone)

So when you’re creating a XAML user control, it’s a good idea to start with a Viewbox defined at the higher values, containing a Grid set to the lower values, that should produce a nice high definition render.

Locally generated XML and image files served through IIS

Once these tiles are generated and saved somewhere local on your virtual machine, you need to create an XML file that will expose them to your phone/tablet app. The resulting XML will need to look something like the example below:

<?xml version="1.0"?>
<tile>
<visual lang="en" addImageQuery="true" version="2">
<binding template="TileSquare150x150Image" branding="none">
<image id="1" src="http://mywebaddress.com/myVirtualDirectory/an_image_square.png"/>
</binding>
<binding template="TileWide310x150Image" branding="none">
<image id="1" src="http://mywebaddress.com/myVirtualDirectory/an_image_wide.png"/>
</binding>
<binding template="TileSquare310x310Image" branding="none">
<image id="1" src="http://mywebaddress.com/myVirtualDirectory/an_image_large.png"/>
</binding>
</visual>
</tile>

 

You can now place this XML file in the same directory as the images that have been generated. All that comes now is to make the tile available to the web at large. For that we can enable IIS on the Azure virtual machine and then simply create a virtual directory in the default web site for your server. There’s a good guide from Microsoft here if you’re not familiar with the process. Don’t forget that you will need to have added “HTTP” as an endpoint in the Azure management dashboard for this virtual machine before port 80 will be available to serve web content!

No background tasks required

Possibly the biggest benefit of this method is just how lightweight it is for the client apps. For a universal app all that is required is to use the TileUpdater class here, and start a periodic update pointing at the Uri of the XML file generated in the last step. The operating system takes care of the rest, including only retrieving the image file when it is about to be displayed, and caching the image locally so long as it matches the previous Uri (so make sure your images are always generated with unique names and update your XML with the new name accordingly).

What if you want multi-sided tiles? Also very easy, just use a periodic update batch, and create one XML file per side, giving you multiple Uris to use in the StartPeriodicUpdateBatch method. Even with all these images the battery use and network load on your user’s devices will be the lowest possible, as you don’t need to download any feeds or other data before also downloading an image to use in the normal tile templates, the Azure virtual machine takes care of all of that for you.

3 thoughts on “Custom live tiles in Windows Runtime apps – in Azure and Local

  1. cabuxa.mapache

    This is the best guide so far about live tiles in Wp 8.1 i’ve found out there. Thank you very much for that awesome post. Keep in the good work ;)

  2. Frank Szendzielarz

    It is also possible to use GDI+ on an Azure Website now. There were problems in the past. You can use an Azure WebJob to do this.

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>