1

I am dynamically creating controls on top of TabPages that are, of course, on a TabControl. I want to be able to dynamically dispose of these, too. How can I do that? Calling Clear on the TabControl's TabPages collection actually disposes the TabPages themselves, which defeats the purpose of "starting over" with new dynamically created controls on those pages.

Calling tabPageBla.Controls.Clear() is close to what I want, but it also disposes a container control that I have on each TabPage (FlowControlLayout) that I need to keep.

Is there a straightforward way to accomplish this (dispose only the dynamically-created controls, but not any of the others)?

UPDATE

Will this work - will grandchildren of TabControl1 also be found (children of the tab pages)?:

List<Control> ctrls = new List<Control>();
ctrls.AddRange(tabControl1.Controls);
foreach (var ctrl in ctrls)
{
    // Controls named "panelRowBla", "richTextBoxBla", and "pictureBoxBla" need to be retained
    string ctrlName = ctrl.Name;
    if ((ctrlName.Contains("panelRow")) ||
        (ctrlName.Contains("richTextBox")) ||
        (ctrlName.Contains("pictureBox")))
    {
        continue;
    }
}

UPDATE 2

That's odd; I could have sworn this was just compiling, but now I'm getting "Argument 1: cannot convert from 'System.Windows.Forms.Control.ControlCollection' to System.Collections.Generic.IEnumerable'"

(on the "AddRange()" line).

B. Clay Shannon-B. Crow Raven
  • 8,547
  • 144
  • 472
  • 862

2 Answers2

4

If you remove the control from the parent, you can explicitly Dispose it, ie:

 parent.Controls.Remove(theControl);
 ((IDisposable)theControl).Dispose();

Given your edits, you should be able to do:

var toRemove = tabControls1
      .Controls
      .Cast<Control>()
      .Where(c => !(c.Name.Contains("panelRow") || c.Name.Contains("richTextBox") || c.Name.Contains("pictureBox")))
      .ToList();

foreach(var c in toRemove)
{
     tabControls1.Controls.Remove(c);
     ((IDisposable)c).Dispose();
}

This will remove all of the controls from the tabControl1 which don't match your filter.

Reed Copsey
  • 554,122
  • 78
  • 1,158
  • 1,373
1

There is no way to tell if the control was created in the designer or dynamically. But what you can do, is to create a list of all the controls, which exist at the very start (controls which were created in the designer).

Create a new list to the class' root, to hold all the "original" controls:

List<Control> DesignerControls;

Use this modified PsychoCoder's method (https://stackoverflow.com/a/3426721/2538037) to list all controls (including controls inside controls):

public IEnumerable<Control> GetAll(Control control)
{
   var controls = control.Controls.Cast<Control>();

   return controls.SelectMany(ctrl => GetAll(ctrl))
                  .Concat(controls);
}

Before you have added any controls dynamically, run this line (for example in the constructor):

DesignerControls = GetAll(this).ToList();

Then add this static method for disposing the dynamically created controls:

public static bool DisposeDynamicObjects(List<Control> NonDynamicControls, List<Control> ControlsToDispose)
{
   try
   {
      for (int i = 0; i < ControlsToDispose.Count; i++)
      {
         if (ControlsToDispose[i] != null &&
             !ControlsToDispose[i].IsDisposed &&
             !NonDynamicControls.Contains(ControlsToDispose[i]))
         {
            ControlsToDispose[i].Dispose();
            --i;
         }
      }
      return true;
   }
   catch (Exception ex) { MessageBox.Show(ex.Message); return false; }
}

When you want to dispose dynamically created controls, use this:

DisposeDynamicObjects(DesignerControls, GetAll(tabControl1).ToList());

The code above disposes all dynamically created controls inside tabControl1.Controls. You can replace the tabControl1 with other controls, like this for example, so all dynamically created controls are deleted from the whole form.

Community
  • 1
  • 1
W0lfw00ds
  • 2,018
  • 14
  • 23
  • Thanks; I changed "tabPage1" to "tabControl1" (hoping that was what you meant - but that seems to dispose of the controls without removing them from sight - do I need to call Refresh or some such for that? – B. Clay Shannon-B. Crow Raven Jun 03 '14 at 20:38
  • I updated the answer with some new code. Thats a one way to use it. – W0lfw00ds Jun 03 '14 at 21:45
  • In the foreach loop calling DisposeDynamicObjects(), I get, "Argument 2: cannot convert from 'System.Collections.Generic.List' to 'System.Windows.Forms.Control.ControlCollection'" – B. Clay Shannon-B. Crow Raven Jun 03 '14 at 22:00
  • 1
    I updated the `DisposeDynamicObjects()`-method's parameter types. Please copy and use this new version. Also notice the `.Cast().ToList()`. – W0lfw00ds Jun 03 '14 at 22:05
  • The last two foreaches above are exactly what I'm using (except that I don't have "Form1" prepended to the call to DisposeDynamicObjects()), and I still get that err msg. – B. Clay Shannon-B. Crow Raven Jun 03 '14 at 22:21
  • Oh, my bad - I see the main method itself changed; compiles now...still doesn't get rid of the dynamic controls, though (which are on FlowLayoutPanels and are images inside PictureBoxes). – B. Clay Shannon-B. Crow Raven Jun 03 '14 at 22:23
  • 1
    You can use recursive method to list All controls inside controls, including GroupBoxes and their controls. Here is one link: http://kon-phum.com/tutors/pascal/programming_cs_getcontrolsonform.html . You could use it like this: `DesignerControls = GetAllControls(tabControl1.Controls);`. And when you wan't to dispose the objects run `Form1.DisposeDynamicObjects(DesignerControls, GetAllControls(tabControl1.Controls));`. I haven't tested out that link's method, but it should work. – W0lfw00ds Jun 03 '14 at 22:38
  • No, the method from that link doesn't compile, either; it says, "Using the generic type 'System.Collections.Generic.IList' requires 1 type arguments" – B. Clay Shannon-B. Crow Raven Jun 03 '14 at 22:44
  • 1
    I rewrote the answer to make it more versatile. I also tested it quickly, and it worked. – W0lfw00ds Jun 03 '14 at 23:32