如何获取 WinForm 上所有控件的列表,即使是 SplitContainers 或 Panels 中的控件

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

我的 WinForm 上有几个容器,如 Panel、SpliContainer、StatusStrip...这些容器中的每一个都包含基本元素,如按钮或文本框。我需要遍历所有表单控件(甚至是 Panels、SplitContainers、StatusStrip 中的控件)以找到一些控件。我尝试使用递归函数

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

但我没有得到容器中的控件!?

更新:

我有一个带有 SplitContainer、面板和 StatuStrip 的表单。在这些控件中的每一个中,我都有几个子控件,例如 StatuStrip1 中的 toolStripStatusLabel1。问题是,当我尝试通过函数 ListAllControls 在 StatuStrip 中找到例如控件 toolStripStatusLabel1 时,我找不到它!?我不知道从表单中获取所有控件的任何其他方法。完整代码在这里:

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

样本:

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! :(");
}

对于此示例,我得到 未找到 :( 但如果我使用 StatusStrip1,我得到 "I found control StatusStrip1!" 我希望现在的问题比以前更清楚。

您最好提供 a good, minimal, complete code example 清楚地说明您的具体情况。但是,根据您添加到问题中的信息,我能够创建我认为具有代表性的代码示例。此答案基于该示例。


正如评论者 LarsTech 指出的那样,ToolStripStatusLabel 不会继承 ControlControlToolStripStatusLabel 共享的最具体的碱基 class 是 Component。所以至少,你在尝试 return 类型 Control 的 object 并且仍然找到 ToolStripStatusLabel 的实例时遇到了一个大问题。即使您确实找到了 object,您也无法将其转换为 Control

另一个问题是,虽然 ToolStrip 本身确实继承了 Control class,但它没有将其 children 存储在 Controls 属性(即在 ControlCollection object 中)。我推测这是因为它的 children 不是 Control object,因此无法存储在 ControlCollection 中。无论如何,这意味着当您递归遍历表单的 object 图时,您必须以不同于 Control 的其他实例的方式处理 ToolStrip 才能找到它的 children.


这是一个示例程序,它演示了一种可行的方法(请参阅此 post 的底部,了解与此示例一起使用的 Designer-generated 代码):

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;
    }
}

由于您正在处理的 object 的 object 继承的不相交性质,必须进行一些特殊处理:

  1. 您找到的项目可能是 Control 的实例,也可能不是。 Control 的实例将其名称存储在 Control.Name 属性 中,但显然不是 Control object 的实例不会。它们需要通过识别存储名称 属性 的类型来专门处理(在本例中为 ToolStripItem.Name,但请注意还有其他非 Control 类型继承 Component,每个都必须单独处理)。我添加了一个 GetNameForComponent(Component) 方法来封装它。
  2. 在您找到的 Control 实例的项目中,有些项目会将其 children 存储在 Controls 集合中,而其他项目不会,而是使用其他一些 属性 来引用存储 children 的集合。同样,提供了一个辅助方法(在这种情况下,GetChildrenForComponent(Component))来封装这种差异。

解决了这两个问题,就可以轻松编写搜索图的基本递归方法了。请注意,与您的方法相比,我对该方法的基本架构做了一些更改。我认为将此代码作为一个完整的 class 本身来实现是不必要的,并且在任何情况下都将结果存储在该 class 的成员中而不是简单地由 return 编辑方法特别不对

在我的示例中,我将它简单地实现为一个方法,如果找到,return就是有问题的 object。

另请注意,您的实施不会找到任何本身就是 object 容器的 object。我也在我的示例中更正了这个问题。


最后一点:按照上述策略,您必须为每个感兴趣的碱基 class 添加代码。也许上面的内容足以满足您的需求,或者您有其他容器类型可以容纳非 Control object 并且不继承 ToolStrip。如果是这样,您将必须根据这些类型在每个辅助方法中添加其他案例。

另一种方法是使用反射来查找包含例如 class 的成员。名称和 children。假设名称总是存储在一个名为 Name 的 属性 中,那部分就相对简单了。

但即使在这个简单的示例中,使用反射来获取 children 也相当复杂。而不是 children 总是在从 属性 中获得的集合 object 中,例如Controls,在一种情况下是名称,但在另一种情况下名称是 Items。可以深入挖掘例如。确定在集合中找到的 object 的类型,但此时代码开始变得过于复杂。

考虑到将 ToolStrip children 与常规 Control children 一样对待是否真的有意义是值得商榷的,无论如何,我不建议在真正通用的解决方案上投入大量精力。最好根据需要处理个案,提醒自己你正在做的事情首先并不是一个好主意。 :)


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;
}