1

I had an issue with using Images and provide a right-click context menu for deleting the Image.

Originally I was binding the absolute file path:

<Image Source="{Binding Path=ImageFileName} .../>

where ImageFileName is something like C:\myapp\images\001.png.

I was getting an error, The process cannot access the file 'X' because it is being used by another process. After a lot of research, I figured out the necessary code change.

I used this Stackoverflow answer: Delete a file being used by another process and put the code into a ValueConverter.

XAML:

<Image Source="{Binding Path=ImageFileName, 
    Converter={StaticResource pathToImageConverter}}" ...>

Value Converter:

public class PathToImageConverter : IValueConverter
{
    public object Convert(object value, Type targetType,
                          object parameter, CultureInfo culture)
    {
        try
        {
            String fileName = value as String;
            if (fileName != null)
            {
                BitmapImage image = new BitmapImage();
                image.BeginInit();
                image.CacheOption = BitmapCacheOption.OnLoad;
                image.UriSource = new Uri(fileName);
                image.EndInit();
                return image;
            }
            return new BitmapImage();
        }
        catch
        {
            return new BitmapImage();
        }
    }

    public object ConvertBack(object value, Type targetType,
                              object parameter, CultureInfo culture)
    {
        throw new NotImplementedException();
    }
}
  1. The concern I have is memory usage. When I add Images to my Container, I see memory increase. However, when I delete Images, and the underlying file is deleted, I do not see any memory being freed up.

  2. I've also always thought of Bitmaps as a very inefficient file format, since they are uncompressed, and tend to be HUGE compared to a JPEG or PNG file. Is the System.Windows.Media.Imaging.BitmapSource class actually creating an uncompressed image out of my PNG?

Thanks very much in advance!

Philip

Community
  • 1
  • 1
Philip Tenn
  • 6,003
  • 8
  • 48
  • 85
  • 1
    Deleting an image doesn't guarantee that memory will be freed. Memory will be collected whenever Garbage Collector sees fit. Cross check by manually calling GC.Collect() and see if memory releases. – Rohit Vats Apr 06 '14 at 18:44
  • 1
    Have you considered using VirtualizingStackPanel and specifying DecodePixel* properties on your images ? – aybe Apr 06 '14 at 18:48
  • @Aybe No, I hadn't but I will look into those. To be honest, never heard of the VirtualizingStackPanel until now. – Philip Tenn Apr 06 '14 at 18:53
  • @RohitVats Good idea, thanks. I am going to try that now and see if the memory is released, or if something's still "hanging onto" those images. – Philip Tenn Apr 06 '14 at 18:58
  • Another tip, since the XAML version of `Image` do accepts `string` as `Source`, what is the point of creating `BitmapImage` in your converter ? This would makes sense when you set `Image.Source` in code-behind, IMO you should just let the Image class do its work, a good idea would be to use your converter to transform your path to an URI type. (http://msdn.microsoft.com/en-us/library/system.windows.controls.image.source(v=vs.110).aspx) – aybe Apr 06 '14 at 19:00
  • 2
    @Aybe The binding converter is needed here in order to be able to delete an image file right after loading it into a BitmapImage. That won't work with the built-in (string to ImageSource) type converter. – Clemens Apr 06 '14 at 19:56
  • @Clemens That's what I ran into ... when I used the built `ImageSource=FilePath` where `FilePath=C:\myapp\images\001.png`, there was a file lock. I do wonder what the built-in (`string` to `ImageSource`) type converter does under the covers though. Know of any way I could get the source for it? – Philip Tenn Apr 06 '14 at 19:59
  • 1
    Well, that's easy. Download and install [JetBrains dotPeek](http://www.jetbrains.com/decompiler/), load PresentationCore.dll, then inspect the ConvertFrom method in the ImageSourceConverter class in namespace System.Windows.Media. – Clemens Apr 06 '14 at 20:20
  • Right I forgot about that point. – aybe Apr 06 '14 at 20:22
  • @Clemens Great idea! I am going to go pull down dotPeek right now! – Philip Tenn Apr 06 '14 at 20:29
  • A note on your second question about bitmap vs jpeg/png: In order to display an image on screen, it has to be decoded into one of the system's native bitmap formats. This is unrelated to the encoding format of an image buffer in a file. So for memory consumption it doesn't matter if you load a jpeg, png, or bmp file. The decoded image is always the same size. – Clemens Apr 06 '14 at 20:41

1 Answers1

4

WPF caches up to 300 ImageSource objects (using weak references) when you load an image from a URI. To avoid this, set the BitmapCreateOptions.IgnoreImageCache option or use a FileStream to load the image instead.

Note that this could have an adverse effect on your app's performance, especially in a situation where you're scrolling and loading images in a virtualized ListBox. But if you're loading really large images, you might want to avoid it.

Use the following code in your converter (note also the added image.Freeze call, which improves performance by making the object read-only and thread-agnostic):

using (var fs = new FileStream(fileName, FileMode.Open, FileAccess.Read))
{
    BitmapImage image = new BitmapImage();
    image.BeginInit();
    image.CacheOption = BitmapCacheOption.OnLoad;
    image.StreamSource = fs;
    image.EndInit();
    image.Freeze();
    return image;
}

Another possible performance optimization is to set DecodePixelWidth to downscale the image.

Regarding your concern about formats -- bitmap is a general name for pixel-based images (in contrast to vector-based images). The file formats you mentioned (PNG, JPEG) are bitmaps as well, they're just encoded (possibly with some compression). The BitmapImage class uses WIC to decode them so they can be rendered on the screen.

Eli Arbel
  • 22,391
  • 3
  • 45
  • 71
  • That is very helpful, thanks for the answer. When I get home tonight, I am going to try the image.Freeze(). Also appreciate the info about the bitmap ... that makes sens. – Philip Tenn Apr 07 '14 at 16:01