69

I'm trying to bind a list of custom objects to a WPF Image like this:

<Image>
    <Image.Source>
        <BitmapImage UriSource="{Binding Path=ImagePath}" />
    </Image.Source>
</Image>

But it doesn't work. This is the error I'm getting:

"Property 'UriSource' or property 'StreamSource' must be set."

What am I missing?

H.B.
  • 166,899
  • 29
  • 327
  • 400
urini
  • 32,483
  • 14
  • 40
  • 37
  • 1
    Unfortunately, the actual reason this fails is not explained in the answers. See [this](https://www.wiredprairie.us/journal/2007/03/bitmapimageurisource_binding_d.html) (*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,*) – mins Aug 25 '20 at 17:19

6 Answers6

85

WPF has built-in converters for certain types. If you bind the Image's Source property to a string or Uri value, under the hood WPF will use an ImageSourceConverter to convert the value to an ImageSource.

So

<Image Source="{Binding ImageSource}"/>

would work if the ImageSource property was a string representation of a valid URI to an image.

You can of course roll your own Binding converter:

public class ImageConverter : IValueConverter
{
    public object Convert(
        object value, Type targetType, object parameter, CultureInfo culture)
    {
        return new BitmapImage(new Uri(value.ToString()));
    }

    public object ConvertBack(
        object value, Type targetType, object parameter, CultureInfo culture)
    {
        throw new NotSupportedException();
    }
}

and use it like this:

<Image Source="{Binding ImageSource, Converter={StaticResource ImageConverter}}"/>
Clemens
  • 123,504
  • 12
  • 155
  • 268
Brian Leahy
  • 34,677
  • 12
  • 45
  • 60
  • 4
    (Actually the type converter does not create a `BitmapImage`, but another subclass of `ImageSource`: `BitmapFrameDecode`, which is internal.) – H.B. Sep 27 '11 at 04:44
  • @H.B. How would you convert back if you wanted to persist the change of the image? – Igor Jun 16 '14 at 09:09
  • Alternative to a converter: Make your property you bind to (here: `ImageSource`) of type `Uri` or `BitmapImage` and cast there. If you need to deal with possible `null` values (cast failed etc.) add `TargetNullValue={x:Null}` to your binding. – Gerrit Oct 28 '15 at 12:10
  • Worked perfectly! ty – Mohammed A. Fadil Oct 21 '19 at 06:59
  • 1
    How does that explain the error message "*Property 'UriSource' or property 'StreamSource' must be set*"? (what was wrong in the OP code which sets the path using a Uri?) – mins Aug 25 '20 at 17:15
21

This article by Atul Gupta has sample code that covers several scenarios:

  1. Regular resource image binding to Source property in XAML
  2. Binding resource image, but from code behind
  3. Binding resource image in code behind by using Application.GetResourceStream
  4. Loading image from file path via memory stream (same is applicable when loading blog image data from database)
  5. Loading image from file path, but by using binding to a file path Property
  6. Binding image data to a user control which internally has image control via dependency property
  7. Same as point 5, but also ensuring that the file doesn't get's locked on hard-disk
Drew Noakes
  • 300,895
  • 165
  • 679
  • 742
18

You can also simply set the Source attribute rather than using the child elements. To do this your class needs to return the image as a Bitmap Image. Here is an example of one way I've done it

<Image Width="90" Height="90" 
       Source="{Binding Path=ImageSource}"
       Margin="0,0,0,5" />

And the class property is simply this

public object ImageSource {
    get {
        BitmapImage image = new BitmapImage();

        try {
            image.BeginInit();
            image.CacheOption = BitmapCacheOption.OnLoad;
            image.CreateOptions = BitmapCreateOptions.IgnoreImageCache;
            image.UriSource = new Uri( FullPath, UriKind.Absolute );
            image.EndInit();
        }
        catch{
            return DependencyProperty.UnsetValue;
        }

        return image;
    }
}

I suppose it may be a little more work than the value converter, but it is another option.

palehorse
  • 26,407
  • 4
  • 40
  • 48
  • 1
    A similar implementation to this is perfect for when your bitmap resource is already loaded into your object and you want to pass the bits to the binding. THANKS! – Scott Fletcher Oct 17 '08 at 18:17
8

The problem with the answer that was chosen here is that when navigating back and forth, the converter will get triggered every time the page is shown.

This causes new file handles to be created continuously and will block any attempt to delete the file because it is still in use. This can be verified by using Process Explorer.

If the image file might be deleted at some point, a converter such as this might be used: using XAML to bind to a System.Drawing.Image into a System.Windows.Image control

The disadvantage with this memory stream method is that the image(s) get loaded and decoded every time and no caching can take place: "To prevent images from being decoded more than once, assign the Image.Source property from an Uri rather than using memory streams" Source: "Performance tips for Windows Store apps using XAML"

To solve the performance issue, the repository pattern can be used to provide a caching layer. The caching could take place in memory, which may cause memory issues, or as thumbnail files that reside in a temp folder that can be cleared when the app exits.

Community
  • 1
  • 1
Luis Cantero
  • 1,278
  • 13
  • 11
8

You need to have an implementation of IValueConverter interface that converts the uri into an image. Your Convert implementation of IValueConverter will look something like this:

BitmapImage image = new BitmapImage();
image.BeginInit();
image.UriSource = new Uri(value as string);
image.EndInit();

return image;

Then you will need to use the converter in your binding:

<Image>
    <Image.Source>
        <BitmapImage UriSource="{Binding Path=ImagePath, Converter=...}" />
    </Image.Source>
</Image>
Dale Ragan
  • 18,202
  • 3
  • 54
  • 70
  • It looks like the converter returns a `BitmapImage`, and the result should be assigned to `Image.Source` directly, not to another `BitmapImage UriSource`. – mins Aug 25 '20 at 17:49
6

you may use

ImageSourceConverter class

to get what you want

    img1.Source = (ImageSource)new ImageSourceConverter().ConvertFromString("/Assets/check.png");