Binding of UriSource to BitmapImage not working

3.3k views Asked by At

My code:

<Image Height="100" Width="100" HorizontalAlignment="Left" VerticalAlignment="Top">
    <Image.Source>
        <BitmapImage DecodePixelWidth="100">
            <BitmapImage.UriSource>
                <PriorityBinding>
                    <Binding Path="MyModel1.ImagePath"/>
                    <Binding Path="MyModel2.ImagePath"/>
                </PriorityBinding>
            </BitmapImage.UriSource>
        </BitmapImage>
    </Image.Source>
</Image>

In my viewmodel, the value of ImagePath:

public object ImagePath
{
    get { return new Uri("F:/myFolder/default.png", UriKind.Absolute); }
}

The path F:/myFolder/default.png exists. I get the error: Property 'UriSource' or property 'StreamSource' must be set. Why is this happening? Where am I making a mistake?

2

There are 2 answers

3
Sheridan On

Just use the plain string file paths and let the Framework convert them to BitmapImage elements:

public string ImagePath
{
    get { return new "F:/myFolder/default.png"; }
}

...

<Image Height="100" Width="100" HorizontalAlignment="Left" VerticalAlignment="Top">
    <Image.Source>
        <PriorityBinding>
            <Binding Path="MyModel1.ImagePath"/>
            <Binding Path="MyModel2.ImagePath"/>
        </PriorityBinding>
    </Image.Source>
</Image>
0
Mike Strobel On

The problem is that you cannot initialize a BitmapImage if you don't have a UriSource or StreamSource immediately available. You don't have a source immediately available because you are providing it via a Binding, and bindings are not available until the data context has been set and the binding has been processed, which does not happen immediately.

You will need to defer creation of the BitmapImage until the sources are available. You can do this with a custom converter:

public class ImageConverter : IValueConverter
{
    public ImageConverter()
    {
        this.DecodeHeight = -1;
        this.DecodeWidth = -1;
    }

    public int DecodeWidth { get; set; }
    public int DecodeHeight { get; set; }

    public object Convert(
        object value,
        Type targetType,
        object parameter,
        CultureInfo culture)
    {
        var uri = value as Uri;
        if (uri != null)
        {
            var source = new BitmapImage();
            source.BeginInit();
            source.UriSource = uri;
            if (this.DecodeWidth >= 0)
                source.DecodePixelWidth = this.DecodeWidth;
            if (this.DecodeHeight >= 0)
                source.DecodePixelHeight = this.DecodeHeight;
            source.EndInit();
            return source;
        }
        return DependencyProperty.UnsetValue;
    }

    public object ConvertBack(
        object value,
        Type targetType,
        object parameter,
        CultureInfo culture)
    {
        return Binding.DoNothing;
    }
}
<Image Height="100"Width="100" HorizontalAlignment="Left" VerticalAlignment="Top">
  <Image.Source>
    <PriorityBinding>
      <Binding Path="Model1.ImagePath" Converter="{StaticResource ImageConverter}" />
      <Binding Path="Model2.ImagePath" Converter="{StaticResource ImageConverter}" />
    </PriorityBinding>
  </Image.Source>
</Image>

...and drop this in your resources:

<l:ImageConverter x:Key="ImageConverter" DecodeWidth="100" />