Search code examples
c#winformscontrolssplitcontainerstatusstrip

How to get a list of all controls on WinForm even those in SplitContainers or Panels


A have several containers on my WinForm like Panel, SpliContainer, StatusStrip... Each of these containers contains basic elements like buttons or textboxs. I need to iterate thru all form controls (even those in Panels, SplitContainers, StatusStrip) to find some control. I try with recursive function

    ListAllControls(Control MainControl)
    {
        foreach (Control control in MainControl.Controls)
        {
            if (control.HasChildren)
            {
                ListAllControls(control);
            }
            else
            { 
                // do something with this control
            }
        }
    }

but I dont get controls that are in containers!?

UPDATE:

I have a Form with SplitContainer, Panel and StatuStrip. In each of these controls I have several child controls like toolStripStatusLabel1 in StatuStrip1. Problem is when I try to find for example control toolStripStatusLabel1 in StatuStrip via function ListAllControls I can't find it!? I don't know any another approach to get all controls from form. Complete code is here:

class Control_Finder
{

    private Control founded_control = null;

    public Control Founded_Control
    {
        get { return founded_control; }
    }

    public void ListAllControls(Control MainControl, string SearchForControl)
    {
        foreach (Control control in MainControl.Controls)
        {
            if (control.HasChildren)
            {
                ListAllControls(control, SearchForControl);
            }
            else
            {
                // check if control has searched name
                if (control.Name == SearchForControl)
                {
                    founded_control = control;
                    return;
                }
            }
        }
    }
} // class

Sample:

Form Me = this;
Control_Finder Test = new Control_Finder();
Test.ListAllControls(Me, "toolStripStatusLabel1");

if (Test.Founded_Control != null)
{
      MessageBox.Show("I found control " + Test.Founded_Control.Name + "!");
}
else
{
      MessageBox.Show("Didn't found! :(");
}

For this sample i get Didn't found :( but if I use StatusStrip1 I get "I found control StatusStrip1!" I hope that the question now more clearly than before.


