Live Windows Phone Tiles

  • Tutorial

For the impatient - we will focus on the dynamic generation of background images for live tiles, the creation of transparent tiles for 8.1 update and localization of application names.


More informative live tile


Developing the next application, it became necessary to display a fairly large amount of data directly on a live tile.
Although there are already three standard templates , they were not enough to implement some ideas. Having asked Google about the possibilities of flexible formatting of text on tiles, it turned out that at the moment this is not possible.
But, as a result of finding a solution, quite often the original approach of other developers was encountered - if it is impossible to display formatted text on a tile, then this text can be shown as a picture. With this approach, it is possible to display on the tile anything, as you like, with any complexity of the layout.

First of all, you need to know what image sizes we need. Actually, pixel sizeslisted on the site for developers. Such sizes of background images will need to be generated on the fly when updating data.

The process of generating a background image itself is not very complicated.
Code example
using System;
using System.IO;
using System.IO.IsolatedStorage;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Media.Imaging;
namespaceExample.Common
{
    publicstaticclassTileImage
    {
        publicstatic Uri Render(
            String title,
            string row1,
            bool row1Bold,
            bool row1Italic,
            string row2,
            bool row2Bold,
            bool row2Italic)
        {
            var bitmap = new WriteableBitmap(Constants.TileWidth, Constants.TileHeight);
            var canvas = new Grid
            {
                Width = bitmap.PixelWidth,
                Height = bitmap.PixelHeight
            };
            var background = new Canvas
            {
                Height = bitmap.PixelHeight,
                Width = bitmap.PixelWidth,
                Background = new SolidColorBrush(Constants.TileBackgroundColor)
            };
            #region titlevar titleBlock = new TextBlock
            {
                Text = title,
                FontWeight = FontWeights.Bold,
                TextAlignment = TextAlignment.Left,
                VerticalAlignment = VerticalAlignment.Stretch,
                Margin = new Thickness(Constants.TilePadding),
                TextWrapping = TextWrapping.NoWrap,
                Foreground = new SolidColorBrush(Constants.TileForegroundColor),
                FontSize = Constants.TileTitleFontSize,
                Width = bitmap.PixelWidth - Constants.TilePadding * 2
            };
            #endregion#region first rowvar firstRowBlock = new TextBlock
            {
                Text = row1,
                TextAlignment = TextAlignment.Left,
                VerticalAlignment = VerticalAlignment.Stretch,
                Margin = new Thickness(Constants.TilePadding, Constants.TilePadding * 2 + Constants.TileTitleFontSize, Constants.TilePadding, Constants.TilePadding),
                TextWrapping = TextWrapping.NoWrap,
                Foreground = new SolidColorBrush(Constants.TileForegroundColor),
                FontSize = Constants.TileTextFontSize,
                Width = bitmap.PixelWidth - Constants.TilePadding * 2
            };
            if (row1Bold)
            {
                firstRowBlock.FontWeight = FontWeights.Bold;
            }
            if (row1Italic)
            {
                firstRowBlock.FontStyle = FontStyles.Italic;
            }
            #endregion#region second rowvar secondRowBlock = new TextBlock
            {
                Text = row2,
                TextAlignment = TextAlignment.Left,
                VerticalAlignment = VerticalAlignment.Stretch,
                Margin = new Thickness(Constants.TilePadding, Constants.TilePadding * 3 + Constants.TileTitleFontSize + Constants.TileTextFontSize, Constants.TilePadding, Constants.TilePadding),
                TextWrapping = TextWrapping.Wrap,
                Foreground = new SolidColorBrush(Constants.TileForegroundColor),
                FontSize = Constants.TileTextFontSize,
                Width = bitmap.PixelWidth - Constants.TilePadding * 2
            };
            if (row2Bold)
            {
                secondRowBlock.FontWeight = FontWeights.Bold;
            }
            if (row2Italic)
            {
                secondRowBlock.FontStyle = FontStyles.Italic;
            }
            #endregion
            canvas.Children.Add(titleBlock);
            canvas.Children.Add(firstRowBlock);
            canvas.Children.Add(secondRowBlock);
            bitmap.Render(background, null);
            bitmap.Render(canvas, null);
            bitmap.Invalidate();
            using (var imageStream = new IsolatedStorageFileStream(Constants.TilePath, FileMode.Create, Constants.AppStorage))
            {
                bitmap.SaveJpeg(imageStream,bitmap.PixelWidth,bitmap.PixelHeight,0,90);
            }
            returnnew Uri("isostore:" + Constants.TilePath, UriKind.Absolute);
        }
    }
}


