Universal Windows Platform – Chaseable Tiles

Chaseable Tiles demonstrates how to use Chaseable Tile Notifications on an application’s pinned Live Tile in Windows 10

Step 1

If not already, follow Setup and Start on how to Install and get Started with Visual Studio 2017 or in Windows 10 choose Start, and then from the Start Menu find and select Visual Studio 2017.

vs2017

Step 2

Once Visual Studio Community 2017 has started, from the Menu choose File, then New then Project…

vs2017-file-new-project

Step 3

From New Project choose Visual C# from Installed, Templates then choose Blank App (Universal Windows) and then type in a Name and select a Location and then select Ok to create the Project
vs2017-new-project-window

Step 4

Then in New Universal Windows Project you need to select the Target Version this should be at least the Windows 10, version 1803 (10.0; Build 17134) which is the April 2018 Update and the Minimum Version to be the same.

vs2017-target-platform

The Target Version will control what features your application can use in Windows 10 so by picking the most recent version you’ll be able to take advantage of those features. To make sure you always have the most recent version, in Visual Studio 2017 select Tools Extensions and Updates… then and then see if there are any Updates

Step 5

From the Menu choose Tools, then NuGet Package Manager and Package Manager Console

vs2017-package-manager-console-font-control

Step 6

Then in the Package Manager Console Window which usually appears at the bottom of Visual Studio 2017 at the PM> Prompt type in following:

 
Install-Package Microsoft.Toolkit.Uwp.Notifications

vs2017-package-manager-console-chaseable-tiles

Followed by typing Enter to install Microsoft.Toolkit.Uwp.Notifications from NuGet into the Application which should be Successfully installed

Step 7

Once done select from the Menu, Project, then Add New Item…

vs2017-project-add-new-item

Step 8

From the Add New Item window select Visual C#, then Code from Installed then select Code File from the list, then type in the Name as Library.cs before selecting Add to add the file to the Project

vs2017-add-new-item-library

Step 9

Once in the Code View for Library.cs the following should be entered:

using Microsoft.Toolkit.Uwp.Notifications;
using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Linq;
using System.Threading.Tasks;
using System.Web;
using Windows.ApplicationModel;
using Windows.ApplicationModel.Activation;
using Windows.ApplicationModel.Core;
using Windows.Foundation;
using Windows.UI.Notifications;
using Windows.UI.Popups;
using Windows.UI.StartScreen;
using Windows.UI.Xaml.Controls;

public class ChaseableItem
{
    private const string key_id = "id";
    private const string key_title = "title";
    private const string key_body = "body";

    private string CreateQueryString(Dictionary<string, string> source)
    {
        string[] array = source.Select(kv =>
        $"{HttpUtility.UrlEncode(kv.Key)}={HttpUtility.UrlEncode(kv.Value)}").ToArray();
        return $"?{string.Join("&", array)}";
    }

    private Dictionary<string, string> ParseQueryString(string query)
    {
        NameValueCollection value = HttpUtility.ParseQueryString(query);
        return value.AllKeys.ToDictionary(x => HttpUtility.UrlDecode(x), 
        x => HttpUtility.UrlDecode(value[x]));
    }

    public string Id { get; set; }
    public string Title { get; set; }
    public string Body { get; set; }

    public ChaseableItem() { }

    public ChaseableItem(string value)
    {
        Dictionary<string, string> dict = ParseQueryString(value);
        Id = dict[key_id];
        Title = dict[key_title];
        Body = dict[key_body];
    }

    public string Create()
    {
        Dictionary<string, string> dict = new Dictionary<string, string>()
        {
            { key_id, this.Id },
            { key_title, this.Title },
            { key_body, this.Body }
        };
        return CreateQueryString(dict);
    }

    public override string ToString()
    {
        return $"Id: {Id}, Title: {Title}, Body: {Body}";
    }
}

public class Tile
{
    private readonly Random random = new Random((int)DateTime.Now.Ticks);

