Disposing of image when using PictureBox - c#

I'm working with a Winforms program that was written a while ago. I've come across some issues with it and I'm trying to optimize the way it handles some things but I'm running across some issues with disposing.
Below is what is currently implementing.
First, It starts with going through the files in the Pictures folder and copies them into a preview folder.
foreach (string s in files)
{
fileName = System.IO.Path.GetFileName(s);
destFile = System.IO.Path.Combine(path, fileName);
File.Copy(s, destFile, true);
}
Next, it opens a form via ShowDialog:
frmPreview frm = new frmPreview(FileNameArray, lblParcel.Text);
frm.ShowDialog();
Next, it goes to the Preview form and gets to this code:
try {
FlowLayoutPanel imagePanel = new FlowLayoutPanel();
if (System.IO.Directory.Exists(path))
{
folder = new DirectoryInfo(path);
foreach (FileInfo files in folder.GetFiles())
{
System.Diagnostics.Debug.Print(files.Extension);
if ((string.Equals(files.Extension, ".jpg", StringComparison.OrdinalIgnoreCase)) || (string.Equals(files.Extension, ".gif", StringComparison.OrdinalIgnoreCase)) || (string.Equals(files.Extension, ".png", StringComparison.OrdinalIgnoreCase)))
{
PictureBox image = new PictureBox();
image.Image = Image.FromFile(files.FullName);
image.SizeMode = PictureBoxSizeMode.Zoom;
image.Size = this.Size;
imagePanel.Controls.Add(image);
}
}
}
this.Controls.Add(imagePanel);
System.Threading.Thread.Sleep(0);
return;
}
catch
{
}
The code above basically takes all the photos, creates a PictureBox with each one, and adds the PictureBox to the FlowLayoutPanel to display in a window for previewing. Problem with this is that it does not dispose properly and gets caught up after trying to visit this preview window for the 3rd time (closing the window and opening it a second time works fine but creates a second process).
Lastly, It implements the following when the form closes.
private void frmPreview_FormClosed(object sender, FormClosedEventArgs e)
{
this.Dispose();
this.Close();
}
The error happens on the 3rd time the preview window is called, when the program goes through the foreach statement posted at the top.
The full line where it catches is:
File.Copy(s, destFile, true);
The process cannot access the file 'C:\Users\username\Pictures\Preview\image.jpg' because it is being used by another process.
I'm 99.9% sure it's because of the PictureBox and FlowLayoutPanel but I can't figure out what to do to fix it. I'd like to change as little as possible since this isn't my program and it will soon be rewritten completely. I mainly just need to temporarily fix the issue until we finish the big picture where this whole program will get scrapped.
I've found a couple posts that seem to be similar issues but none of the fixes have changed anything at all. Below are all the posts I looked into and trying implementing unsuccessfully:
file-copy-the-process-cannot-access-the-file
file-is-being-used-by-another-process
dispose-of-a-picturebox

Issue fixed after implementing the recommendation of #RezaAghaei. Changed the Preview form to this:
foreach (FileInfo files in folder.GetFiles())
{
System.Diagnostics.Debug.Print(files.Extension);
if ((string.Equals(files.Extension, ".jpg", StringComparison.OrdinalIgnoreCase)) || (string.Equals(files.Extension, ".gif", StringComparison.OrdinalIgnoreCase)) || (string.Equals(files.Extension, ".png", StringComparison.OrdinalIgnoreCase)))
{
using (var stream = new FileStream(files.FullName, FileMode.Open))
{
PictureBox image = new PictureBox();
image.Image = Image.FromStream(stream);
image.SizeMode = PictureBoxSizeMode.Zoom;
image.Size = this.Size;
imagePanel.Controls.Add(image);
}
}
}
I also improved the efficiency of the ShowDialog call by implementing a using block:
using (frmPreviewPhotos frm = new frmPreviewPhotos(NEWphotoFileNameArray, lblParcel.Text))
{
frm.ShowDialog();
}