Solution

  • It would have been better for you to have provided a good, minimal, complete code example that clearly shows your specific scenario. However, based on the information you added to your question, I was able to create what I think is a representative code example. This answer is based on that example.


    As commenter LarsTech notes, the ToolStripStatusLabel does not inherit Control. The most specific base class shared by Control and ToolStripStatusLabel is Component. So at the very least, you've got a big problem in your attempt to return an object of type Control and yet still find an instance of ToolStripStatusLabel. Even if you did find the object, you wouldn't be able to cast it to Control.

    Another issue is that while ToolStrip itself does inherit the Control class, it does not store its children in the Controls property (i.e. in a ControlCollection object). I presume this is because its children aren't Control objects and so could not be stored in a ControlCollection. In any case, this means that as you recurse through the object graph of your form, you have to handle ToolStrip differently than other instances of Control in order to find its children.


    Here is a sample program that demonstrates one approach that would work (see the bottom of this post for the Designer-generated code that goes with this example):

    Form1.cs:

    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }
    
        private void button1_Click(object sender, EventArgs e)
        {
            Component component = FindControl(this.Controls, "toolStripStatusLabel1");
    
            label2.Text = component != null ?
                "Found control named \"" + GetNameForComponent(component) + "\"" :
                "No control was found";
        }
    
        private static string GetNameForComponent(Component component)
        {
            Control control = component as Control;
    
            if (control != null)
            {
                return control.Name;
            }
    
            ToolStripItem item = component as ToolStripStatusLabel;
    
            if (item != null)
            {
                return item.Name;
            }
    
            return "<unknown Component type>";
        }
    
        private Component FindControl(IEnumerable controlCollection, string name)
        {
            foreach (Component component in controlCollection)
            {
                if (GetNameForComponent(component) == name)
                {
                    return component;
                }
    
                IEnumerable childControlCollection = GetChildrenForComponent(component);
    
                if (childControlCollection != null)
                {
                    Component result = FindControl(childControlCollection, name);
    
                    if (result != null)
                    {
                        return result;
                    }
                }
            }
    
            return null;
        }
    
        private static IEnumerable GetChildrenForComponent(Component component)
        {
            ToolStrip toolStrip = component as ToolStrip;
    
            if (toolStrip != null)
            {
                return toolStrip.Items;
            }
    
            Control control = component as Control;
    
            if (control != null)
            {
                return control.HasChildren ? control.Controls : null;
            }
    
            return null;
        }
    }
    

    Because of the disjoint nature of the object inheritance for the objects you are dealing with, some special handling must be done:

    1. Items you find may be instances of Control or not. Instances of Control store their name in the Control.Name property, but obviously instances that are not Control objects won't. They need to be handled specifically by identifying the type where their name property is stored (in this case, ToolStripItem.Name, but do note that there are other non-Control types that inherit Component and each would have to be handled separately). I've added a GetNameForComponent(Component) method to encapsulate this.
    2. Of the items you find which are instances of Control, some will store their children in the Controls collection, while others will not, instead using some other property to reference the collection in which the children are stored. Again, a helper method (in this case, GetChildrenForComponent(Component)) is provided to encapsulate this difference.

    With these two issues addressed, the basic recursive method to search the graph can then be easily written. Please note that I have made some changes to the basic architecture of the method as compared to yours. It is my opinion that implementing this code as a complete class by itself is unnecessary, and in any case having the result stored in a member of that class rather than simply being returned by the method is particularly wrong.

    In my example, I have implemented it simply as a single method that returns the object in question if found.

    Also please note that your implementation would not find any object that is itself a container of objects. I have corrected this in my examples as well.


    One last note: following the above strategy, you would have to add code for each base class of interest. Maybe the above is sufficient for your needs, or maybe you have other container types that hold non-Control objects and which don't inherit ToolStrip. If so, you would have to add additional cases in each of the helper methods as appropriate to those types.

    An alternative to that would be to use reflection to find the class members that contain e.g. the name and children. Assuming the name is always stored in a property called Name, that part would be relatively simple.

    But even in this simple example, using reflection to obtain the children is fairly complicated. Rather than the children always being in a collection object obtained from a property named e.g. Controls, in one case that's the name but in another case the name is Items. One can dig deeper to e.g. determine the type of objects found in the collection, but the code starts to get excessively complicated at that point.

    Given that it's debatable whether it really even makes sense to treat ToolStrip children the same as regular Control children in a form anyway, I would not advise investing much effort in a truly generalized solution. Better to handle individual cases as needed, to remind oneself that what you're doing isn't really such a good idea in the first place. :)


    Form1.Designer.cs:

    partial class Form1
    {
        /// <summary>
        /// Required designer variable.
        /// </summary>
        private System.ComponentModel.IContainer components = null;
    
        /// <summary>
        /// Clean up any resources being used.
        /// </summary>
        /// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
        protected override void Dispose(bool disposing)
        {
            if (disposing && (components != null))
            {
                components.Dispose();
            }
            base.Dispose(disposing);
        }
    
        #region Windows Form Designer generated code
    
        /// <summary>
        /// Required method for Designer support - do not modify
        /// the contents of this method with the code editor.
        /// </summary>
        private void InitializeComponent()
        {
            this.statusStrip1 = new System.Windows.Forms.StatusStrip();
            this.toolStripStatusLabel1 = new System.Windows.Forms.ToolStripStatusLabel();
            this.button1 = new System.Windows.Forms.Button();
            this.label1 = new System.Windows.Forms.Label();
            this.label2 = new System.Windows.Forms.Label();
            this.statusStrip1.SuspendLayout();
            this.SuspendLayout();
            // 
            // statusStrip1
            // 
            this.statusStrip1.ImageScalingSize = new System.Drawing.Size(20, 20);
            this.statusStrip1.Items.AddRange(new System.Windows.Forms.ToolStripItem[] {
            this.toolStripStatusLabel1});
            this.statusStrip1.Location = new System.Drawing.Point(0, 228);
            this.statusStrip1.Name = "statusStrip1";
            this.statusStrip1.Size = new System.Drawing.Size(282, 25);
            this.statusStrip1.TabIndex = 0;
            this.statusStrip1.Text = "statusStrip1";
            // 
            // toolStripStatusLabel1
            // 
            this.toolStripStatusLabel1.Name = "toolStripStatusLabel1";
            this.toolStripStatusLabel1.Size = new System.Drawing.Size(111, 20);
            this.toolStripStatusLabel1.Text = "Strip status text";
            // 
            // button1
            // 
            this.button1.Location = new System.Drawing.Point(12, 12);
            this.button1.Name = "button1";
            this.button1.Size = new System.Drawing.Size(75, 23);
            this.button1.TabIndex = 1;
            this.button1.Text = "button1";
            this.button1.UseVisualStyleBackColor = true;
            this.button1.Click += new System.EventHandler(this.button1_Click);
            // 
            // label1
            // 
            this.label1.AutoSize = true;
            this.label1.Location = new System.Drawing.Point(12, 38);
            this.label1.Name = "label1";
            this.label1.Size = new System.Drawing.Size(52, 17);
            this.label1.TabIndex = 2;
            this.label1.Text = "Result:";
            // 
            // label2
            // 
            this.label2.AutoSize = true;
            this.label2.Location = new System.Drawing.Point(70, 38);
            this.label2.Name = "label2";
            this.label2.Size = new System.Drawing.Size(0, 17);
            this.label2.TabIndex = 3;
            // 
            // Form1
            // 
            this.AutoScaleDimensions = new System.Drawing.SizeF(8F, 16F);
            this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
            this.ClientSize = new System.Drawing.Size(282, 253);
            this.Controls.Add(this.label2);
            this.Controls.Add(this.label1);
            this.Controls.Add(this.button1);
            this.Controls.Add(this.statusStrip1);
            this.Name = "Form1";
            this.Text = "Form1";
            this.statusStrip1.ResumeLayout(false);
            this.statusStrip1.PerformLayout();
            this.ResumeLayout(false);
            this.PerformLayout();
    
        }
    
        #endregion
    
        private System.Windows.Forms.StatusStrip statusStrip1;
        private System.Windows.Forms.ToolStripStatusLabel toolStripStatusLabel1;
        private System.Windows.Forms.Button button1;
        private System.Windows.Forms.Label label1;
        private System.Windows.Forms.Label label2;
    }