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

Posted

技术标签:

【中文标题】如何获取 WinForm 上所有控件的列表,即使是 SplitContainers 或 Panels 中的控件【英文标题】:How to get a list of all controls on WinForm even those in SplitContainers or Panels 【发布时间】:2015-12-26 06:50:11 【问题描述】:

我的 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、Panel 和StatuStrip 的表单。在这些控件中的每一个中,我都有几个子控件,例如StatuStrip1 中的toolStripStatusLabel1。问题是当我尝试通过函数 ListAllControls 在StatuStrip 中找到例如控制工具StripStatusLabel1 时,我找不到它!?我不知道有任何其他方法可以从表单中获取所有控件。完整代码在这里:

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,我得到 “我找到了控件 StatusStrip1!” 我希望现在的问题比以前更清楚。

【问题讨论】:

在调试器中单步执行代码时发生了什么?代码是否到达了任何有问题的容器?如果是这样,当foreach 语句试图枚举容器的孩子时发生了什么?您发布的代码很简单,应该可以正常工作。所以你的问题出在你没有发布的代码中。请提供可靠地重现问题的a good, minimal, complete code example。 这是所有代码。它是一个简单的递归函数,需要通过其名称来查找控件。调试器正确地迭代所有窗体根控件,它们不是控件容器。我可以毫无问题地看到它们的名称和属性。当步入某些容器时,通常会显示一个空字符串来表示名称和属性。只是提到我尝试使用
.Controls.Find("", true) 并出现同样的问题!
"This is all code" -- 如果你的意思是“这都是 the 代码”,那么这显然不是真的。请阅读我提供的链接中的页面,了解 minimalcomplete 的含义,并解释为什么需要这样的代码示例。跨度> 如果您正在寻找特定的控件,那么您的示例 .Controls.Find("<control_name>", true) 应该可以工作。您需要更好地记录这一点。 ToolStripStatusLabel 不是控件。它是一个组件。您必须找到 ToolStrip 控件,然后搜索该集合中的任何项目。 【参考方案1】:

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

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

另一个问题是,虽然 ToolStrip 本身确实继承了 Control 类,但它不会将其子级存储在 Controls 属性中(即在 ControlCollection 对象中)。我认为这是因为它的子对象不是Control 对象,因此无法存储在ControlCollection 中。无论如何,这意味着当您通过表单的对象图进行递归时,您必须以不同于其他 Control 实例的方式处理 ToolStrip 才能找到它的子级。

下面是一个示例程序,它演示了一种可行的方法(请参阅本文底部的设计器生成的代码,该代码与此示例一起使用):

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;
    

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

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

解决了这两个问题,然后可以轻松编写搜索图的基本递归方法。请注意,与您的相比,我对该方法的基本架构进行了一些更改。我认为将这段代码单独实现为一个完整的类是不必要的,并且无论如何将结果存储在该类的成员中而不是简单地由方法返回是特别错误的。

在我的示例中,我将它简单地实现为一个方法,如果找到该对象,则返回该方法。

另外请注意,您的实现不会找到任何本身就是对象容器的对象。我也在我的示例中更正了这一点。

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

另一种方法是使用反射来查找包含例如的类成员。名字和孩子。假设名称始终存储在名为Name 的属性中,那部分会相对简单。

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

考虑到将ToolStrip 孩子与普通Control 孩子以某种形式对待是否真的有意义存在争议,我不建议在真正通用的解决方案上投入太多精力。最好根据需要处理个别情况,提醒自己你所做的首先并不是一个好主意。 :)

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;

【讨论】:

非常感谢彼得·杜尼霍。我的问题是我不明白标准控件和 StatusStrip 的“控件”之间的区别。您以不同方式对待它的想法(解决方案)很棒!再次感谢您,欢呼!

以上是关于如何获取 WinForm 上所有控件的列表,即使是 SplitContainers 或 Panels 中的控件的主要内容,如果未能解决你的问题,请参考以下文章

winform 父窗体如何获取子窗体控件的值

winform中如何获取控件在窗体上位置?

winform如何从DataGridView中从右键菜单获取一行数据

Winform如何获取一堆控件中某个的Tag

如何在winform列表框项目上添加工具提示

c# winform如何取得一个区域的所有控件对象