none
Disposing a WPF Image or BitmapImage so the Source picture file can be modified

    Question

  • I'm trying to create a program using WPF to display iTunes album art.  The problem I have is that the picture has to saved to a file first and then displayed in an Image (System.Windows.Controls.Image) component on the WPF form using a BitmapImage and its UriSource property.  All this is fine, except when the track changes and has to overwrite the 'artwork.jpg' file which it cannot do as either the Image control or the BitmapImage (or both) has locked the file (I just get an UnauthorisedAccessException).

    With a normal windows forms PictureBox control (i.e. not using WPF) I can just use a Bitmap object to store the saved image from its path and call the .Dispose() method on that Bitmap when I want to modify the source image file.  Is there any way of doing something like this on WPF?  I tried using GC.Collect() but this did not seem to do anything.

    Also, I am running the WPF Window from a standard windows forms Form and use .Close() on the WPF Window when it's not displayed (like when the artwork is trying to be changed) and use "window1 = new Window1()" each time I need to window to be displayed again, but this does not seem to free any memory or release the source of the Image control while the window is closed.

    Basically is there any way of disposing the Window or just releasing the picture file from it's hold so it can be changed?
    Thursday, August 21, 2008 12:46 PM

Answers

  • Finally sorted it by playing about with Streams and using the BitmapImage's StreamSource rather than UriSource.  One thing that confused me is you have to use BeginInit() and EndInit() around anywhere you set properties of the BitmapImage.
    I also tried doing it by using UriSource and setting the BitmapCacheOption to Onload, this stopped my UnauthorisedAccessExceptions but for some reason the BitmapImage didn't change when a new UriSource was set (I guess it was still using it's cached version).  It all works ok using StreamSource and leaving the default BitmapCacheOption setting.

    Here's the code I used in case anyone finds it useful:

    window1 = new Window1();  // Create new instance of the window  
    System.Windows.Media.Imaging.BitmapImage albumArt = new System.Windows.Media.Imaging.BitmapImage();  // Create new BitmapImage  
    System.IO.Stream stream = new System.IO.MemoryStream();  // Create new MemoryStream  
    Bitmap bitmap = new Bitmap(albumArtSource);  // Create new Bitmap (System.Drawing.Bitmap) from the existing image file (albumArtSource set to its path name)  
    bitmap.Save(stream, System.Drawing.Imaging.ImageFormat.Png);  // Save the loaded Bitmap into the MemoryStream - Png format was the only one I tried that didn't cause an error (tried Jpg, Bmp, MemoryBmp)  
    bitmap.Dispose();  // Dispose bitmap so it releases the source image file  
    albumArt.BeginInit();  // Begin the BitmapImage's initialisation  
    albumArt.StreamSource = stream;  // Set the BitmapImage's StreamSource to the MemoryStream containing the image  
    albumArt.EndInit();  // End the BitmapImage's initialisation  
    window1.albumArt.Source = albumArt;  // Finally, set the WPF Image component's source to the BitmapImage 


    Thank's for the help.
    • Marked as answer by RustyDrummer Friday, August 22, 2008 1:26 PM
    Friday, August 22, 2008 1:26 PM
  • Have you looked into setting the BitmapImage's StreamSource property instead of using UriSource??

    HTH,
    Drew
    • Marked as answer by RustyDrummer Friday, August 22, 2008 1:06 PM
    Friday, August 22, 2008 3:33 AM
    Moderator

All replies

  • Have you looked into setting the BitmapImage's StreamSource property instead of using UriSource??

    HTH,
    Drew
    • Marked as answer by RustyDrummer Friday, August 22, 2008 1:06 PM
    Friday, August 22, 2008 3:33 AM
    Moderator
  • Looks like it could maybe work with that and BitmapCacheOption.Onload

    I'll give it a try, thanks
    Friday, August 22, 2008 11:57 AM
  • Finally sorted it by playing about with Streams and using the BitmapImage's StreamSource rather than UriSource.  One thing that confused me is you have to use BeginInit() and EndInit() around anywhere you set properties of the BitmapImage.
    I also tried doing it by using UriSource and setting the BitmapCacheOption to Onload, this stopped my UnauthorisedAccessExceptions but for some reason the BitmapImage didn't change when a new UriSource was set (I guess it was still using it's cached version).  It all works ok using StreamSource and leaving the default BitmapCacheOption setting.

    Here's the code I used in case anyone finds it useful:

    window1 = new Window1();  // Create new instance of the window  
    System.Windows.Media.Imaging.BitmapImage albumArt = new System.Windows.Media.Imaging.BitmapImage();  // Create new BitmapImage  
    System.IO.Stream stream = new System.IO.MemoryStream();  // Create new MemoryStream  
    Bitmap bitmap = new Bitmap(albumArtSource);  // Create new Bitmap (System.Drawing.Bitmap) from the existing image file (albumArtSource set to its path name)  
    bitmap.Save(stream, System.Drawing.Imaging.ImageFormat.Png);  // Save the loaded Bitmap into the MemoryStream - Png format was the only one I tried that didn't cause an error (tried Jpg, Bmp, MemoryBmp)  
    bitmap.Dispose();  // Dispose bitmap so it releases the source image file  
    albumArt.BeginInit();  // Begin the BitmapImage's initialisation  
    albumArt.StreamSource = stream;  // Set the BitmapImage's StreamSource to the MemoryStream containing the image  
    albumArt.EndInit();  // End the BitmapImage's initialisation  
    window1.albumArt.Source = albumArt;  // Finally, set the WPF Image component's source to the BitmapImage 


    Thank's for the help.
    • Marked as answer by RustyDrummer Friday, August 22, 2008 1:26 PM
    Friday, August 22, 2008 1:26 PM
  • That solution works, but there is a lot extra that you don't need.

    Rather than loading it as an image then saving it to the memory stream, why not load the image file directly to a stream:

    byte[] buffer = System.IO.File.ReadAllBytes(path); 
    MemoryStream ms = new MemoryStream(buffer); 
     
    BitmapImage image = new BitmapImage(); 
    image.BeginInit(); 
    image.StreamSource = ms; 
    image.EndInit(); 
    image.Freeze(); 

    • Proposed as answer by Alan D Jackson Monday, December 15, 2008 2:32 PM
    Monday, December 15, 2008 2:31 PM
  • why readallbytes and create a memorystream when you could just use a filestream?

    using (FileStream fs = new FileStream(path)) { image.BeginInit();
    image.CacheOption = BitmapCacheOption.OnLoad;
    image.StreamSource = fs;
    image.EndInit();
    }
    image.Freeze();


    • Edited by Jamie Pate Tuesday, April 03, 2012 6:37 PM
    Tuesday, April 03, 2012 6:32 PM