6

I'm trying create a user perfil edit window, in this window has a Image control
When I selected a image file, it will show in this Image control and copy this file at my image folder, first time is all right, but second time, it show a error

"The process cannot access the file 'C:\1.jpg' because it is being used by another process."

I think it is because my Image control is using this file, so, I don't know what can I do

private void Select_Click(object sender, RoutedEventArgs e)
{
    OpenFileDialog od = new OpenFileDialog();
    if (od.ShowDialog() == true)
    {
        string imageLocal = @"C:/1.jpg";
        File.Copy(od.FileName, imageLocal, true);
        image1.Source = new BitmapImage(new Uri(imageLocal));
    }
}
Gayot Fow
  • 8,710
  • 1
  • 35
  • 48
Lai32290
  • 8,062
  • 19
  • 65
  • 99
  • The issue here lies in how you are loading the file and converting it to an image. It won't work using the constructor method you are using. There's alternate methods if you want to avoid that 'used by another process' exception. – Gayot Fow Aug 10 '13 at 23:37
  • what can I do for resolver this problem? – Lai32290 Aug 10 '13 at 23:38

3 Answers3

6

If you want to load and display an image, and keep the file amenable to operations in the file system (like reloading it or moving it to another directory), the Uri constructor will not work because (as you point out), the BitmapImage class hangs on to the file handle.

Instead, use a method like this...

    private static BitmapImage ByStream(FileInfo info)
    {   //http://social.msdn.microsoft.com/Forums/en-US/wpf/thread/dee7cb68-aca3-402b-b159-2de933f933f1
        try
        {
            if (info.Exists)
            {
                // do this so that the image file can be moved in the file system
                BitmapImage result = new BitmapImage();
                // Create new BitmapImage   
                Stream stream = new MemoryStream(); // Create new MemoryStream   
                Bitmap bitmap = new Bitmap(info.FullName);
                // 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   
                result.BeginInit(); // Begin the BitmapImage's initialisation   
                result.StreamSource = stream;
                // Set the BitmapImage's StreamSource to the MemoryStream containing the image   
                result.EndInit(); // End the BitmapImage's initialisation   
                return result; // Finally, set the WPF Image component's source to the 
                                BitmapImage  
            }
            return null;
        }
        catch
        {
            return null;
        }
    }

This method takes a FileInfo and returns a BitmapImage which you can display and simultaneously move it to another directory or display it again.

A much simpler method, copied from another answer below, is this:

public static BitmapImage LoadBitmapImage(string fileName)
{
    using (var stream = new FileStream(fileName, FileMode.Open))
    {
        var bitmapImage = new BitmapImage();
        bitmapImage.BeginInit();
        bitmapImage.CacheOption = BitmapCacheOption.OnLoad;
        bitmapImage.StreamSource = stream;
        bitmapImage.EndInit();
        bitmapImage.Freeze(); 
        return bitmapImage;
    }
}
Clemens
  • 123,504
  • 12
  • 155
  • 268
