如何获取 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
不会继承 Control
。 Control
和 ToolStripStatusLabel
共享的最具体的碱基 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 继承的不相交性质,必须进行一些特殊处理:
- 您找到的项目可能是
Control
的实例,也可能不是。 Control
的实例将其名称存储在 Control.Name
属性 中,但显然不是 Control
object 的实例不会。它们需要通过识别存储名称 属性 的类型来专门处理(在本例中为 ToolStripItem.Name
,但请注意还有其他非 Control
类型继承 Component
,每个都必须单独处理)。我添加了一个 GetNameForComponent(Component)
方法来封装它。
- 在您找到的 是 个
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;
}
我的 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
不会继承 Control
。 Control
和 ToolStripStatusLabel
共享的最具体的碱基 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 继承的不相交性质,必须进行一些特殊处理:
- 您找到的项目可能是
Control
的实例,也可能不是。Control
的实例将其名称存储在Control.Name
属性 中,但显然不是Control
object 的实例不会。它们需要通过识别存储名称 属性 的类型来专门处理(在本例中为ToolStripItem.Name
,但请注意还有其他非Control
类型继承Component
,每个都必须单独处理)。我添加了一个GetNameForComponent(Component)
方法来封装它。 - 在您找到的 是 个
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;
}