Related

How do I cache an image

Every time the user presses a button my application gets the image from the web, but this slow since the image isn't getting cached. Any way I can cache the image so it doesn't download it over and over making things faster.
This is my code so you know how my application works.
if (listBox1.SelectedIndex == 0)
{
richTextBox1.Text = "Explore any game with Dex";
pictureBox1.Load("THE LINK ");
ProtoxRe.lol = 0;
}
else if ((listBox1.SelectedIndex == 1))
{
richTextBox1.Text = "SOME TEXT";
pictureBox1.Load("LINK");
ProtoxRe.lol = 1;
}
There's probably more sophisticated methods, and this assumes your image is not expected to change, but you can do something like this:
//Image class is from this library
using System.Drawing;
//The scope of this will depend on where you need to access it from
Image img;
private Image GetImage()
{
if (img == null)
{
img = GetImageFromHttp();
}
return img;
}
This is very basic, and has a number of flaws; you're not caching it between sessions so every application start will require the http get, and you're not checking if the image on the web is different, but it's a starting point.

How to wait until a service finishes opening a window before processing it

I have code that opens a window using a service in my WPF C# application. I want to create a print-screen image file of that window once it is opened. The problem is that my code is creating an image file that has a print-screen of the area BEFORE the window is actually opened. I tried to create a delegate on the ViewOpened event, so that it waits until the window is actually opened before I generate the print screen. But I still get an image of the area BEFORE the window opens. How can I fix this?
Here is my code:
private void M_printScreenButton_Click(object sender, EventArgs e)
{
var allViews = SystemConfigurationComponent.GetAllViews();
foreach (var view in allViews)
{
// TODO: Add condition to select which views to process. Maybe add a boolean field to View table.
if (string.Equals(view.Uri, #"gtcs\gcs\mmc_userpreferences", StringComparison.InvariantCultureIgnoreCase))
{
ViewsIntegrationService.OpenMimic(view.Uri, "", "", 2, ThisMimic.Context, null);
ViewsIntegrationService.ViewOpened += delegate
{
using (Bitmap bitmap = new Bitmap(view.DefaultWidth, view.DefaultHeight))
{
using (Graphics g = Graphics.FromImage(bitmap))
{
g.CopyFromScreen(new Point(view.DefaultLeft, view.DefaultTop), Point.Empty,
new Size() {Width = view.DefaultWidth, Height = view.DefaultHeight});
}
bitmap.Save(#"C:\\temp\\GTCS_PrintScreens\\test.jpg", ImageFormat.Jpeg);
//bitmap.Save(view.WindowTitle.Replace(" ", string.Empty).Replace("'", string.Empty) + ".jpg", ImageFormat.Jpeg);
}
//ViewsIntegrationService.CloseView(new AlstomGuiMimicImpl(m_mimicsManager, ThisMimic));
};
}
}
}
It appears you are first opening the view then adding the event handler after it is opened.
Try:
ViewsIntegrationService.ViewOpened += delegate ...
Then
ViewsIntegrationService.OpenMimic(...
Additionally, the Loaded event is probably a better event to use for the purpose of taking a screen shot.

SaveAs and delete same time c# [duplicate]

I am trying to delete local copy(on the computer) of an image file once uploaded using file dialog. It throws The process cannot access the file 'C:\Documents and Settings\username\My Documents\My Pictures\1220.bmp' because it is being used by another process.
private void _btnImportPhoto_Click(object sender, RoutedEventArgs e)
{
//user clicked import/change photo, open file dialog to browse for photo
System.Windows.Forms.OpenFileDialog fileDialog = new System.Windows.Forms.OpenFileDialog();
fileDialog.Multiselect = false;
fileDialog.Filter = ResourceFile.PhotoFileTypes;
if (fileDialog.ShowDialog() == System.Windows.Forms.DialogResult.OK)
{
FilePath = fileDialog.FileName;
FilePathCopy = fileDialog.FileName;
string safeFilePath = fileDialog.SafeFileName;
Bitmap bitmap = new Bitmap(FilePath);
CurrentPhoto = bitmap;
Bitmap bitmap1 = new Bitmap(FilePathCopy); //A copy to save when if delete local copy is chosen
m_PhotoCopy = bitmap1;
FileSystem.DeleteFile(FilePath);
}
_btnSave.IsEnabled = _btnCancel.IsEnabled = true;
}
}
Please let me know how to work around this issue.
Thanks.
you need to dispose of the bitmap object try doing this. As this will dispose of the bitmap object as soon as it leaves the using context { }
using (Bitmap bitmap1 = new Bitmap(FilePathCopy))
{
//do all bitmap stuff in here
}
First I see
Bitmap bitmap = new Bitmap(FilePath);
CurrentPhoto = bitmap;
Where CurrentPhoto I presume some global variable that you want hold.
This, instead throws and exception:
FileSystem.DeleteFile(FilePath);
Cause image file at FilePath is actually CurrentPhoto. What you can do.
1) If use of CurrentPhoto has any sense inside this function, do what you want to do inside this function, and after dispose CurrentPhoto object, even in a way like #Bobby suggests (using block)
2) If you want to have it by the way, you can try to use Bitmap's Clone
method like this:
CurrentPhoto = bitmap.Clone();
and after call your:
bitmap.Dispose();
FileSystem.DeleteFile(FilePath);
Should work.
Try this...
http://www.lockergnome.com/blade/2006/11/28/windows-error-message-error-deleting-file-or-folder/
It will allow you to delete files or folder.

Dynamic WPF Image Loading Issue

I have an issue that I think has not been covered in the multitude of other WPF image loading issues. I am scanning in several images and passing them to a "Preview Page". The preview page takes the image thumbnails and displays what a printout would look like via a generated bitmap.
The weird thing to me is, it will work fine if I run the program the first time. Upon reaching the end of the process and hitting "start over", the preview will return blank. I am creating the BitmapImage in a method that saves the bitmap as a random file name so I do not believe theres a lock on the file the second time around. Also, if I go to look at the temporary file created through explorer, it is drawn correctly so I know the appropriate data is getting to it.
Finally, when I navigate away from this page, I am clearing necessary data. I'm really perplexed and any help would be appreciated.
//Constructor
public Receipt_Form() {
InitializeComponent();
printData = new List<Object>();
this.Loaded += new RoutedEventHandler(MainWindow_Loaded);
}
void MainWindow_Loaded(object sender, RoutedEventArgs e) {
// populates global variable fileName
var task = System.Threading.Tasks.Task.Factory.StartNew(() => outputToBitmap()); task.ContinueWith(t => setImage(fileName),
System.Threading.Tasks.TaskScheduler.FromCurrentSynchronizationContext());
// I started the image creation in a separate thread because I
// thought it may be blocking the UI thread, but it didn't matter
}
private void setImage(string imageURI) {
BitmapImage image;
using (FileStream stream = File.OpenRead(imageURI)) {
image = new BitmapImage();
image.BeginInit();
image.StreamSource = stream;
image.CacheOption = BitmapCacheOption.OnLoad;
image.EndInit();
}
receiptPreview.Source = image;
//this works the first iteration but not the second, though the temp file is created successfully
}
Found the issue - the Modern UI container was getting cleared when transitioning off the page.

.NET PictureBox - how to be sure that the resource is released

I have an OpenFileDialog and PictureBox in user control. To understand the problem better I'll explain in few words how this user control works. The user can select an image to be opened for the form. The name of this image is saved in a DataBase and the file for the image is copied in a default location. When there is some image saved in the database it is load in the picturebox when the form with the picturebox control is loaded. If the user select another image and want to save the form with the new image I have a method that takes care to delete the old image file from my default location and that is where the problem occurs.
When I have loaded image and try to save new one, sometimes (very rare in fact) I get an error that The resource is being used by another process.. I can paste the exact error if needed. I think that the problem is caused from the picturebox and the way it deals with images.
Here is my code:
if (openFileDialog1.ShowDialog() == DialogResult.OK)
{
try
{
if (MyImage != null)
{
MyImage.Dispose();
}
selectedFile = openFileDialog1.FileName;
selectedFileName = openFileDialog1.SafeFileName;
MyImage = new Bitmap(openFileDialog1.FileName);
pictureBox1.Image = (Image)MyImage;
int imageWidth = pictureBox1.Image.Width;
int picBoxWidth = pictureBox1.Width;
if (imageWidth != 0 && picBoxWidth > imageWidth)
{
pictureBox1.Width = imageWidth;
}
else
{
pictureBox1.Width = defaultPicBoxWidth;
}
}
catch (Exception ex)
{
logger.Error(ex.ToString());
MessageBox.Show("Error loading image!", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
and my delete method:
public void DeleteImage(AppConfig imageInfo, string imageName)
{
string imgPath = imageInfo.ConfigValue.ToString();
try
{
File.Delete(imgPath + "\\" + imageName);
}
catch (Exception ex)
{
MessageBox.Show(ex.ToString());
}
}
I thought that :
if (MyImage != null)
{
MyImage.Dispose();
}
will deal with this problem but still sometimes it occurs. And because it's not everytime it's even more critical to deal with it because at some point I may decide that I have solved it but in fact to be just lucky for some time.
MyImage = new Bitmap(openFileDialog1.FileName);
pictureBox1.Image = (Image)MyImage;
Yes, that code puts a lock on the file. The lock is produced by a memory mapped file object that GDI+ creates to efficiently map the pixel data of the file into memory without having to allocate space in the paging file. You will not be able to delete the file as long as the image is displayed in the picture box and not disposed, the lock prevents that. You will have to dispose the image and set the Image property back to null before you can delete the file.
You can prevent the file from getting locked by making an in-memory copy of the image:
using (var temp = new Bitmap(openFileDialog1.FileName)) {
pictureBox1.Image = new Bitmap(temp);
}
It is not as efficient of course, to be avoided if the image is large. And do beware that another process may in fact have a similar lock on the file. Nothing you can do about that.
A major difficulty with things like PictureBox is that because a PictureBox has no way of knowing whether it is the only user of an image, it consequently has no way of knowing whether it should dispose of that image when it no longer needs it.
Consequently, whatever code owns a picture box must also take ownership of the image associated therewith. There are three approaches I could suggest for doing so:
Create a control derived from PictureBox which documents itself as assuming ownership of any image given to it. Such a control should probably replace the image property with a SetImageWithOwnership method (with the semantics that once an image is passed to the PictureOwningBox, the box will be expected to "own" it, and will dispose it either when the box is Disposed or when a different image is given to the box).
Attach event handlers to a PictureBox to handle the scenarios where either the box is destroyed or a different image is assigned to it.
Have any code which would cause the PictureBox to be disposed or have a different image loaded, also dispose the Image that had been assigned to it.
While there may be cases where it would be appropriate to call GC.Collect and let the garbage-collector take care of things, such an approach is generally unsound.
try that:
using(Bitmap MyImage = new Bitmap(openFileDialog1.FileName))
{
pictureBox1.Image = (Image)MyImage;
int imageWidth = pictureBox1.Image.Width;
int picBoxWidth = pictureBox1.Width;
if (imageWidth != 0 && picBoxWidth > imageWidth)
{
pictureBox1.Width = imageWidth;
}
else
{
pictureBox1.Width = defaultPicBoxWidth;
}
}
I've had problems like this before, and one way that I've found to make sure that the resource is released, even after Dispose(), which really only marks the object for removal by the garbage collector, is by using GC.Collect(). I'm sure that there is a cleaner way to handle the resource disposal, but the time that it takes the GC.Collect() to run shouldn't hinder your program.

Categories

Resources