Today I’ll help you add some bling to your UWP apps (and maybe something useful) with scheduled live tiles using the built-in features on the W10 SDK and some Azure help – although any cloud storage provider should do fine. I’ll divide this into the “backend stuff”, where we’ll generate our tile templates, and the “client stuff”, where your app will consume the tile notifications.

It goes without saying that you need Visual Studio (VS) and the W10 SDK, which I assume you have installed.

I’ll provide some examples from my pet project Pocketnow WP, so don’t worry if you see some references to it 🙂

1. The backend stuff

Here I’ll make use of some Azure features, so it’s best that you have a working subscription. What we want to achieve is a set of XML files on blob storage with the tile templates. You can use any other means to achieve this, as long as the end result remains the same.

This particular setup is free in Azure given that the tile generation is simple and doesn’t usually involve much data IO or CPU.

First off, please a create a mobile service on the management portal, or use an existing one if your app already makes use of it. I won’t go over the details, so I assume you already have some experience on the subject. In the end, I want you to be on the “Get started” page, ready to download your starter project (in C#). If you already have a mobile services project in your solution, use that one and skip this step.

dashboard

After opening the project in VS, you’ll have the default mobile service project structure with lots of unnecessary things (for us). We just want to focus on the ScheduledJobs folder where we will put our brilliant new job to generate the templates:

mobile5

You could use the default “SampleJob.cs” as the starting point, but let’s add a new one to keep it clean:

mobile6

Give it a fancy name ending with “Job” (this is important!)

mobile7

You’ll end up with an empty class like this:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;

namespace pocketnoww10Service.ScheduledJobs
{
    public class LovelyLiveTileGeneratorJob
    {
    
    }
}

Next, we need to make it runnable by Azure, so make it inherit from ScheduledJob and add the ExecuteAsync method:

public class LovelyLiveTileGeneratorJob : ScheduledJob
{
    public async override Task ExecuteAsync()
    {
        Services.Log.Info("Hello from scheduled job! The time is precisely " + DateTime.Now.ToUniversalTime());
    }
}

You should now be able to publish the mobile service to Azure and test it, but let’s add the rest of the code before we try it.

The next step is to generate the templates as described in the SDK reference. I’ll produce a string version of the XML step-by-step, but you can use the code provided in the MSDN reference. I’m adding a new UpdateLiveTileFile method which will generate the tile template code and write it to a container in Azure blob storage. In this next bit of code I assume that I have a model for my stories which I want to appear in my users’ live tiles, entering the method as a list. Inside I’ll iterate them and build template strings:

 

private async Task UpdateLiveTileFile(IEnumerable<StoryModel> stories)
{
    foreach (StoryModel story in stories)
    {
		string xmlContent = 
			"<tile>" +
			"  <visual branding=\"nameAndLogo\">" +
			"    <binding template=\"TileMedium\" branding=\"name\">" +
			"      <text hint-style=\"body\" hint-wrap=\"true\">" + System.Security.SecurityElement.Escape(story.Title) + "</text>" +
			"     <image placement=\"background\" src=\"" + System.Security.SecurityElement.Escape(story.PictureSource) + "\" hint-overlay=\"40\" />" +
			"    </binding>" +
			"    <binding template=\"TileWide\">" +
			"      <group>" +
			"        <subgroup hint-weight=\"33\">" +
			"          <image placement=\"inline\" src=\"" + System.Security.SecurityElement.Escape(story.AuthorPictureSource) + "\" hint-crop=\"circle\" />" +
			"        </subgroup>" +
			"        <subgroup>" +
			"      <text hint-style=\"body\" hint-wrap=\"true\">" + System.Security.SecurityElement.Escape(story.Title) + "</text>    " +
			"      </subgroup>" +
			"      </group>" +
			"     <image placement=\"background\" src=\"" + System.Security.SecurityElement.Escape(story.PictureSource) + "\" hint-overlay=\"40\" />" +
			"    </binding>" +
			"    <binding template=\"TileLarge\">" +
			"      <group>" +
			"        <subgroup hint-weight=\"33\">" +
			"          <image placement=\"inline\" src=\"" + System.Security.SecurityElement.Escape(story.AuthorPictureSource) + "\" hint-crop=\"circle\" />" +
			"        </subgroup>" +
			"        <subgroup>" +
			"        <text hint-style=\"caption\" hint-wrap=\"true\">" + System.Security.SecurityElement.Escape(story.Title) + "</text>    " +
			"        </subgroup>" +
			"      </group>" +
			"      <image placement=\"inline\" src=\"" + System.Security.SecurityElement.Escape(story.PictureSource) + "\" />      " +
			"    </binding>" +
			"  </visual>" +
			"</tile>";
    }
}

To protect your eyes, here’s a generated template using this code. You can see 3 binding sections, each for a different tile size (in this case Medium, Wide and Large). Again, check the reference for a description of the options:

<tile>
  <visual branding="nameAndLogo">
    <binding template="TileMedium" branding="name">
      <text hint-style="body" hint-wrap="true">Samsung Galaxy Tab S2 9.7 treated to Marshmallow makeover in Europe</text>
     <image placement="background" src="http://pocketnow.com/images/300/190/1454330272/2016/04/Galaxy-Tab-S2-9.7-1.jpg" hint-overlay="40" />
    </binding>
    <binding template="TileWide">
      <group>
        <subgroup hint-weight="33">
          <image placement="inline" src="http://pocketnow.com/images/50/50/1454330272/userphoto/72.jpg" hint-crop="circle" />
        </subgroup>
        <subgroup>
      <text hint-style="body" hint-wrap="true">Samsung Galaxy Tab S2 9.7 treated to Marshmallow makeover in Europe</text>    
      </subgroup>
      </group>
     <image placement="background" src="http://pocketnow.com/images/300/190/1454330272/2016/04/Galaxy-Tab-S2-9.7-1.jpg" hint-overlay="40" />
    </binding>
    <binding template="TileLarge">
      <group>
        <subgroup hint-weight="33">
          <image placement="inline" src="http://pocketnow.com/images/50/50/1454330272/userphoto/72.jpg" hint-crop="circle" />
        </subgroup>
        <subgroup>
        <text hint-style="caption" hint-wrap="true">Samsung Galaxy Tab S2 9.7 treated to Marshmallow makeover in Europe</text>    
        </subgroup>
      </group>
      <image placement="inline" src="http://pocketnow.com/images/300/190/1454330272/2016/04/Galaxy-Tab-S2-9.7-1.jpg" />      
    </binding>
  </visual>
</tile>

Now that we have our generated templates, we need to store them on Azure (or your equivalent). If you already have a storage account you can use it and skip the first step, but please create a new container:

First, create a storage account:
storage1

Then create a container:

storage2

Now we need to get a connection string to connect the mobile service to this storage account. To do this go to the dashboard and check the access keys in the bottom app bar, then take note of your storage account name (if you already forgot it) and copy the primary access key:

storage3

storage4

We can now add a new connection string to our web.config to connect to this storage account, so open it (should be on the project root) and add a new connection called “StorageConnectionString” inside the connectionStrings tag using the access key you copied. It should look like this:

<connectionStrings>
    <add name="MS_TableConnectionString" connectionString="XXXX" providerName="System.Data.SqlClient" />
    <add name="MS_NotificationHubconnectionString" connectionString="XXXX" />
    <add name="StorageConnectionString" connectionString="DefaultEndpointsProtocol=https;AccountName=<YOUR_STORAGE_ACCOUNT>;AccountKey=<YOUR_PRIMARY_ACCESS_KEY>" />
</connectionStrings>

Now we need to connect to the container using this connection string. In the beginning of UpdateLiveTileFile method add some initialization code (check the inline comments):

// Get the connection string
CloudStorageAccount storageAccount = CloudStorageAccount.Parse(ConfigurationManager.ConnectionStrings["StorageConnectionString"].ConnectionString);

// Get a blob client
CloudBlobClient blobClient = storageAccount.CreateCloudBlobClient();

// Create or get an existing container (use a nice name for this, AND NOT HARDCODED)
CloudBlobContainer container = blobClient.GetContainerReference("LovelyLiveTileGeneratorContainerName");
bool result = await container.CreateIfNotExistsAsync();

// Set the permissions on the container to public so that client apps can access it
// This means you shouldn't use this container for anything else
await container.SetPermissionsAsync(new BlobContainerPermissions
{
    PublicAccess = BlobContainerPublicAccessType.Blob
});

Finally, prepare our generation loop to write to the blob for each tile with a different number each time. In the end the loop should look like this.

// Start an iterator to be appended to the file name
int i = stories.Count() - 1;
foreach (StoryModel story in stories)
{
    #region Template generation code
    ...
    #endregion

    // Get a file reference -> CHANGE THE NAME OF THE FILE!
    CloudBlockBlob fileBlob = container.GetBlockBlobReference(string.Format("LovelyLiveTileFile-{0}.xml", i));

    // And write to the file
    await fileBlob.UploadTextAsync(xmlContent);
    i--;
}

Our next step is to publish the mobile service to Azure. To ease the process you can download a publish profile from your mobile service dashboard:

mobile8

Then go to your project and hit Publish from the context menu:

mobile9

Now browse to the publish profile you just downloaded:

mobile10

mobile11

Then press OK till you reach the last page, where you press Publish:

mobile12

You should now run the job once to ensure the files are being generated. To achieve this, go to the mobile service page on the azure management portal and access the “Scheduler” tab. Here you should open the job you just published:

schedulerThen click “Run once”

run_once

If all went well (no reason it shouldn’t have), if you access your blob container URL directly from a browser you should be presented with the generated tile templates:

blob

That’s it for our first part! Now comes the easier bit – consume the files we’ve just generated.


2. The client stuff

It’s considerably easy to consume the generated tile template files from a UWP app. I usually put my code on the App.xaml.cs, but it can go anywhere. Here’s a summary of the steps you need to do:

  1. Instantiate a TileUpdater
  2. Set a list of URIs to be polled
  3. Enable queued notifications
  4. Set the polling frequency
  5. Start polling
  6. Profit

This translates to very simple code that can run at startup or on demand (if you tie it to your settings page).

First off, add a new method on your App.xaml.cs (or wherever you want a method to subscribe your app to tile updates):

public static void SubscribeToTileNotifications()
{
	TileUpdater tileUpdater = TileUpdateManager.CreateTileUpdaterForApplication();
}

Now then, we need a way to turn tile updates on and off. I usually do this by writing a bool on my roaming settings (usually on a settings page which I won’t include), but you can use whichever method you prefer. So, let’s assume that somewhere in your app there’s a process that writes to the tag “LIVE_TILE_ONOFF” on the roaming settings and add some logic to the subscribe method you just created:

ApplicationDataContainer appSettings = ApplicationData.Current.RoamingSettings;
if (appSettings.Values.Keys.Contains("LIVE_TILE_ONOFF")
    && (bool)appSettings.Values["LIVE_TILE_ONOFF"])
{
   // subscription code
}
else
{
   // unsubscription code
}

Next, we need to construct a list with URLs pointing to the templates we are creating on the blob container. A simple loop should do (don’t forget to set your blob container URL on TILE_UPDATE_URL), so add it under the subscription code comment:

List<Uri> tileUpdateUris = new List<Uri>();
string TILE_UPDATE_URL = "https://<YOUR_STORAGE_ACCOUNT>.blob.core.windows.net/LovelyLiveTileGeneratorContainerName/LovelyLiveTileFile-{0}.xml";
int TILE_UPDATE_SIZE = 5;
for (int i = 0; i < TILE_UPDATE_SIZE; i++)
{
	tileUpdateUris.Add(new Uri(string.Format(TILE_UPDATE_URL, i)));
}

We can now ask the tile updater to subscribe to notifications. There are several options for the update recurrence, but I’ll stick with an hardcoded hourly update.

tileUpdater.EnableNotificationQueue(true);
tileUpdater.StartPeriodicUpdateBatch(tileUpdateUris, PeriodicUpdateRecurrence.Hour);

Finally, you should also add some code to unsubscribe from notifications (by user command), so add this under the unsubscription code comment on the else block:

tileUpdater.Clear();
tileUpdater.EnableNotificationQueue(false);
tileUpdater.StopPeriodicUpdate();

In the end you should end up with something like this:

public static void SubscribeToTileNotifications()
{
	TileUpdater tileUpdater = TileUpdateManager.CreateTileUpdaterForApplication();

	ApplicationDataContainer appSettings = ApplicationData.Current.RoamingSettings;
	if (appSettings.Values.Keys.Contains("LIVE_TILE_ONOFF")
        && (bool)appSettings.Values["LIVE_TILE_ONOFF"])
	{
		List<Uri> tileUpdateUris = new List<Uri>();
		string TILE_UPDATE_URL = "https://<YOUR_STORAGE_ACCOUNT>.blob.core.windows.net/LovelyLiveTileGeneratorContainerName/LovelyLiveTileFile-{0}.xml";
		int TILE_UPDATE_SIZE = 5;
		for (int i = 0; i < TILE_UPDATE_SIZE; i++)
		{
			tileUpdateUris.Add(new Uri(string.Format(TILE_UPDATE_URL, i)));
		}

		tileUpdater.EnableNotificationQueue(true);
		tileUpdater.StartPeriodicUpdateBatch(tileUpdateUris, PeriodicUpdateRecurrence.Hour);
	}
	else
	{
		tileUpdater.Clear();
		tileUpdater.EnableNotificationQueue(false);
		tileUpdater.StopPeriodicUpdate();
	}
}

All that’s left to do is call your new method SubscribeToTileNotifications from somewhere in your app and you’re done, your tiles will come to life!

1 COMMENT

  1. Hi,
    I have recently developed an UWP application with adaptive Live Tiles. I have many pages. One of them is “Current Forecast” page where I have placed code for adaptive Live Tile inside OnNavigatedTo event. This page loads into Main Page by default. So it will be called every time the app is opened. Live Tiles are updated locally using data from Local Cache.

    The problem is that Live Tiles work as expected when I run the app in Debug mode (x86) in Visual Studio however it fails to work when I install it using its appx package.

    Kindly help me out. I am kind of stuck and I cannot find solution to this problem.

    Regards,
    Satish,
    Email: satish12321@gmail.com

LEAVE A REPLY

Please enter your comment!
Please enter your name here