« Rotated text in a TabControl | Main | Loading BitmapSources in the Background ... »

BitmapImage.UriSource binding doesn't work ...

For whatever reason, WPF/XAML doesn't support direct binding of a Uri object to the UriSource of a BitmapImage object. When run, the application fails with an error, "Property 'UriSource' or property 'StreamSource' must be set."  (Here's a complaint about this on MSDN Forums).

This makes easy displays of image items with a source or Uri property more challenging than it should be. I've found one work-around for now (it's not as snappy as I'd like though if the images are large). As with any binding of images, binding to large images may have a negative impact on application performance as Bitmap's must be created on the UI thread.

Here's how to use my new UriToImageConverter value converter:

First, create, in the appropriate Resources, an instance of the new ValueConverter. 

<local:UriToImageConverter x:Key="LocalUriToImageConverter"/>

Of course, you'll need to declare the new xml namespace (usually in the root element in XAML, such as the Window or Page):

xmlns:local="clr-namespace:MyApplicationNamespace"

Then, in the DataTemplate (which is likely where you'd use this), you'll need to use the converter, not as a converter to a Uri, but to the ImageSource itself:

<Image x:Name="imgPhoto" Width="80" Height="60" Grid.Row="0"
Source="{Binding Path=FilenameUri,
Converter={StaticResource LocalUriToImageConverter}}"
>

Finally, add the C# code below to your project.

The Path in this instance can be either a string or a Uri. The trick is that this converter returns a new ImageSource:

 
   1:      public class UriToImageConverter : IValueConverter
   2:      {
   3:   
   4:          public object Convert(object value, Type targetType, object parameter,
System.Globalization.CultureInfo culture)
   5:          {
   6:              if (value == null)
   7:              {
   8:                  return null;
   9:              }
  10:   
  11:              if (value is string)
  12:              {
  13:                  value = new Uri((string)value);
  14:              }
  15:   
  16:              if (value is Uri)
  17:              {                
  18:                  BitmapImage bi = new BitmapImage();
  19:                  bi.BeginInit();
  20:                  bi.DecodePixelWidth = 80;
  21:                  //bi.DecodePixelHeight = 60;                
  22:                  bi.UriSource = (Uri)value;
  23:                  bi.EndInit();
  24:                  return bi;
  25:              }
  26:   
  27:              return null;
  28:          }
  29:   
  30:          public object ConvertBack(object value, Type targetType, object parameter, 
System.Globalization.CultureInfo culture)
  31:          {
  32:              throw new Exception("The method or operation is not implemented.");
  33:          }
  34:   
  35:      }

Note that once the value is converted to a Uri (if necessary), a new BitmapImage object is created [line 18]. Then, within the appropriate Begin/End Init block [lines 19-23], the UriSource (and an example DecodePixelWidth is set). The new ImageSource (BitmapImage) is returned to the converter. So, the Uri is converted directly to the BitmapImage, with WPF being none-the-wiser!

 

Comments

Worked like a charm. Dropped my app from taking up 300+ mb to around 34 of memory loading around 2000 300k files. Many, many thanks.

Help support my web site by searching and buying through Amazon.com (in assocation with Amazon.com).