How do I change an image source dynamically from code-behind in WPF with an image in Properties.Resources?

dtaylor picture dtaylor · Feb 21, 2013 · Viewed 57.8k times · Source

I have a WPF application that needs to provide feedback to the user about an internal state. The design is to have three images, call them Red, Yellow, and Green. One of these images will be displayed at a time depending on the state. Here are the points:

  • The three images are in Properties.Resources in the code-behind
  • Only one of the images will be shown at a time.
  • The state change comes from a process in code-behind and not from the user.
  • I would like to bind an image control so that I can change the image from code-behind.

I’m assuming I’ll need an image converter to change the JPG image to an image source such as:


[ValueConversion(typeof(System.Drawing.Bitmap), typeof(ImageSource))]
public class BitmapToImageSourceConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        var bmp = value as System.Drawing.Bitmap;
        if (bmp == null)
            return null;
        return System.Windows.Interop.Imaging.CreateBitmapSourceFromHBitmap(
                    bmp.GetHbitmap(),
                    IntPtr.Zero,
                    Int32Rect.Empty,
                    BitmapSizeOptions.FromEmptyOptions());
    }

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

I’d prefer to convert the images once during initialization and keep a list of Image sources. I’m also assuming I’ll need a dependency property to bind the control to, but I’m not sure how to set that up with this list of image sources:


    // Dependancy Property for the North Image
    public static readonly DependencyProperty NorthImagePathProperty
        = DependencyProperty.Register(
            "NorthImagePath",
            typeof(ImageSource),
            typeof(MainWindow),
            new PropertyMetadata("**Don't know what goes here!!!**"));

    // Property wrapper for the dependancy property
    public ImageSource NorthImagePath
    {
        get { return (ImageSource)GetValue(NorthImagePathProperty); }
        set { SetValue(NorthImagePathProperty, value); }
    }

Answer

Clemens picture Clemens · Feb 21, 2013

Although an image resource in a WPF project generates a System.Drawing.Bitmap property in Resources.Designer.cs, you could directly create a BitmapImage from that resource. You only need to set the Build Action of the image file to Resource (instead of the default None).

If you have a file Red.jpg in the Resources folder of your Visual Studio Project, creating a BitmapImage would look like shown below. It uses a WPF Pack Uri.

var uri = new Uri("pack://application:,,,/Resources/Red.jpg");
var bitmap = new BitmapImage(uri);

If you have an Image control declared somewhere in XAML like this:

<Image x:Name="image"/>

you could simply set the Source property of the image to your BitmapImage in code behind:

image.Source = bitmap;

In case you prefer to set the Source property by binding you could create a string property that returns the image URI. The string will automatically be converted to a BitmapImage by a built-in TypeConverter in WPF.

public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();
        DataContext = this;
        ImageUri = "pack://application:,,,/Resources/Red.jpg";
    }

    public static readonly DependencyProperty ImageUriProperty =
        DependencyProperty.Register("ImageUri", typeof(string), typeof(MainWindow));

    public string ImageUri
    {
        get { return (string)GetValue(ImageUriProperty); }
        set { SetValue(ImageUriProperty, value); }
    }
}

In XAML you would bind to that property like this:

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

Of course you could as well declare the property to be of type ImageSource

public static readonly DependencyProperty ImageProperty =
    DependencyProperty.Register("Image", typeof(ImageSource), typeof(MainWindow));

public ImageSource Image
{
    get { return (ImageSource)GetValue(ImageProperty); }
    set { SetValue(ImageProperty, value); }
}

and bind in the same way:

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

Now you could pre-load your images and put them into the property as needed:

private ImageSource imageRed =
    new BitmapImage(new Uri("pack://application:,,,/Resources/Red.jpg"));
private ImageSource imageBlue =
    new BitmapImage(new Uri("pack://application:,,,/Resources/Blue.jpg"));
...
Image = imageBlue;

UPDATE: After all, your images do not need to be resources in the Visual Studio project. You could just add a project folder, put the image files into that folder and set their Build Action to Resource. If for example you call the folder Images, the URI would be pack://application:,,,/Images/Red.jpg.