Simply put, a canvas is created, on which arbitrary elements are pounced and saved as a JPG file in local storage.
Now, it’s easy to use a file from local storage as a background for a tile.

As you can see from the example, a small tile looks unchanged, but a medium-sized tile already uses the generated image:


You can also generate images for other tile sizes. For example, on a small one you can show the basic information in abbreviated form, on the middle a little more space and you can add more data, but on a wide one you can certainly not save, but still you need to arrange everything as conveniently as possible.
An example of this approach can be seen in the image in the title of the article.

Now transparent!


As many people know, soon our Windows Phone will be updated to the new version 8.1. We need to prepare for this.
One of the innovations of the update is the ability to add a background image to the start screen. In order for our tile to comply with new trends and not obscure the background of the desktop, you need to make the background image of the tile transparent.

A small unpleasant surprise awaits us here - the image can only be saved in JPG format. Colleagues from Microsoft for some reason decided that this would be enough. Well, you have to reinvent the wheel.

In my work, I am always sure that someone faced such problems before me and, very often, I am right. This case is no exception. On the vast Internet, and more specifically on ToolStack, there was a ready-made libraryPNGWriter , which allows you to generate PNG.

Using the library is extremely simple, it just adds the WritePNG method to the WriteableBitmap class:

var bitmap = new WriteableBitmap(Constants.TileWidth, Constants.TileHeight);
bitmap.WritePNG(imageStream);

The principle of application remains the same - we save the image in local storage and use it as a background for live tiles. For WP8.0, the tile will have the theme color, but in the new WP8.1, the background of the start screen will be visible through the tile.

The result will be very interesting. The background will be viewed through the tile, the parallax effect will also appear when scrolling through the start screen:


Application Name Localization


For applications that do not have a specific brand or an easily recognizable name, localization of the name may be required. It is logical that “Skype” or “WhatsApp” does not need to be translated, but functional names, for example, “flashlight”, “camera” should be localized.

The task of localizing the name of the application may not seem trivial for the average developer. But the instruction on how to do this is written in great detail and, in fact, everything is much simpler.

I do not want to repeat the entire text of the instructions from the Microsoft website . I want to dwell only on small changes that seem to me to simplify the localization process.

Microsoft recommends creating one project to generate all localized DLLs. It seemed to me a little dreary to constantly rename, copy the created DLL file. Therefore, I created several projects for each localization that my application will support. And in each project, I set up a deployment immediately to the root of the Windows Phone project.
For this, the path to the folder of the assembled DLL file was slightly changed.
For the default locale:


For all other locales (the number in the Target Name corresponds to the locale number):


Now you can safely change the values ​​in the resource tables. Each time you build a Windows Phone project, DLL files with localization will be collected first and replace obsolete ones in the root of the main project. Thus, we avoid manipulating the manual transfer of DLL files.

The text of the application name in the phone list will depend on the locale specified in the phone. In my example, it will look like this:




It is also worth noting that when publishing an application in the Marketplace, it became possible to specify alternative names for the application. It would be logical to add all localized names there.

Conclusion


Using such simple approaches, the application will become more convenient and interesting.
Let's hope that Microsoft will continue to develop the development environment and features of the Windows Phone API, and in new versions we will not have to invent such bicycles.

Have a nice development.

You can download the finished project and get acquainted with the code on GitHub:
https://github.com/vbilenko/WPExample01

List of links:

Also popular now: