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));
}
}
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;
}
}
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.
Very simple solution:
System.GC.Collect();
System.GC.WaitForPendingFinalizers();
File.Copy(od.FileName, imageLocal, true);
Related
I have a .zip file containing three jpg images.
I want to display all three of them in one FlowDocumentReader in WPF.
This is my code:
FlowDocument flowDoc = new FlowDocument();
ZipArchive zipFile = ZipFile.OpenRead("images.zip");
foreach (ZipArchiveEntry zip in zipFile.Entries)
{
Stream imageStream = zip.Open();
BitmapImage bitmapImage = new BitmapImage();
bitmapImage.BeginInit();
bitmapImage.CacheOption = BitmapCacheOption.OnLoad;
bitmapImage.StreamSource = imageStream;
bitmapImage.EndInit();
bitmapImage.DownloadCompleted += BitmapImage_DownloadCompleted;
Image myImage = new Image
{
Source = bitmapImage,
Stretch = Stretch.Uniform
};
BlockUIContainer uiContainer = new BlockUIContainer
{
Child = myImage
};
flowDoc.Blocks.Add(uiContainer);
}
FlowDocReader.Document = flowDoc;
Up to the last line it always works without problems, but when the FlowDocumentReader loads the document there randomly appear problems:
Sometimes the code works well and alle three images are displayed in the FlowDocumentReader.
Sometimes some of the images are displayed with some errors (shifted colors at some bottom lines of pixels). I get no exception in this cases.
Sometimes only one, two or no image is displayed. In this case there is 2x per image not displayed Exception thrown: 'System.IO.InvalidDataException' in System.dll. This exception was originally thrown at this call stack: System.IO.Compression.InflaterZlib.Inflate(System.IO.Compression.ZLibNative.FlushCode)
I get all of this random results using the same .zip file. I can reproduce the problem using different .zip files.
Am I doing something wrong, or is there a known bug?
There are situations where BitmapImage does apparently not read its StreamSource until the end. This may for example occur when you read a bitmap from the response stream of a HTTP request.
I also observed it with the Stream returned from the ZipArchiveEntry.Open() method.
My workaround in these situations is to copy the frame buffer into an intermediate MemoryStream:
using (var archive = ZipFile.OpenRead("images.zip"))
{
foreach (var entry in archive.Entries)
{
using (var imageStream = entry.Open())
using (var memoryStream = new MemoryStream())
{
imageStream.CopyTo(memoryStream);
memoryStream.Seek(0, SeekOrigin.Begin);
var bitmapImage = new BitmapImage();
bitmapImage.BeginInit();
bitmapImage.CacheOption = BitmapCacheOption.OnLoad;
bitmapImage.StreamSource = memoryStream;
bitmapImage.EndInit();
...
}
}
}
The error is that the memory stream is not accessible when it is closed. It occurs when I try to set the previous image of the image control back to a Gif. When the previous image was a gif, I convert it to a bitmap with a memory stream. When the previous image was a normal png or jpg, it can display successfully.
I have tried to research how to fix the error. A solution I've tried is to convert the images to byte[] but then the gif isn't animating like they have been converted to a static image.
To display the gif I use a NuGet package called WPFAnimatedGif and to make the Wpf look better I use the NuGet package Materialdesigntheme
This is the event code that fires upon drag leave
if (previousIcon != null)
{
ToggleButton buttonControl = (ToggleButton)sender;
Image imageControl = (Image)((Grid)buttonControl.Content).Children[1];
if (previousIcon.ContainsKey(buttonControl))
ImageBehavior.SetAnimatedSource(imageControl, previousIcon[buttonControl]);
}
How I convert it
BitmapImage bmImage = new BitmapImage();
using (MemoryStream stream = new MemoryStream())
{
PngBitmapEncoder encoder = new PngBitmapEncoder();
encoder.Frames.Add(BitmapFrame.Create(wbm));
encoder.Save(stream);
bmImage.BeginInit();
bmImage.CacheOption = BitmapCacheOption.OnLoad;
bmImage.StreamSource = stream;
bmImage.EndInit();
bmImage.Freeze();
}
For full code visit these Pastebins:
https://pastebin.com/PQkFAhQj
https://pastebin.com/wCEBeN1X
https://pastebin.com/wAERvDW3
Edit
Updated conversion function based upon responses
public BitmapImage ConvertWriteableBitmapToBitmapImage(WriteableBitmap wbm)
{
BitmapImage bmImage = new BitmapImage();
MemoryStream stream = new MemoryStream();
PngBitmapEncoder encoder = new PngBitmapEncoder();
encoder.Frames.Add(BitmapFrame.Create(wbm));
encoder.Save(stream);
//stream.Position = 0;
bmImage.BeginInit();
bmImage.CacheOption = BitmapCacheOption.OnLoad;
bmImage.StreamSource = stream;
bmImage.EndInit();
bmImage.Freeze();
return bmImage;
}
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));
}
}
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;
}
}
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.
Very simple solution:
System.GC.Collect();
System.GC.WaitForPendingFinalizers();
File.Copy(od.FileName, imageLocal, true);
I'm using the following line of code to open an Image from a file:
pictureBox1.Image = Image.FromFile("test.png");
I expect it to lock the file, load the image to memory, set pictureBox1.Image to the copy in memory, and release the lock. In reality, the lock won't go away until I Dispose() of the Image in memory. I can not release the lock on the file on the harddrive that I am no longer using until I get rid of the file in memory that I am using.
Microsoft's site mentions it in a C#-labeled article, but their solution is written in visual basic, which is useless to me.
In summary:
I want to set pictureBox1.Image to the image stored in "test.png", then let the user edit or delete "test.png" or whatever.
The approach with stream is not correct.
See here https://stackoverflow.com/a/8701748/355264
Correct code from above link:
Image img;
using (var bmpTemp = new Bitmap("image_file_path"))
{
img = new Bitmap(bmpTemp);
}
Or better yet, use a using statement (the code below is otherwise copied from sylon's [deleted] post). This way if the Image.FromStream throws an exception, you can still be assured that the stream is immediately closed.
using (FileStream stream = new FileStream("test.png", FileMode.Open, FileAccess.Read))
{
pictureBox1.Image = Image.FromStream(stream);
}
You can also use a stream to read the image then close the stream.
FileStream stream = new FileStream("test.png", FileMode.Open, FileAccess.Read);
pictureBox1.Image = Image.FromStream(stream);
stream.Close();
The easiest ever way I found is to freeze the object that contains the Source (path to the file). All controls that can contain an image, seem to have a .Source which, if not null, it will lock the file it points to.
Now the trick is to change the Image control to a "read-only" state, which then unlocks the file.
My solution:
private Image CreatePreviewImage()
{
Image ReportImage = new Image();
Uri path = new Uri(#"C:\Folder\Image1.png");
if (File.Exists(path.OriginalString))
{
ReportImage.Name = "Report1";
ReportImage.Source = LoadImageFromFile(path);
}
return ReportImage;
}
public ImageSource LoadImageFromFile(Uri path)
{
BitmapImage bitmap = new BitmapImage();
bitmap.BeginInit();
bitmap.UriSource = path;
bitmap.CacheOption = BitmapCacheOption.OnLoad;
bitmap.CreateOptions = BitmapCreateOptions.IgnoreImageCache;
bitmap.DecodePixelWidth = 900;
bitmap.EndInit();
bitmap.Freeze(); //This is the magic line that releases/unlocks the file.
return bitmap;
}
talking open, read and release
StreamReader streamReader = new StreamReader("picture.png");
Bitmap tmpBitmap = (Bitmap)Bitmap.FromStream(streamReader.BaseStream);
streamReader.Close();
pictureBox1.Image = tmpBitmap;`
I have an image control with a source image located in my c drive. I get a message that the image is being used by another process whenever I try to delete the original image to change it with another one dynamically. How do I release the image from the image control to be able to delete it.
I tried this variants:
string path = ((BitmapImage)img.Source).UriSource.LocalPath;
img.SetValue(System.Windows.Controls.Image.SourceProperty, null);
File.Delete(path);
And:
string path = ((BitmapImage)img.Source).UriSource.LocalPath;
img.Source = null;
File.Delete(path)
But it's not work...
Try setting the bitmap image through the stream source property. That way the app won't put a lock on the file since you loaded it through a stream.
http://msdn.microsoft.com/en-us/library/system.windows.media.imaging.bitmapimage.streamsource(VS.85).aspx
//this function allows you to load an image from a file and release it
BitmapImage loadPhoto(string path)
{
BitmapImage bmi = new BitmapImage();
bmi.BeginInit();
bmi.CacheOption = BitmapCacheOption.OnLoad;
bmi.CreateOptions = BitmapCreateOptions.IgnoreImageCache;
bmi.UriSource = new Uri(path);
bmi.EndInit();
return bmi;
}