RSS
 

Creating tiled backgrounds in Metro style XAML apps

20 Jul
TL;DR version: This class will let you tile an image in a Metro-style XAML app. Use an image that's at least 128x128 for best memory efficiency Textures can make your application beautiful. Here's an example from http://subtlepatterns.com, which is full of classy textures: [caption id="attachment_285" align="aligncenter" width="600"] A nice tile texture[/caption] These textures can make great backgrounds, but because the texture is usually smaller than your application (especially tiny textures like  ), you need to repeat (tile) the image vertically and horizontally. Images that "match up" on opposite sides are ideal. All the images on  http://subtlepatterns.comhttp://bgrepeat.com and http://www.repeatxy.com are tileable. So how do you program your application to repeat this image across a surface? In HTML, you can use the css style "background-repeat: repeat" to tile an image across your webpage. In WPF, you can set up the TileBrush properties on an ImageBrush to tile an image across your application. But in Silverlight, Silverlight for Windows Phone, or Metro style XAML applications, you're out of luck.

Fake it

The easiest solution is to use Photoshop (or cheaper equivalent) to create a huge image that you can set as a background. Just use the pattern tool to tile your texture onto an image that's bigger than your application's surface will ever be. The question is: can you guarantee a maximum size? With Metro style XAML applications,  there is no maximum resolution. The other problem is that these massive image files tend to use up a LOT of memory. In my case, it was so much memory that I was likely to fail Microsoft's Win8 certification.

Make it

There was no way I was going to convert my application into a JavaScript & HTML app, so I set out to create a control that would look just like a WPF tiled brush. The easiest way to do this is to manually arrange a set of images in a control. I'm very familiar with WPF, and I just knew there'd be some weird inconsistencies in the new WinRT APIs that made this harder than it should be, so I decided to spike this in WPF first. I inherited from Canvas, gave it an ImageSource property, and slapped my code into the OnRenderSizeChanged method. It worked perfectly, here's the code: [gist id=3077863 file=TileCanvas.cs]

I broke a convention :(

If I was a WPF Purist I would have never extended a Panel class in order to provide a visual control. I'd have built a templated control (Inheriting from Control), and declared the canvas as a template part, and provided a default template that just contained that canvas in my Generic.xaml file. But my whole aim was to keep memory usage low, and in WPF every visual costs you memory, so I decided to pre-optimise. It's also simpler this was as I only have to show you one file :).

Optimising Memory

A tilebrush in WPF on a 1000x800 surface will happily repeat a 4 pixel image for you, thousands of times (200,000 to be exact), without breaking a sweat. However, with my TileCanvas, we're creating an Image visual every time we want to repeat the tile, so you don't want an ImageSource that's too small. You have to find a happy balance between a huge image (which uses up a lot of memory by itself) and a lot of small images (where you don't keep paying for the image data, but you do pay for the visual tree explosion). I have found that by repeating your pattern, Photoshop style, onto an image that's a sized with a nice round number like 128x128 pixels is a good compromise. This applies to the WPF spike as well as the following Metro solution.

Make it - Metro Style

If you want to build a Metro Style application, you need to program against the WinRT API. This provides .NET developers with new challenges. Microsoft say that WinRT allows .NET developers to use their existing skills, which is fair. What they don't mention is that you're going to need a lot of new knowedge. WinRT is like a parallel universe - a lot of the things you expect to find are there, but they are a bit different, or they're in a different place. A lot of operations are now done asyncronously, which should be make apps snappier. Truly a "parallel" universe (sorry). When I tried to port my WPF TileCanvas over to use the WinRT version of XAML controls, I ran into two differences that changed the way I had to do things:
  • There's no longer a RenderSizeChanged method - after a bit of experimenting, I decided on the LayoutUpdated event (although adding images causes this event to be raised, so I had to track the old size in order to avoid stack overflow)
  • ImageSources now start with a width and height of 0. It's not until the ImageOpened event is raised that you can find out the width and height of an image. And you don't get an ImageOpened event until your image is loaded into the Visual Tree. To get around this chicken-and-egg situation, I add a single image to the canvas, and then once it's opened and I have the width & height I do a proper layout of the whole screen. I also attach to the ImageFailed event to help you debug, and clean up my event subscriptions once they've fired.
