0

I have a control that contains an image that is supposed to be pulled from a file location (either web or local). I have the following XAML code:

        <Image Width="96" Height="96" Stretch="Uniform" Grid.Column="1"
               Source="{Binding PreviewImageFullPath, FallbackValue={StaticResource img_Fallback}, TargetNullValue={StaticResource img_Fallback}}" />

and this is the img_Fallback that it refers to:

<BitmapImage UriSource="pack://application:,,,/GamutBase;component/Images/Icon_PreviewMissing.png" x:Key="img_Fallback" />

When the relevant property is a null or empty string it shows up just fine. And as expected if the property is set and the file exists the main image shows up. However when the property is set to a location that isn't found nothing shows up at all, neither the main image (obviously) or the fallback. How do you make the fallback show up in this case?

sfaust
  • 2,089
  • 28
  • 54
  • Note that FallBackValue is the property of the binding itself. It means, when the binding fails, the FallBackValue will be used. But the binding itself does not fail, right? `Image` has an [ImageFailed](https://msdn.microsoft.com/en-us/library/system.windows.controls.image.imagefailed(v=vs.110).aspx) event, which you might use to detect failure to load an image from web... –  Jun 01 '17 at 20:31
  • ImageFailed event worked perfectly, I just told it to return a null on image failed and trigger a property change event, updates just as I would hope! If you make it an answer I will accept. – sfaust Jun 01 '17 at 21:16
  • You could also implement a binding converter that explicitly reads the image file or makes a http request, and returns a fallback image when the original one could not be loaded. – Clemens Jun 02 '17 at 05:37
  • I thought of that actually, just seems like a lot of code setup for a pretty simple thing. I like how the image control takes care of background loading on its own and with that solution I would have to implement it myself if I'm not mistaken. – sfaust Jun 02 '17 at 18:16
  • Feel free to mark my answer – Peter Jun 30 '17 at 07:44
  • Well as mentioned I like the image fallback better as it's less code and less complicated. However since @elgonzo did not make it an answer and yours definitely works I have accepted it... – sfaust Jul 01 '17 at 04:11

1 Answers1

2

This might be overkill - but you could solve this with a Behaviour.

XAML

    xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
    xmlns:myBehaviour="clr-namespace:MyWpfApp.Behaviours"

If you dont find the http://schemas.microsoft.com/expression/2010/interactivitynamespace - add it from your Assemblys in your Visual Studio.

    <Image Width="96" 
           Height="96" 
           Stretch="Uniform" 
           Grid.Column="1"
           Source="{Binding PreviewImageFullPath}">
        <i:Interaction.Behaviors>
            <myBehaviour:WebImageFallBackBehaviour FallBackImageSource="{StaticResource img_Fallback}" />
        </i:Interaction.Behaviors>
    </Image>

C#

    public class WebImageFallBackBehaviour : System.Windows.Interactivity.Behavior<Image>
    {


        public string FallBackImageSource
        {
            get { return (string)GetValue(FallBackImageSourceProperty); }
            set { SetValue(FallBackImageSourceProperty, value); }
        }

        // Using a DependencyProperty as the backing store for FallBackImageSource.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty FallBackImageSourceProperty =
            DependencyProperty.Register("FallBackImageSource", typeof(string), typeof(WebImageFallBackBehaviour), new PropertyMetadata(string.Empty));



        protected override void OnAttached()
        {
            base.OnAttached();
            this.AssociatedObject.ImageFailed += AssociatedObject_ImageFailed;
        }

        protected override void OnDetaching()
        {
            base.OnDetaching();
            this.AssociatedObject.ImageFailed -= AssociatedObject_ImageFailed;
        }

        private void AssociatedObject_ImageFailed(object sender, System.Windows.ExceptionRoutedEventArgs e)
        {
            Image self = sender as Image;

            if(self != null)
            {
                self.Source = new BitmapImage(new Uri(FallBackImageSource, UriKind.Relative));
            }
        }
    }

Additional Information

How to set the Source an Image programmaticly.

Peter
  • 1,655
  • 22
  • 44
  • Wow, that's quite a bit of code for this, thanks! I'm going with handling the image failed event in this case but upvoting for the effort on this... – sfaust Jun 01 '17 at 21:15