Udostępnij za pośrednictwem


Binding an embedded image using a value converter

Recently I needed to bind some embedded images to data templates. If you look at the Image type in Silverlight 2, you will see that it exposes a Source property of type ImageSource that can be set to the URI of an image either relative to the XAP, or in its absolute form. If the URI is valid, the image stream is opened and read at runtime, an underlying BitmapImage is created around the stream and is then fed into Image.Source (BitmapImage derives from ImageSource).

However this is all great when the images are on a web site. But what about images that are packaged with my XAP ? It is actually not very hard to bind those either. Assuming you have a XAP assembly named Foo (with Foo being the default namespace), and say your image, Bar.png is stored in a project folder named Images, once you compile the assembly, the image is embedded into the assembly as a resource named Foo.Images.Bar.png. To get this done through Visual Studio mark your image  as an Embedded Resource. However to access the embedded image and create a BitmapImage out of it, you will have to write some code like this:

BitmapImage bim = new BitmapImage();
bim.SetSource(this.GetType().Assembly.GetManifestResourceStream("Foo.Images.Bar.png"));

This BitmapImage instance can then be bound directly to the Image.Source property using a traditional binding expression.

However, you may already know all of this, and in any case I wanted to make this a little more general purpose. I did not quite like the fact that the string literal representing the image (in the above case “Foo.Images.Bar.png” ) could not be directly fed to the Image element in my XAML like a URI. I had to create some sort of artificial property in some type that would instead expose a BitmapImage instance, and then bind that property to my Image.Source. In effect what I wanted to do was something like this:

<Image Source="Foo.Images.Bar.png" />

but sadly that would not work. So instead I took the approach of using a value converter. I wrote a converter that looks like this:

public class ImageResourceNameToBitmapImageConverter : IValueConverter
  {
    public object Convert(object value, Type targetType,
      object parameter, System.Globalization.CultureInfo culture)
    {
      //check to see that the parameter types are conformant
      if (!(value is string) || targetType != typeof(ImageSource) )
        return null;
      BitmapImage MenuItemImage = new BitmapImage();
      try
      {
        MenuItemImage.SetSource(this.GetType().Assembly.
          GetManifestResourceStream(value as string));
      }
      catch (Exception Ex)
      {
        return null;
      }
      return MenuItemImage;
    }
    public object ConvertBack(object value, Type targetType,
      object parameter, System.Globalization.CultureInfo culture)
    {
      throw new NotImplementedException();

    }
  }

 

Now in my XAML I can declare the converter:

 

<local:ImageResourceNameToBitmapImageConverter x:Key="REF_ImageResourceNameToBitmapImageConverter" />

and then an Image element like this:

<Image Source="{Binding Converter={StaticResource REF_ImageResourceNameToBitmapImageConverter}}" DataContext="Foo.Images.Bar.png" />

 

This does the trick. Since the Binding does not specify a path, the converter gets the DataContext (which is the qualified image resource string name) passed in as the value parameter, and all I do in the converter is use the same code as before to create and pass out the BitmapImage instance.

 

Came in pretty handy and saved me the grief of creating unnecessary CLR properties for each necessary image binding.

Comments

  • Anonymous
    March 13, 2009
    The comment has been removed