Here is my working Metro-style TileCanvas: [gist id=3006848 file=TileCanvas.cs]

Almost perfect?

One thing that I would add in future would be to not throw away the entire canvas of images every time it was built. It would be better to determine just how many images need to be added and removed. At the moment, this isn't a big issue because metro apps are always fullscreen so they don't change size unless you snap/rotate them.

Shameless plug

I needed this class because I wanted a background texture in my new Windows 8 game, Chromazone. It's just been accepted my Microsoft to the store, check it out: http://apps.microsoft.com/webpdp/en-US/app/chromazone/fe3aea27-d44f-43e0-8662-1c45178e4dec
 
 

Tags: , , , ,

Leave a Reply

 

 
  1. Dew Drop – July 20, 2012 (#1,368) | Alvin Ashcraft's Morning Dew

    20 July 2012 at 1:28 pm

    [...] Creating tiled backgrounds in Metro style XAML apps (Rob Fonseca-Ensor) [...]

     
  2. Windows 8 Developer Links – 2012-07-23Dan Rigby | Dan Rigby

    23 July 2012 at 3:32 am

    [...] it’s fortunate that there’s now a bit less tiresome way of doing this…”Creating tiled backgrounds in Metro style XAML apps (Rob Fonseca-Ensor)“Textures can make your application beautiful. … So how do you [...]

     
    • Dim

      12 October 2012 at 11:41 am

      Awesome idea, thank you Rob!

      Weird that Microsoft doesn’t provide a built-in option for tiling backgrounds – nothing new to them

      One thing a XAML Noob like myself has to remember – place this Canvas into a Grid Element along with the rest of your content.

      My mistake was to put stuff INTO the Canvas, which of course didn’t work due to the Children.Clear() call.

      Thanks again and keep up the great work!

      Cheers,
      Dim

       
  3. Simon (Darkside) Jackson

    31 July 2012 at 10:41 am

    Fantastic article Rob, will certainly come in handy for my WinRT adventures.
    In theory could also use this technique for generating procedural backgrounds.

    Great job

     
  4. WinRT–Set an Image Source to an Asset via XAML / Set a tiled background : Blake's Application Development Blog

    4 September 2012 at 5:59 pm

    [...] had found a great blog post at http://www.robfe.com/2012/07/creating-tiled-backgrounds-in-metro-style-xaml-apps/#comments that gave an example of how to tile an image over the background in a WinRT application.  [...]

     
  5. Tim

    25 September 2012 at 6:50 pm

    Could you provide a line or two of how you reference/use this class from XAML? Thanks.

     
    • Evgeny

      16 November 2012 at 9:50 pm

      Yes, please tell us how use it on a specific example

      Thanks!

       
  6. Farhan

    5 November 2012 at 7:14 am

    How can I give the tiled backgorund to Grid’s backgound ? Grid’s background can be set by ImageBrush, please help me.

     
  7. Creating a smooth extended splash screen experience

    5 June 2013 at 8:38 am

    [...] solution that you can download at the end of this post. The one extra thing I’m using is Rob Fonseca-Ensor’s TileCanvas class to create a tiled background on the extended splash screen UserControl, to give a more interesting [...]

     
  8. Nathan

    4 July 2013 at 9:20 am

    I made some changes to make it more efficient by only removing and adding images as needed.

    https://gist.github.com/ngbrown/5925335/revisions

     
  9. Jacob Abrams

    23 September 2013 at 7:31 am

    This doesn’t seem to work when the scale resolution of the display is not 100. Just use the simulator and change the display and you’ll see it looks pretty broken. I’ve been trying to figure out how to fix it but haven’t had any luck. Has anyone else?

     
    • Arthur Dumas

      5 May 2014 at 5:39 am

      I know this is an old post but this technique is still quite handy (until WinRT gets tile support like WPF).

      There is a small problem that will show itself when dragging a metro app from a low res monitor to a large res monitor (like from a laptop to an external display). This might also be the problem Jacob had with the emulator when changing scaling (or resolution).

      In the Rebuild() method, the bitmap’s PixelWidth/PixelHeight are only valid on the first call.

      Simple solution is to add 2 new member variables of type nullable int to store the width/height. Assign them in the ImageOnImageFailed( / ImageOnImageOpened() methods as appropriate and test them in Rebuild().