How to release/caching image resources after get them using XAML

1.7k views Asked by At

Currently I’m working in a WPF project using prism 6, avoiding code-behind as much as possible. I have a variable in the ViewModel which contains the local path where the image in question is stored. In the View, I bind the source property of the Image control to the ViewModel’s variable and I can get the image displayed.

The problem arises when I need to delete the image from disk when it’s still being displayed in the view. Then, if I do so, I get the typical “Image is in use”. I read in the forum about image caching and I wonder if I could apply it in this case to avoid this behavior only using XAML if possible.

I’m using this approach:

<Border Grid.Column="0" BorderThickness="2" BorderBrush="#808080" Height="300" 
        Width="300" Background="#FCFCFC">
    <Image Height="350" Width="350" HorizontalAlignment="Center" 
           VerticalAlignment="Center" Source="{Binding ImageUri}"/>
</Border>
4

There are 4 answers

0
Peregrine On BEST ANSWER

You don't need to create an ImageSource object in your ViewModel. Instead, just make a property of type byte[], which you set from the file using standard IO functions. You can then bind the Source property of your Image control to this byte array - the control will automatically handle the conversion to a displayable image.

See my answer to this question for means to handle this in an async manner.

Once the file has been read, you can delete it as required. If the image comes from some external source, e.g. a web download, you can use the data directly without even needing to save it to disk at all.

4
Prateek Shrivastava On

As long as you have the ImageSource pointing to a file on local disk - you cannot delete it. Following 2 approaches come to my mind:

  1. Since you have the path to actual image - why not copy it to a TEMP folder and Bind to this path. That way you are never referring to actual image which can now be deleted.

  2. Use/Build a http host (CMS) for image files (like) imgur (which StackOverflow uses too). Always save/host files on this. That way ImagesSource is a http uri.

0
Clemens On

If you can assign the DataContext before calling InitializeComponent (of the MainWindow or UserControl), you can explicitly create a BitmapImage in XAML and set its CacheOption to OnLoad. The framework will then immediately load the file and not keep it open.

<Image>
    <Image.Source>
        <BitmapImage UriSource="{Binding ImageUri}" CacheOption="OnLoad"/>
    </Image.Source>
</Image>

In case the DataContext or the ImageUri property has to be set after InitializeComponent, you could add a property of type ImageSource

public ImageSource Image
{
    get { return BitmapFrame.Create(
              ImageUri, BitmapCreateOptions.None, BitmapCacheOption.OnLoad); }
}

or byte[]:

public byte[] Image
{
    get { return File.ReadAllBytes(ImageUri.LocalPath); }
}

and bind to it like:

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

or asynchronously to increase the UI's responsiveness:

<Image Source="{Binding Image, IsAsync=True}"/>
1
Sir Rufo On

You can write a converter for this to convert the filename to the bytes

public class FilenameToBytesConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        if (value == null)
        {
            return null;
        }
        var path = (string)value;

        if (!File.Exists(path))
        {
            return null;
        }

        return File.ReadAllBytes(path);
    }

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

and use it in the view

<Window ...
        xmlns:Converters="clr-namespace:YourNamespacePath.Converters"/>

    <Window.Resources>
        <Converters:FilenameToBytesConverter x:Key="FilenameToBytesConverter"/>
    </Window.Resources>

    <Image Source="{Binding ImageUri, Converter={StaticResource FilenameToBytesConverter}}"/>