    private TileNotification GetNotification(ChaseableItem item)
    {
        TileContent content = new TileContent()
        {
            Visual = new TileVisual()
            {
                Arguments = item.Create(),
                Branding = TileBranding.NameAndLogo,
                TileMedium = new TileBinding()
                {
                    Content = new TileBindingContentAdaptive()
                    {
                        Children =
                        {
                            new AdaptiveText()
                            {
                                Text = item.Title,
                                HintStyle = AdaptiveTextStyle.Body
                            },
                            new AdaptiveText()
                            {
                                Text = item.Body,
                                HintWrap = true,
                                HintStyle = AdaptiveTextStyle.CaptionSubtle
                            }
                        }
                    }
                }
            }
        };
        return new TileNotification(content.GetXml());
    }

    public ChaseableItem Update(string title, string body)
    {
        string id = random.Next(1, 100000000).ToString();
        ChaseableItem item = new ChaseableItem() { Id = id, Title = title, Body = body };
        TileNotification notification = GetNotification(item);
        TileUpdateManager.CreateTileUpdaterForApplication().Update(notification);
        return item;
    }
}

public static class Library
{
    private const string app_title = "Chaseable Tiles";
    private static readonly Tile tile = new Tile();

    private static IAsyncOperation<IUICommand> _dialogCommand;

    private static async Task<bool> ShowDialogAsync(string content, string title = app_title)
    {
        try
        {
            if (_dialogCommand != null)
            {
                _dialogCommand.Cancel();
                _dialogCommand = null;
            }
            _dialogCommand = new MessageDialog(content, title).ShowAsync();
            await _dialogCommand;
            return true;
        }
        catch (TaskCanceledException)
        {
            return false;
        }
    }

    public static async void Launched(LaunchActivatedEventArgs args)
    {
        if(args.TileActivatedInfo != null)
        {
            string argument = args.TileActivatedInfo.RecentlyShownNotifications
            .Select(s => s.Arguments).FirstOrDefault();
            await ShowDialogAsync($"Selected - {new ChaseableItem(argument)}");
        }
    }

    public static async void Add(TextBox title, TextBox desc)
    {
        await ShowDialogAsync($"Added - {tile.Update(title.Text, desc.Text)}");
    }

    public static async Task<bool> PinAsync()
    {
        bool isPinned = false;
        AppListEntry entry = (await Package.Current.GetAppListEntriesAsync()).FirstOrDefault();
        if (entry != null)
        {
            isPinned = await StartScreenManager.GetDefault().ContainsAppListEntryAsync(entry);
        }
        if (!isPinned)
        {
            isPinned = await StartScreenManager.GetDefault().RequestAddAppListEntryAsync(entry);
        }
        return isPinned;
    }
}

In the Code File for Library there are using statements to include the necessary functionality. There is also a ChaseableItem Class with const for Keys, a CreateQueryString to convert a Dictionary of string and string to a Query String, there is a ParseQueryString to convert a Query String back to a Dictionary of string and string. There are also Properties for Id, Colour, Title and Body. The CreateMethod will produce a Query String from a the ChaseableItem using a Dictionary of string and string and the ToString Method produces a string representation of the ChaseableItem Class Object.

The Tile Class has a Random to produce randomised numeric values, a GetNotification Method to create a TileNotification which uses TileContent from the Microsoft.Toolkit.Uwp.Notifications Package and there is an Update Method which will create a ChaseableItem with an Id using Random and then with the TileUpdateManager call Update on the CreateTileUpdaterForApplication to add the TileNotification to the Live Tile for the Application.

The Library Class has const and readonly Values including an Instance of the Tile Class and has an IAsyncOperation of IUICommand for use with the ShowDialogAsync Method which will display a MessageDialog. There is a Launched Method which will check the TileActivatedInfo from the passed in LaunchActivatedEventArgs get the FirstOrDefault from the RecentlyShownNotifications and use ShowDialogAsync with a created ChaseableItem to display its details in a MessageDialog.

