Search code examples
c#winformsfile-iodispose

Disposing of image when using PictureBox


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


Solution

  • 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();
    }