views:

167

answers:

2

I'm having a problem in WPF where a window doesn't release it's file-lock on the background image file after closing, before another part of the application tries to write to the image.

So as an example; say I have a WPF app consisting of 3 windows, 1 "menu" selection window and 2 others. Both of the windows create an ImageBrush using a BitmapImage as the ImageSource (the same image).

Window A has a button that when pressed, cycles through the available background images by copying them each over the file used as the original ImageSource and creating a new ImageBrush and setting the Window.Background to the new brush.

Window B simply uses the ImageBrush to draw the Window.Background.

If Window A is launched, backgrounds switched, closed and then Window B launched, everything is fine.

If Window B is launched, closed, then Window A is launched and backgrounds switched it crashes. Trying to switch the backgrounds throws an IOException because:

"The process cannot access the file 'C:\Backgrounds\Background.png' because it is being used by another process."

So Window B must still be holding onto it somehow!? I have tried doing a GC.Collect(); GC.WaitForPendingFinalizers(); to see if that cures the problem but it doesn't.

+2  A: 

I assume you're loading the image directly from the file, like that ?

BitmapImage img = new BitmapImage();
img.BeginInit();
img.UriSource = imageUrl;
img.EndInit();

Try to load it from a stream instead ; that way you can close the stream yourself after the image is loaded, so that the file isn't locked :

BitmapImage img = new BitmapImage();
using (FileStream fs = File.OpenRead(imageFilePath)
{
    img.BeginInit();
    img.StreamSource = fs;
    img.EndInit();
}
Thomas Levesque
Actually it was worse than that: backgroundBrush.ImageSource = new BitmapImage(new Uri(Constants.ShellLocation + @"Background\TempBkgrnd.png", UriKind.Absolute));
Siyfion
+4  A: 

The answer Thomas gave is correct, and works well if you have a file path, don't want to cache the bitmap, and don't want to use XAML.

However it should also be mentioned that BitmapImage has a built-in way to load the bitmap immediately by setting BitmapCacheOption:

BitmapImage img = new BitmapImage { CacheOption = BitmapCacheOption.OnLoad };
img.BeginInit();
img.UriSource = imageUrl;
img.EndInit();

or

<BitmapImage CacheOption="OnLoad" UriSource="..." />

This will load the bitmap immediately and explicitly close the stream, just as using a FileStream would, but with several differences:

  • It will work with any Uri, such as a pack:// Uri.
  • It can be used directly from XAML
  • The bitmap is cached in the bitmap cache, so future use of the same Uri won't go to the disk. In your particular application this may be a bad thing, but for other uses it may be a desirable feature.
Ray Burns
That's brill, thank you both!
Siyfion