Gayot Fow
  • 8,710
  • 1
  • 35
  • 48
  • I'm trying open JPG file, I had edited code for this format; bitmap.Save(stream, ImageFormat.Jpeg); . But it will run FileFormatException. "The image cannot be decoded. The image header might be corrupted." – Lai32290 Aug 11 '13 at 01:08
  • Try it with an uncorrupted file – Gayot Fow Aug 11 '13 at 05:35
  • Yes, when I tried ImageFormat.PNG and PNG file, no had problem, just has problem when I use JPEG, for any jpeg or jpg file – Lai32290 Aug 12 '13 at 01:32
  • The png encoder works for jpg files also if they are not corrupted. All types are rendered to the screen accurately. And at the central issues, you are not getting the exception any longer? – Gayot Fow Aug 12 '13 at 01:54
  • @Lai32290 There is no need to explicitly choose a decoder. You can simply pass the file content as stream to the BitmapImage `StreamSource` property. WPF will automatically select the right decoder, as shown in my answer. Moreover, the solution shown here involves a `System.Drawing.Bitmap`, which is not WPF but WinForms. It decodes the image file into a `Bitmap`, then encodes it back into a MemoryStream and finally decodes that into a BitmapImage. This is extremely inefficient compared to directly decoding a FileStream into a BitmapImage. – Clemens Aug 12 '13 at 07:26
  • @Garry I greatly dislike this answer, as it shows a complicated and inefficient way of decoding an image file in WPF. WPF has built-in support for decoding file streams, hence there is no need at all to use an intermediate System.Drawing.Bitmap. As the answer has already been accepted, I fear that it might lead others into the wrong direction. I'd really appreciate (and upvote) if you would edit the answer to show the correct way of doing it. Meanwhile it even got another upvote, don't know why... – Clemens Aug 12 '13 at 07:35
  • @Clemen, of course. But please be fair enough to understand that this method has the imprimatur of several years of production implementation and the accumulated processing of hundreds of thousands of images without memory leaks or other fault. The conceptual design for what it does was originally rooted by an authority at Microsoft. However, I'm happy to take your solution and put it through the same stress scenarios and revist this page with the results if that's of any use?. – Gayot Fow Aug 12 '13 at 08:15
  • @Garry That would be great! It would of course be interesting to see that for some hidden reason it is *more efficient* or more stable to intermediately decode and encode an image. Otherwise I'm just expecting to see the "WPF way" here. – Clemens Aug 12 '13 at 08:43
  • I will site your code in my photo browser http://tyburnphotobrowser.codeplex.com/ to replace the existing code and put it through the paces. The testing regimen takes about 60 hours on a 'good' try; so it will take a while for me to organize it (you'll have to be patient). But you might end up with a thanx and a tip of the hat on Codeplex :) – Gayot Fow Aug 12 '13 at 09:52
  • We have an opportunity to collaborate. Send me email and I'll make a proposal to you. – Gayot Fow Aug 12 '13 at 15:39
  • Would I be able to run all those tests in that project after modifying the code? By the way, if you follow the link from your code example and scroll down (past 2008 into 2012) you will notice this comment: `why readallbytes and create a memorystream when you could just use a filestream?`. That's exactly what I'm talking about. – Clemens Aug 12 '13 at 21:42
  • Of course. If the improvements you cite bear fruit, you can modify the app and issue a new release as co-author, plus explain your rationale in the release notes! I will have to ask Codeplex how to add another person to the page but otherwise fine. Why not? – Gayot Fow Aug 12 '13 at 22:04
  • Garry, I could not build your project out of the box (see the issue there). Therefore I suggest that you try the improvement yourself. Changing the code is a matter of a minute. – Clemens Aug 13 '13 at 07:22
  • I'll take it from here. Thanks for your help. – Gayot Fow Aug 13 '13 at 07:55
  • Lets say using the second method - the "LoadBitmapImage" one - and I have a list of Bitmap images loaded like that. And I call BitmapList.Clear(). Will that properly remove the bitmaps from memory the next GC sweep or do I need to call Dispose() on each bitmap before .Clear()? – user99999991 Jun 17 '15 at 21:33
2

The method shown below loads a BitmapImage from file and immediately closes the file after loading. Note that it is necessary to set the BitmapCacheOption.OnLoad flag when the source stream is closed right after EndInit.

public static BitmapImage LoadBitmapImage(string fileName)
{
    using (var stream = new FileStream(fileName, FileMode.Open))
    {
        var bitmapImage = new BitmapImage();
        bitmapImage.BeginInit();
        bitmapImage.CacheOption = BitmapCacheOption.OnLoad;
        bitmapImage.StreamSource = stream;
        bitmapImage.EndInit();
        bitmapImage.Freeze(); // just in case you want to load the image in another thread
        return bitmapImage;
    }
}

This code will work for any image format that is supported by WPF. When passing the image file content as stream to the StreamSource property, WPF will automatically create the appropriate decoder.

Clemens
  • 123,504
  • 12
  • 155
  • 268
0

Very simple solution:

System.GC.Collect();  
System.GC.WaitForPendingFinalizers();

File.Copy(od.FileName, imageLocal, true);
Clemens
  • 123,504
  • 12
  • 155
  • 268
sma6871
  • 3,198
  • 3
  • 38
  • 52