Also in the Library Class is an Add Method which will call the Update Method of the Tile Class and then display its details in a MessageDialog with ShowDialogAsync. Finally there is a PinAsync Method which determines if the application is pinned to start with the GetAppListEntriesAsync from the Package.Current with ContainsAppListEntryAsync from the StartScreenManager.GetDefault method and if not uses the RequestAddAppListEntryAsync to Pin the application to Start in Windows 10.

Step 10

In the Solution Explorer select App.xaml

vs2017-app-library

Step 11

From the Menu choose View and then Code

vs2017-view-code

Step 12

Once in the Code View in protected override void OnLaunched(LaunchActivatedEventArgs e) { … } below Frame rootFrame = Window.Current.Content as Frame; the following should be entered:

Library.Launched(e);

In OnLaunched Event Handler the Launched Method of the Library Class will be called, because the static Keyword is used it is not necessary to use new to create an Instance of the Library Class before calling this Method.

Step 13

In the Solution Explorer select MainPage.xaml

vs2017-mainpage-library

Step 14

From the Menu choose View and then Designer

vs2017-view-designer

Step 15

The Design View will be displayed along with the XAML View and in this between the Grid and /Grid elements, enter the following XAML:

<Grid Margin="50">
	<Grid.ColumnDefinitions>
		<ColumnDefinition Width="50*"/>
		<ColumnDefinition Width="50*"/>
	</Grid.ColumnDefinitions>
	<TextBox Name="Title" Grid.Column="0" PlaceholderText="Title" Margin="20" VerticalAlignment="Top"/>
	<TextBox Name="Body" Grid.Column="1" PlaceholderText="Body" Margin="20" VerticalAlignment="Top"/>
</Grid>
<CommandBar Name="Command" VerticalAlignment="Bottom">
	<AppBarButton Icon="Pin" Label="Pin" Click="Pin_Click"/>
	<AppBarButton Icon="Add" Label="Add" Click="Add_Click"/>
</CommandBar>

Within the main Grid Element, the first block of XAML is a Grid Control which has two Columns which are TextBox Controls in a Column each for Title and Body. The second block of XAML is a CommandBar with AppBarButton for Pin which calls Pin_Click and for Add which calls Add_Click and again because the static Keyword is used it is not necessary to use new to create an Instance of the Library Class before calling this Method.

Step 16

From the Menu choose View and then Code

vs2017-view-code

Step 17

Once in the Code View, below the end of public MainPage() { … } the following Code should be entered:

private async void Pin_Click(object sender, RoutedEventArgs e)
{
	await Library.PinAsync();
}

private void Add_Click(object sender, RoutedEventArgs e)
{
	Library.Add(Title, Body);
}

Below the MainPage() there is a Pin_Click Event Handler which calls the PinAsync Method in the Library Class and Add_Click Event Handler which calls the Add Method in the Library Class and again because the static Keyword is used it is not necessary to use new to create an Instance of the Library Class before calling this Method.

Step 18

That completes the Universal Windows Platform Application so Save the Project then in Visual Studio select the Local Machine to run the Application

vs2017-local-machine

Step 19

After the Application has started running you can then select Pin to pin-to-start the Application and then you can then type into the TextBox for Title and Body then select Add to update the Live Tile for the Application in Start of Windows 10

run-chaseable-tiles

Step 20

The Live Tile will be displayed in Windows 10 on the Start Menu for the Application with the Title and Body Entered, you can create multiple Notifications and when selected the correct content will be displayed back in the Application.

ran-chaseable-tiles

Step 21

To Exit the Application select the Close button in the top right of the Application

vs2017-close

Chaseable Tiles shows how easy it is to add many different TileNotification items to the Live Tile of an Application and when selected response to the particular Notification that was displayed within your Application which could be anything from displaying a News Story or anything else where you’ve updated a Live Tile and want your Application to have that information presented when the Live Tile is interacted with when a particular TileNotification is being displayed.

Creative Commons License

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s