How to do WP8 and resolution dependent images correctly

8 בדצמבר 2013

tags: , ,
no comments

BUILD// 2014 Update:

I recommend watching the excellent talk by Peter Torr.

Peter had kindly reviewed this post an I appreciate it, also recommended linking to the new updated docs.

TL;DR: if you need to use images per resolution use the following code, it will save you time.

As of today, the Windows Phone platform supports 4 distinct resolutions:

  • WVGA – the original resolution of WP7
  • WXGA and 720p – introduced with WP8
  • 1080p – introduced with WP8 GDR 3 update (support is still limited), probably as preparations for future devices such as Phablets.

I will not reiterate the technical details of each resolution as it is specified in the WP documentation in a quite clear way:http://msdn.microsoft.com/en-us/library/windowsphone/develop/jj206974(v=vs.105).aspx

In most cases you should stay oblivious to the device resolution your app is running on, cause WP is handling the scaling between different resolutions, which work quite well with your mostly vector based xaml output.

You already might guess that images which are not vector are the first to be effected with auto scaling behavior of WP8. If you dig within the documentation link I've provided, you will learn that WP does not offer any automatic resource loading per resolution like other platforms have (such as iOS and Android). Though bridging over that limitation is quite straightforward and the doc contains sample code you can copy-paste and use in your app.

The problem this post will be addressing is that the documentation does not really gives you the whole truth, in order to use this correctly I found out that I need to follow some consistent approach.

1. You need to specify sizes for each image you use, the sizes should be based upon the scaled resolution property which currently is either 480×800 or 480*853.

2. Keep the image stretch mode uniform.

3. Use a converter or attached property to give you the correct image file based on its resolution.

I am using the following attached property and if you choose to use this as well it handles for you both 1 & 2 constrains:

  public class ImageSourceExtension
    {
        public static string Prefix { get; set; }
        public static string Resolution { get; set; }

        public static readonly DependencyProperty ImageSourceProperty =
            DependencyProperty.RegisterAttached("ImageSource", typeof (string), typeof (ImageSourceExtension), new PropertyMetadata(string.Empty, OnImageSourceChanged));

        private static void OnImageSourceChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            var image = d as Image;
            if (image == null) return;
            image.Stretch = Stretch.Uniform;
            image.Source = new BitmapImage(new Uri(string.Format("{2}/Assets/Icons/{0}{1}.png",e.NewValue, Resolution, Prefix), UriKind.Relative));
            (image.Source as BitmapImage).ImageOpened += (o, p) =>
            {

                image.Width = (image.Source as BitmapImage).PixelWidth / GetScaleFactor(Resolution);
                image.Height = (image.Source as BitmapImage).PixelHeight / GetScaleFactor(Resolution);
            };

        }

        private static double GetScaleFactor(string resulotion)
        {
            if (resulotion == "_WVGA") return 1.0;
            if (resulotion == "_WXGA") return 1.6;
            if (resulotion == "_720p") return 1.5;
            if (resulotion == "_1080p") return 2.25;
            return 1.0;
        }

        public static void SetImageSource(Image element, string value)
        {
            element.SetValue(ImageSourceProperty, value);
        }

        public static string GetImageSource(Image element)
        {
            return (string)element.GetValue(ImageSourceProperty);
        }

    }

To use the ImageSourceExtension property you first need to configure it, place the following code in you App.xaml.cs or similar bootstrapping code. (Note: that its decoupled from the App object, you can place this code in a class library to use in every WP application).

        private string GetResolution()
        {
            if (App.Current.Host.Content.ScaleFactor == 100) return "_WVGA";
            if (App.Current.Host.Content.ScaleFactor == 160) return "_WXGA";
            if (App.Current.Host.Content.ScaleFactor == 150) return "_720p";
            if (App.Current.Host.Content.ScaleFactor == 225) return "_1080p";
            return "_WXGA";
        }
            // Use this line if you use "Resource" build action on your images, if            // use use "Content" then you can remove this following line.
            ImageSourceExtension.Prefix = "/YOURASSEMBLY;component";
            ImageSourceExtension.Resolution= GetResolution();

When you are done with the configuration, all you need to do is use it directly in XAML:

<Image b:ImageSourceExtension.ImageSource="{Binding imageName}" />

This code is solving a problem that when you only use the converter (as described in the doc)  to get the image for each resolution it is still get scaled! so for example you have an image with sizes of 96×96 for WXGA the actual size it will be rendered with will be 144×144 pixels on WXGA (assuming stretch mode is None) and that is not what we want.

Hope that helps.

Add comment
facebook linkedin twitter email

Leave a Reply

Your email address will not be published.

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

*