1

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

Community
  • 1
  • 1
Hank
  • 475
  • 9
  • 28
  • Use `Image.FromStream` [this way](http://stackoverflow.com/a/38830222/3110834). – Reza Aghaei Nov 07 '16 at 20:59
  • From first glance, that looks like it might be what I was looking for. I'll try it out. – Hank Nov 07 '16 at 21:01
  • 1
    Also you don't need to dispose a form after you closed it. If it's a dialog form, use it in a using block, otherwise you don't need to use this.Dispose at all. You may want to take a look at this post [Do I need to Dispose a Form after the Form got Closed?](http://stackoverflow.com/a/39501121/3110834) – Reza Aghaei Nov 07 '16 at 21:04
  • Worked perfectly. I also updated the `ShowDialog` call to dispose properly with a using block – Hank Nov 07 '16 at 21:13
  • FYI, the fact that you were getting that error means that your `Image` objects aren't getting disposed (at least until the GC decides to bother with them). This is effectively a leak of GDI objects, which is something you want to avoid even if it no longer results in an exception. I recommend explicitly disposing of all the images when you dispose the Form. – adv12 Nov 07 '16 at 21:16
  • How do I go about disposing of the images without losing the display of them in the `FlowLayoutPanel`? At what point should I dispose them? – Hank Nov 07 '16 at 21:17
  • Dispose them in the form's `Dispose()` method, and then make sure you dispose of the form when you're done with it (e.g. with a `using` block around the `ShowDialog` call as suggested by @RezaAghaei). – adv12 Nov 07 '16 at 21:20
  • @adv12 My main issue with disposing of the images is exactly how to access them from the Dispose class. The are added to the `FlowLayoutPanel` as controls but I don't have a way to call each specific image since the images themselves were created in a `foreach`. Can I dispose of every image in the `FlowLayoutPanel` in some way by tracing through it and disposing each one? Is that what you are suggesting? – Hank Nov 07 '16 at 21:26
  • @Hank, yes, that would work, or at creation time you could add them to a list that you keep handy for easy disposal later. Lots of ways to skin the cat-- – adv12 Nov 07 '16 at 21:30
  • @adv12 Thanks for the input. I'm fairly new to C# and application development so I know almost nothing about disposing. I'm disposing of the `Image` and not the `PictreBox`, right? – Hank Nov 07 '16 at 21:34
  • Yes, the `Image`. The `PictureBox` itself will get disposed as part of the `Form`'s cleanup. – adv12 Nov 07 '16 at 21:35

1 Answers1

1

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();
}
NobodyNada
  • 7,529
  • 6
  • 44
  • 51
Hank
  • 475
  • 9
  • 28