WP7: Generate Live Tile Images Locally (Without The Visual Tree)

11 September 2011

When updating your live tiles locally, if you've tried creating .jpgs programmatically using the WritableBitmap(UIElement) constructor you may have come across issues using complex UIElements like StackPanels or multiple elements. The main issue I had was one where the elements would just overlap each other and not actually stack or render in a manner they would if they were in the Visual Tree.

Well, it turns out that there's a way to fix this without using Canvas.SetTop() all over the place, or temporarily adding the UIElement to the visual tree - you simply need to ensure that all your elements have a specific width and height set and call a few methods, namely the Measure(), Arrange() and UpdateLayout() methods. A quick example of a Live Tile image being generated is below:

        StackPanel sp = new StackPanel();
        sp.Height = 173;
        sp.Width = 173;
        sp.Background = (SolidColorBrush)Application.Current.Resources["PhoneBackgroundBrush"];

        TextBlock heading = new TextBlock();
        heading.Style = (Style)Application.Current.Resources["PhoneTextTitle3Style"];
        heading.Foreground = new SolidColorBrush(Colors.White);
        heading.Margin = new Thickness { Left = 10, Bottom = 0, Right = 0, Top = 0 };
        heading.Text = "My Heading";
        heading.Height = 35;
        heading.Width = 173;

        Border headingBack = new Border();
        headingBack.Background = (SolidColorBrush)Application.Current.Resources["PhoneAccentBrush"];
        headingBack.Width = 173;
        headingBack.Height = 35;
        headingBack.Child = heading;
        headingBack.Measure(new Size(173, 35));
        headingBack.Arrange(new Rect(0, 0, 173, 35));
        headingBack.UpdateLayout();

        sp.Children.Add(headingBack);                       

        TextBlock tbAction = new TextBlock();
        tbAction.Text = "My Item";
        tbAction.Style = (Style)Application.Current.Resources["PhoneTextNormalStyle"];
        tbAction.Margin = new Thickness(4,0,0,0);
        tbAction.Height = 27;
        tbAction.Width = 173;                    
        sp.Children.Add(tbAction);

        //call measure, arrange and updatelayout to prepare for rendering
        sp.Measure(new Size(173, 173));
        sp.Arrange(new Rect(0, 0, 173, 173));
        sp.UpdateLayout();

        WriteableBitmap wbm = new WriteableBitmap(173, 173);            
        wbm.Render(sp, null);
        wbm.Invalidate();

        //write image to isolated storage - note that the \Shared\ShellContent folder is required
        string sIsoStorePath = @"\Shared\ShellContent\tile-bg.png"
        using (IsolatedStorageFile appStorage = IsolatedStorageFile.GetUserStoreForApplication())
        {
            //ensure directory exists
            String sDirectory = System.IO.Path.GetDirectoryName(sIsoStorePath);
            if (!appStorage.DirectoryExists(sDirectory))
            {
                appStorage.CreateDirectory(sDirectory);
            }

            using (IsolatedStorageFileStream stream = new IsolatedStorageFileStream(sIsoStorePath, System.IO.FileMode.Create, appStorage))
            {
                wbm.SaveJpeg(stream, 173, 173, 0, 100);
            }
        }

To be quite honest I'm not exactly sure which calls are essential and which aren't, since I worked in a sort of worked bottom up way - word on the street is that Arrange() is essential and the other two might not be, but if anyone has any concrete answers on which methods need to be called in which situations on which UIElements, sound out!

Tags: images, live tiles, wp7

Add a Comment

1 Comment