Condividi tramite


WPF and Binary Resources

Recently, I had to access binary resources from a WPF control and although it sounds like a straight forward thing to do, I found out that there were a couple of twists to it.

From the markup

If you need to display an icon on your UI you would write something like that from the markup:
<Image Name="MyImage" Source="Resources/Icon1.ico" />

Beware, that by default, WPF resolves resources at run time, so you can refer to the icon file, provided you move it around with you app while preserving the relative directory structure. What you probably want to do, is to embed your resources in your assembly.
The markup above works fine, if you add the icon file to the project and marked it as resource. So far so good.

If the resource is part of the project, you can also set the image source from the property sheet.

clip_image002

Then notice something interesting, the image source end up looking something like that:
<Image Name="MyImage" Source="/WpfApplication1;component/Resources/Icon1.ico"/>

The weird path is actually a so called packed URI. This URI scheme provides a way to refer resources embedded in files. For more details on the pack URIs see Pack URIs in Windows Presentation Foundation in the MSDN documentation.

Icon files can contain several bitmaps, and by referencing the resource in this way, you cannot control which one is used. You might for example want to pick the 16x16 icon rather that the 32x32 for a better rendering.

This can be done in the code behind:

From the code behind

First you need to create the URI. The simplest syntax, if the resource is embedded in the running assembly is
Uri resourceUri = new Uri(@"Resources/Icon1.ico", UriKind.Relative);

Notice that you need to specified that the URI is relative. The syntax for the absolute path is
Uri resourceUri = new Uri(@"/WpfApplication1;component/Resources/Icon1.ico");

But you'll notice that the name of the assembly is included in the URI. A more portable version is
Uri resourceUri = new Uri(@"pack://application:,,/Resources/icon1.ico");

Anyway, once you have the Uri, the way to access the resource is to get hold of the corresponding stream:
StreamResourceInfo resourceInfo = Application.GetResourceStream(resourceUri);
Stream resourceStream = resourceInfo.Stream;

To iterate through the bitmaps in this stream, we can use the IconBitmapDecoder helper class and look for the one which is 16x16:
// Look for the 16x16 icon
if (iconBitmapDecoder.Frames.Count > 1)
{
    foreach (BitmapFrame bitmapFrame in iconBitmapDecoder.Frames)
    {
        if (bitmapFrame.Height == 16 && bitmapFrame.Width == 16)
        {
            MyImage.Source = bitmapFrame;
            break;
        }
    }
}

That's it! I buy you beer if you can do the same thing directly from the markup.