How can i dispose all the images in a generic list in WPF?
Here is my try:
//piclist is a global variable pointing to a folder on harddrive
foreach (string s in this.piclist)
{
this.picsToDisplay.Add(this.BitmapFromUri(new Uri(s)));
}
private BitmapImage LoadImage(string myImageFile)
{
BitmapImage myRetVal = null;
if (myImageFile != null)
{
BitmapImage image = new BitmapImage();
using (FileStream stream = File.OpenRead(myImageFile))
{
image.BeginInit();
image.CacheOption = BitmapCacheOption.OnLoad;
image.StreamSource = stream;
image.EndInit();
}
myRetVal = image;
}
return myRetVal;
}
and this is how i clear and dispose everything:
if (this.picsToDisplay.Count > 0)
{
foreach (BitmapImage b in this.picsToDisplay)
b.StreamSource.Dispose();
this.picsToDisplay.Clear();
}
Array.ForEach(Directory.GetFiles(this.tempPics),
delegate(string path) { File.Delete(path); });
It crashes first at b.StreamSource.Dispose();
Saying that streamsource is null 'reference not set to an instance of an object'
If i comment out that line, it crashes at this part: File.Delete(path);
With the message that the picture i'm trying to delete is in use.
The pictures getting displayed in an image control in WPF.
Its source is already set to NULL before i proceed to dispose etc.
What am i missing guys?
============
EDIT
private void btnLoadPictures_Click(object sender, EventArgs e)
{
this.ClearPicturesFromList();
DirectoryInfo Dir = new DirectoryInfo(this.pictures);
List<string> stringList = new List<string>();
FileInfo[] picList = Dir.GetFiles();
stringList = (from FI in picList
where FI.LastWriteTime.Date.ToString("dd-MM-yyyy") ==
this.dpPictureDate.SelectedDate.Value.ToString("dd-MM-yyyy")
select (FI.FullName)).ToList();
try
{
if (Directory.Exists(this.tempPics))
{
if (stringList.Count > 0)
{
foreach (string s in stringList)
{
string destFolder = System.IO.Path.Combine(this.tempPics, System.IO.Path.GetFileName(s));
File.Copy(s, destFolder, true);
}
DirectoryInfo Dir2 = new DirectoryInfo(this.tempPics);
FileInfo[] pics = Dir2.GetFiles();
this.picsInTempFolder = (from FI in pics
select (FI.FullName)).ToList();
foreach (string s in this.picsInTempFolder)
{
this.picsToDisplay.Add(this.LoadImage(s));
}
this.indexCounter = 0;
this.imgBox.Source = (BitmapImage)this.picsToDisplay[this.indexCounter];
this.tbxPictureName.Text = System.IO.Path.GetFileName(this.picsInTempFolder[this.indexCounter]);
stringList.Clear();
pics = null;
}
}
}
catch (Exception exp)
{
this.WriteToRichTextBox(exp.ToString());
}
}
pressing the clearimages button for the first time, i get the exception about the file being in use. doing it the second time, actually works fine.
putting a thread.sleep between clearing all lists and stuff than delete the files in the temp directory doenst work. but somehow it needs a sort of delay.
try this method for loading images
private BitmapImage LoadImage(string myImageFile)
{
BitmapImage myRetVal = null;
if (myImageFile != null)
{
BitmapImage image = new BitmapImage();
using (FileStream stream = File.OpenRead(myImageFile))
{
image.BeginInit();
image.CacheOption = BitmapCacheOption.OnLoad;
image.StreamSource = stream;
image.EndInit();
}
myRetVal = image;
}
return myRetVal;
}
Related
I am trying to athis listview that shows images in a folder
public void LoadData()
{
var imgList = Directory.GetFiles(directoryPath, "*.jpg",
SearchOption.AllDirectories);
myListView.ItemsSource = imgList;
}
The ListView ItemTemplate is an Image and a button to delete the Image
The click event of the button is handled as follows
private void DeleteImg(object sender, RoutedEventArgs e)
{
string delImg = (string)(sender as Button).DataContext;
File.Delete(delImg);
LoadData();
}
I get the following error when I try to delete an image
System.IO.IOException: 'The process cannot access the file 'xyz' because it is being used by another process.'
xyz is the file name obviously
I think it's because the image is open in ListView
Any ideas about how I can close the file before deleting it
What I have tried:
I tried
myListView.ItemsSource = null;
myListView.Items.Clear();
before delete
also:
System.GC.Collect();
System.GC.WaitForPendingFinalizers();
I found this and it works;
public void LoadData()
{
var imgList = Directory.GetFiles(directoryPath, "*.jpg", SearchOption.AllDirectories);
myListView.ItemsSource = imgList;
foreach (string f in imgList)
{
var bi = new BitmapImage();
bi.BeginInit();
bi.CacheOption = BitmapCacheOption.OnLoad;
bi.UriSource = new Uri(f);
imgLister.Add(bi);
bi.EndInit();
bi.Freeze();
}
}
Freeze() methode Frees up memory, and allows file to be deleted
I met one problem when I tried to copy file.
Error description
I worked with DataGridView and PictureBox.
Deguber of VS 2015 stopped me at
FileStream fs = File.Open(file, FileMode.Open);
in function CopyFile. I cant understand what's wrong i did.
This is some code (C#, .NET 4.5.1) from main form:
static string CopyFile(string file, string to)
{
FileInfo fileInfo = new FileInfo(file);
byte tmp = 0;
string temp = to + "\\" + fileInfo.Name;
FileStream newFile = File.Open(temp, FileMode.Create);
try
{
FileStream fs = File.Open(file, FileMode.Open);
for (int i = 0; i < fileInfo.Length; i++)
{
tmp = (byte)fs.ReadByte();
newFile.WriteByte(tmp);
}
fs.Close();
}
catch (FileNotFoundException ex)
{
MessageBox.Show("Не вдалося найти файл.");
}
newFile.Close();
return temp;
}
private void WriteNewUserToFile(User item, string pathToFile)
{
StreamWriter sw = new StreamWriter(File.Open(#pathToFile, FileMode.Append, FileAccess.Write));
sw.WriteLine(string.Format("{0}, {1}, {2}, {3}, {4}, {5}",
item.Id,
item.Image,
item.FirstName,
item.LastName,
item.Email,
item.Phone));
sw.Close();
}
private void btnAddUser_Click(object sender, EventArgs e)
{
AddUserForm dlg = new AddUserForm();
if (dlg.ShowDialog() == DialogResult.OK)
{
User item = dlg.NewUser;
item.Image = CopyFile(item.Image, "images");
WriteNewUserToFile(item, "data/users.dat");
users.Add(item);
//this.AddNewDataGridRow(item);
}
}
And some code of AddNewUserForm:
public User NewUser
{
get { return newUser; }
set { newUser = value; }
}
private void btnImage_Click(object sender, EventArgs e)
{
OpenFileDialog dlg = new OpenFileDialog();
if (dlg.ShowDialog() == DialogResult.OK)
{
txtImage.Text = dlg.FileName;
try
{
picboxImage.Image = Image.FromFile(txtImage.Text);
}
catch
{
picboxImage.Image = Image.FromFile(#"images\NoImg.bmp");
}
}
}
private void btnApply_Click(object sender, EventArgs e)
{
NewUser = new User
{
Id = Convert.ToInt32(txtId.Text),
LastName = txtLastName.Text,
FirstName = txtFirstName.Text,
Email = txtEmail.Text,
Phone = txtPhone.Text,
Image = txtImage.Text
};
this.DialogResult = DialogResult.OK;
}
If somebody need all project/code, click here (download VS project).
When you set the Image for the PictureBox using the following code, the call keeps the file handle open. So when you try to open the file again you encounter the exception.
picboxImage.Image = Image.FromFile(txtImage.Text);
According to this accepted answer, when the file handle is closed is unpredictable, in some cases, the handle won't be closed even if you explicitly close the Image.
So you may use the technique in that answer like this, to ensure the file handle is closed properly.
picboxImage.Image = Image.FromStream(new MemoryStream(File.ReadAllBytes(txtImage.Text)));
I am writing a winform application in C# to open an image and overlay another image on top of it.
The bottom image is a .jpg and the top one is a .bmp converted from .svg. The .jpg and the .svg are the only ones I want to keep in the folder. The .bmp works as a temp.
I was using the following code to overlay the images. But I am having trouble to delete the temp .bmp as it is used by another process. I think it is this combine code still have access to the last .bmp file.
Could anyone help me on this? Thanks.
private void buttonSearch_Click(object sender, EventArgs e)
{
FailInfo.Text = "";
deletebmp(strFolderPath);
...
// Check if the specified front image exists. Yes, show the file name and convert SVG to BMP. No, show the error msg.
if (File.Exists(strFilePathF))
{
labelFront.Text = strFileNameF;
var svgConvert = SvgDocument.Open(svgFilePathF);
svgConvert.Draw().Save(bmpFilePathF);
pictureBoxFront.Image = Image.FromFile(strFilePathF);
}
else
{
labelFront.Text = "Couldn't find the file!";
pictureBoxFront.Image = null;
}
// Check if the specified back image exists. Yes, show the file name and convert SVG to BMP. No, show the error msg.
if (File.Exists(strFilePathBF))
{
labelBack.Text = strFileNameBF;
strFilePathB = strFilePathBF;
pictureBoxBack.Image = Image.FromFile(strFilePathB);
labelResult.Text = "FAIL";
labelResult.BackColor = Color.FromArgb(255, 0, 0);
var svgConvert = SvgDocument.Open(svgFilePathBF);
bmpFilePathB = strFolderPath + strFileNameBF + ".bmp";
svgConvert.Draw().Save(bmpFilePathB);
svgFilePathB = svgFilePathBF;
inspectionres(svgFilePathB);
labelreason.Visible = true;
}
else if (File.Exists(strFilePathBP))
{
labelBack.Text = strFileNameBP;
strFilePathB = strFilePathBP;
pictureBoxBack.Image = Image.FromFile(strFilePathB);
labelResult.Text = "PASS";
labelResult.BackColor = Color.FromArgb(0, 255, 0);
var svgConvert = SvgDocument.Open(svgFilePathBP);
bmpFilePathB = strFolderPath + strFileNameBP + ".bmp";
svgConvert.Draw().Save(bmpFilePathB);
svgFilePathB = svgFilePathBP;
inspectionres(svgFilePathB);
labelreason.Visible = false;
}
else
{
labelBack.Text = "Couldn't find the file!";
pictureBoxBack.Image = null;
labelResult.Text = "ERROR";
labelResult.BackColor = Color.FromArgb(0, 255, 255);
labelreason.Visible = false;
}
}
//
// Overlay the SVG file on top of the JPEG file
//
private Bitmap Combine(string jpegFile, string bmpFile)
{
Image image1 = Image.FromFile(jpegFile);
Image image2 = Image.FromFile(bmpFile);
Bitmap temp = new Bitmap(image1.Width, image1.Height);
using (Graphics g = Graphics.FromImage(temp))
{
g.DrawImageUnscaled(image1, 0, 0);
g.DrawImageUnscaled(image2, 0, 0);
}
return temp;
}
//
// Show the overlaid graphic in the picturebox
//
private void checkBoxOverlay_CheckedChanged(object sender, EventArgs e)
{
try
{
if (FindFront)
if (checkBoxOverlay.Checked)
pictureBoxFront.Image = Combine(strFilePathF, bmpFilePathF);
else
pictureBoxFront.Image = Image.FromFile(strFilePathF);
else
pictureBoxFront.Image = null;
if (FindBack)
if (checkBoxOverlay.Checked)
pictureBoxBack.Image = Combine(strFilePathB, bmpFilePathB);
else
pictureBoxBack.Image = Image.FromFile(strFilePathB);
else
pictureBoxBack.Image = null;
}
catch (Exception ex)
{
MessageBox.Show("Error loading image" + ex.Message);
}
}
//
// Option of changing the image folder
//
private void buttonPath_Click(object sender, EventArgs e)
{
FolderBrowserDialog FolderBrowserDialog1 = new FolderBrowserDialog();
if (FolderBrowserDialog1.ShowDialog() == DialogResult.OK)
{
strFolderPath = FolderBrowserDialog1.SelectedPath + "\\";
}
}
//
// Pull the inspection result info from the SVG file
//
private void inspectionres(string filename)
{
XDocument document = XDocument.Load(filename);
XElement svg_Element = document.Root;
string sb = null;
var faillist = (from svg_path in svg_Element.Descendants("{http://www.w3.org/2000/svg}text") select svg_path).ToList();
foreach (var item in faillist)
{
sb += item.ToString();
}
}
//
// Delete all the .bmp files generated from .svg files
//
private void deletebmp(string path)
{
// Unload the images from the picturebox if applicable
pictureBoxFront.Image = null;
pictureBoxBack.Image = null;
string[] files = Directory.GetFiles(path, "*.bmp");
for (int i = 0; i < files.Length; i ++ )
File.Delete(files[i]);
}
}
Image implements IDisposable, so simply setting the pictureBox.Image property to null will not release resources (in your case, the file). Your Combine method also leaves the images open. You have to call Dispose before attempting to delete the file:
Image image1 = Image.FromFile(path1);
File.Delete(path1); // error - file is locked
Image image2 = Image.FromFile(path2);
image2.Dispose();
File.Delete(path2); // works
An alternative approach (and I assume you're using WinForms here, in WPF it's a little different) would be to load the bitmap from the file manually (using FromStream). Then, you can close the stream immediately and delete the file:
Image image;
using (Stream stream = File.OpenRead(path))
{
image = System.Drawing.Image.FromStream(stream);
}
pictureBox.Image = image;
File.Delete("e:\\temp\\copy1.png"); //works
Vesan's answer didn't helped me so I found an different solution.
So I can safe/open an image and if I want instantly delete the image.
i used it for my dataGridView_SelectionChanged:
private void dataGridViewAnzeige_SelectionChanged(object sender, EventArgs e)
{
var imageAsByteArray = File.ReadAllBytes(path);
pictureBox1.Image = byteArrayToImage(imageAsByteArray);
}
public Image byteArrayToImage(byte[] byteArrayIn)
{
MemoryStream ms = new MemoryStream(byteArrayIn);
Image returnImage = Image.FromStream(ms);
return returnImage;
}
All above answers are perfectly fine, but I've got a different approach.
Using Image abstract class, you will not get quite a lot of options for manipulating and resizing image.
Rather you do as follows:-
Bitmap img = new Bitmap(item);
img.SetResolution(100, 100);
Image imgNew = Image.FromHbitmap(img.GetHbitmap());
pictureBox1.Image = imgNew;
img.Dispose();
I know how to pass data through
NavigationService.Navigate(new Uri("/Second.xaml?msg=mesage", UriKind.Relative));
The question is, how can I pass an image selected from the library to another page?
To select an image, I use the PhotoChooserTask and in the event where it is completed I have this:
private void photoChooserTask_Completed(object sender, PhotoResult e)
{
if (e.ChosenPhoto != null)
{
BitmapImage image = new BitmapImage();
image.SetSource(e.ChosenPhoto);
this.img.Source = image;
}
}
How can I send the chosen photo to another page? Do I have to write it in a buffer, set a global variable or 'save' it in Isolated storage?
You could save your picture in IsolatedStorage first, pass the file path to another page as a string parameter, load the picture out when you need it.
Use PhoneApplicationService to save the image into State, load it when you need it.
Sample for saving into IsolatedStorage:
public static void SaveStreamToStorage(Stream imgStream, string fileName)
{
using (IsolatedStorageFile iso_storage = IsolatedStorageFile.GetUserStoreForApplication())
{
//Structure the path you want for your file.
string filePath = GetImageStorePathByFileName(fileName);
//Constants.S_STORE_PATH is the path I want to store my picture.
if (!iso_storage.DirectoryExists(Constants.S_STORE_PATH))
{
iso_storage.CreateDirectory(Constants.S_STORE_PATH);
}
//I skip the process when I find the same file.
if (iso_storage.FileExists(filePath))
{
return;
}
try
{
if (imgStream.Length > 0)
{
using (IsolatedStorageFileStream isostream = iso_storage.CreateFile(filePath))
{
BitmapImage bitmap = new BitmapImage();
bitmap.SetSource(imgStream);
WriteableBitmap wb = new WriteableBitmap(bitmap);
// Encode WriteableBitmap object to a JPEG stream.
Extensions.SaveJpeg(wb, isostream, wb.PixelWidth, wb.PixelHeight, 0, 100);
isostream.Close();
bitmap.UriSource = null;
bitmap = null;
wb = null;
}
}
}
catch(Exception e)
{
if (iso_storage.FileExists(filePath))
iso_storage.DeleteFile(filePath);
throw e;
}
}
}
Sample for reading picture from IsolatedStorage:
public static BitmapImage LoadImageFromIsolatedStorage(string imgName)
{
try
{
var bitmapImage = new BitmapImage();
//bitmapImage.CreateOptions = BitmapCreateOptions.DelayCreation;
using (var iso = IsolatedStorageFile.GetUserStoreForApplication())
{
//Check if file exists to prevent exception when trying to access the file.
if (!iso.FileExists(GetImageStorePathByFileName(imgName)))
{
return null;
}
//Since I store my picture under a folder structure, I wrote a method GetImageStorePathByFileName(string fileName) to get the correct path.
using (var stream = iso.OpenFile(GetImageStorePathByFileName(imgName), FileMode.Open, FileAccess.Read))
{
bitmapImage.SetSource(stream);
}
}
//Return the picture as a bitmapImage
return bitmapImage;
}
catch (Exception e)
{
// handle the exception
Debug.WriteLine(e.Message);
}
return null;
}
You could use a variable defined in your app.xaml.cs and call it from your other page like so (don't mind the variable names, just a code sample I use for language support):
private LanguageSingleton LanguageInstance
{
get
{
return (App.Current as App).Language;
}
}
Here is how you could define that variable:
public LanguageSingleton Language { get; set; }
I'm sure there are more ways in doing this but this is one solution.
I'd like to display the contents(.jpg files) from a local directory in an Image control. The images has to be replaced with a 5 sec delay.
DirectoryInfo dir = new DirectoryInfo(#"D:\somedir");
FileInfo[] files = dir.GetFiles();
foreach (var item in files)
{
imgBox.Source = (ImageSource)new ImageSourceConverter().ConvertFromString(item.FullName);
}
Load images into a memory, then user a background thread to rotate them with the defined delay. Dispatcher call is required to access the UI control from a background thread.
List<Image> images;
void GetImagesIntoAList()
{
List<Image> images = new List<Image>();
DirectoryInfo dir = new DirectoryInfo(#"D:\somedir");
FileInfo[] files = dir.GetFiles();
foreach (var item in files)
{
FileStream stream = new FileStream(item.FullName, FileMode.Open, FileAccess.Read);
Image i = new Image();
BitmapImage src = new BitmapImage();
src.BeginInit();
src.StreamSource = stream;
src.EndInit();
i.Source = src;
images.Add(i);
}
Thread rotator = new Thread(rotate);
rotator.Start();
}
void rotate()
{
foreach(var img in images)
{
Dispatcher.BeginInvoke( () =>
{
nameOfImageControlOnAWindow.Source = img;
}
);
Thread.Sleep(5000);
}
}