如何获取特定类型(Button/Textbox)的 Windows 窗体形式的所有子控件?

我需要得到一个表单的所有控件类型为 x。我非常肯定我曾经看到过这样的代码:

dim ctrls() as Control
ctrls = Me.Controls(GetType(TextBox))

我知道我可以使用递归函数迭代所有的控件,但是 有没有更简单或更直接的方法,比如下面这些?

Dim Ctrls = From ctrl In Me.Controls Where ctrl.GetType Is Textbox
233426 次浏览

在 C # 中(因为你这样标记了它) ,你可以像这样使用一个 LINQ 表达式:

List<Control> c = Controls.OfType<TextBox>().Cast<Control>().ToList();

递归编辑:

在本例中,首先创建控件列表,然后调用一个方法来填充它。因为该方法是递归的,所以它不返回列表,只是更新列表。

List<Control> ControlList = new List<Control>();
private void GetAllControls(Control container)
{
foreach (Control c in container.Controls)
{
GetAllControls(c);
if (c is TextBox) ControlList.Add(c);
}
}

使用 Descendants函数在一个 LINQ 语句中完成这个操作是可能的,尽管我对它不太熟悉。有关这方面的更多信息,请参见 这一页

编辑2以返回一个集合:

正如@ProfK 所建议的,简单地返回所需控件的方法可能是更好的实践。为了说明这一点,我修改了代码如下:

private IEnumerable<Control> GetAllTextBoxControls(Control container)
{
List<Control> controlList = new List<Control>();
foreach (Control c in container.Controls)
{
controlList.AddRange(GetAllTextBoxControls(c));
if (c is TextBox)
controlList.Add(c);
}
return controlList;
}

您可以使用 LINQ 查询来执行此操作。这将查询 TextBox 类型表单上的所有内容

var c = from controls in this.Controls.OfType<TextBox>()
select controls;

这可能行得通:

Public Function getControls(Of T)() As List(Of T)
Dim st As New Stack(Of Control)
Dim ctl As Control
Dim li As New List(Of T)


st.Push(Me)


While st.Count > 0
ctl = st.Pop
For Each c In ctl.Controls
st.Push(CType(c, Control))
If c.GetType Is GetType(T) Then
li.Add(CType(c, T))
End If
Next
End While


Return li
End Function

我认为你所说的获取所有控制的功能只适用于 WPF

给你另一个选择。我通过创建一个示例应用程序来测试它,然后将一个 GroupBox 和一个 GroupBox 放在初始 GroupBox 中。在嵌套的 GroupBox 中,我放置了3个 TextBox 控件和一个按钮。这就是我使用的代码(甚至包括您正在寻找的递归)

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


return controls.SelectMany(ctrl => GetAll(ctrl,type))
.Concat(controls)
.Where(c => c.GetType() == type);
}

为了在表单加载事件中测试它,我需要初始 GroupBox 中所有控件的计数

private void Form1_Load(object sender, EventArgs e)
{
var c = GetAll(this,typeof(TextBox));
MessageBox.Show("Total Controls: " + c.Count());
}

而且每次都返回正确的计数,所以我认为这将非常适合您所寻找的:)

这是递归 GetAllControls ()的一个改进版本,它实际上可以在私有变量上工作:

    private void Test()
{
List<Control> allTextboxes = GetAllControls(this);
}
private List<Control> GetAllControls(Control container, List<Control> list)
{
foreach (Control c in container.Controls)
{
if (c is TextBox) list.Add(c);
if (c.Controls.Count > 0)
list = GetAllControls(c, list);
}


return list;
}
private List<Control> GetAllControls(Control container)
{
return GetAllControls(container, new List<Control>());
}

不要忘记,在其他控件 除了容器控件中也可以有一个 TextBox。您甚至可以向 PictureBox 添加 TextBox。

所以你还需要检查

someControl.HasChildren = True

在任何递归函数中。

这是我从测试这段代码的布局中得到的结果:

TextBox13   Parent = Panel5
TextBox12   Parent = Panel5
TextBox9   Parent = Panel2
TextBox8   Parent = Panel2
TextBox16   Parent = Panel6
TextBox15   Parent = Panel6
TextBox14   Parent = Panel6
TextBox10   Parent = Panel3
TextBox11   Parent = Panel4
TextBox7   Parent = Panel1
TextBox6   Parent = Panel1
TextBox5   Parent = Panel1
TextBox4   Parent = Form1
TextBox3   Parent = Form1
TextBox2   Parent = Form1
TextBox1   Parent = Form1
tbTest   Parent = myPicBox

尝试在表单上使用 一个按钮一个 RichTextBox

Option Strict On
Option Explicit On
Option Infer Off


Public Class Form1


Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click


Dim pb As New PictureBox
pb.Name = "myPicBox"
pb.BackColor = Color.Goldenrod
pb.Size = New Size(100, 100)
pb.Location = New Point(0, 0)
Dim tb As New TextBox
tb.Name = "tbTest"
pb.Controls.Add(tb)
Me.Controls.Add(pb)


Dim textBoxList As New List(Of Control)
textBoxList = GetAllControls(Of TextBox)(Me)


Dim sb As New System.Text.StringBuilder
For index As Integer = 0 To textBoxList.Count - 1
sb.Append(textBoxList.Item(index).Name & "   Parent = " & textBoxList.Item(index).Parent.Name & System.Environment.NewLine)
Next


RichTextBox1.Text = sb.ToString
End Sub


Private Function GetAllControls(Of T)(ByVal searchWithin As Control) As List(Of Control)


Dim returnList As New List(Of Control)


If searchWithin.HasChildren = True Then
For Each ctrl As Control In searchWithin.Controls
If TypeOf ctrl Is T Then
returnList.Add(ctrl)
End If
returnList.AddRange(GetAllControls(Of T)(ctrl))
Next
ElseIf searchWithin.HasChildren = False Then
For Each ctrl As Control In searchWithin.Controls
If TypeOf ctrl Is T Then
returnList.Add(ctrl)
End If
returnList.AddRange(GetAllControls(Of T)(ctrl))
Next
End If
Return returnList
End Function


End Class

这可能是古老的技术,但它的工作像魅力。我使用递归来更改控件的所有标签的颜色。效果很好。

internal static void changeControlColour(Control f, Color color)
{
foreach (Control c in f.Controls)
{


// MessageBox.Show(c.GetType().ToString());
if (c.HasChildren)
{
changeControlColour(c, color);
}
else
if (c is Label)
{
Label lll = (Label)c;
lll.ForeColor = color;
}
}
}

我从“心理编码器”改编而来。 现在可以找到所有控件(包括嵌套的)。

public static IEnumerable<T> GetChildrens<T>(Control control)
{
var type = typeof (T);


var allControls = GetAllChildrens(control);


return allControls.Where(c => c.GetType() == type).Cast<T>();
}


private static IEnumerable<Control> GetAllChildrens(Control control)
{
var controls = control.Controls.Cast<Control>();
return controls.SelectMany(c => GetAllChildrens(c))
.Concat(controls);
}

我将前面的一些想法组合成一个扩展方法。这里的好处是可以返回正确类型的可枚举值,而且继承可以由 OfType()正确处理。

public static IEnumerable<T> FindAllChildrenByType<T>(this Control control)
{
IEnumerable<Control> controls = control.Controls.Cast<Control>();
return controls
.OfType<T>()
.Concat<T>(controls.SelectMany<Control, T>(ctrl => FindAllChildrenByType<T>(ctrl)));
}

这是解决方案。

Https://stackoverflow.com/a/19224936/1147352

我已经写了这段代码,只选择了面板,你可以添加更多的开关或如果。在里面

下面是一个经过测试和可行的通用解决方案:

我有大量的 UpDownNumeric 控件,有些在主窗体中,有些在窗体内的分组框中。 我只希望最后一个选定的控件将背景颜色更改为绿色,对于这个控件,我首先将所有其他控件设置为白色,使用这个方法: (也可以扩展到子级)

    public void setAllUpDnBackColorWhite()
{
//To set the numericUpDown background color of the selected control to white:
//and then the last selected control will change to green.


foreach (Control cont in this.Controls)
{
if (cont.HasChildren)
{
foreach (Control contChild in cont.Controls)
if (contChild.GetType() == typeof(NumericUpDown))
contChild.BackColor = Color.White;
}
if (cont.GetType() == typeof(NumericUpDown))
cont.BackColor = Color.White;
}
}

我想修改心理编码器的回答: 由于用户希望获得某种类型的所有控件,我们可以通过以下方式使用泛型:

    public IEnumerable<T> FindControls<T>(Control control) where T : Control
{
// we can't cast here because some controls in here will most likely not be <T>
var controls = control.Controls.Cast<Control>();


return controls.SelectMany(ctrl => FindControls<T>(ctrl))
.Concat(controls)
.Where(c => c.GetType() == typeof(T)).Cast<T>();
}

这样,我们可以调用函数如下:

private void Form1_Load(object sender, EventArgs e)
{
var c = FindControls<TextBox>(this);
MessageBox.Show("Total Controls: " + c.Count());
}
public List<Control> GetAllChildControls(Control Root, Type FilterType = null)
{
List<Control> AllChilds = new List<Control>();
foreach (Control ctl in Root.Controls) {
if (FilterType != null) {
if (ctl.GetType == FilterType) {
AllChilds.Add(ctl);
}
} else {
AllChilds.Add(ctl);
}
if (ctl.HasChildren) {
GetAllChildControls(ctl, FilterType);
}
}
return AllChilds;
}

如果你愿意,你可以试试这个:)

    private void ClearControls(Control.ControlCollection c)
{
foreach (Control control in c)
{
if (control.HasChildren)
{
ClearControls(control.Controls);
}
else
{
if (control is TextBox)
{
TextBox txt = (TextBox)control;
txt.Clear();
}
if (control is ComboBox)
{
ComboBox cmb = (ComboBox)control;
if (cmb.Items.Count > 0)
cmb.SelectedIndex = -1;
}


if (control is CheckBox)
{
CheckBox chk = (CheckBox)control;
chk.Checked = false;
}


if (control is RadioButton)
{
RadioButton rdo = (RadioButton)control;
rdo.Checked = false;
}


if (control is ListBox)
{
ListBox listBox = (ListBox)control;
listBox.ClearSelected();
}
}
}
}
private void btnClear_Click(object sender, EventArgs e)
{
ClearControls((ControlCollection)this.Controls);
}

虽然其他一些用户已经发布了足够的解决方案,但我想发布一个更通用的方法,可能更有用。

这在很大程度上是基于 JYelton 的反应。

public static IEnumerable<Control> AllControls(
this Control control,
Func<Control, Boolean> filter = null)
{
if (control == null)
throw new ArgumentNullException("control");
if (filter == null)
filter = (c => true);


var list = new List<Control>();


foreach (Control c in control.Controls) {
list.AddRange(AllControls(c, filter));
if (filter(c))
list.Add(c);
}
return list;
}

使用反射:

// Return a list with all the private fields with the same type
List<T> GetAllControlsWithTypeFromControl<T>(Control parentControl)
{
List<T> retValue = new List<T>();
System.Reflection.FieldInfo[] fields = parentControl.GetType().GetFields(System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
foreach (System.Reflection.FieldInfo field in fields)
{
if (field.FieldType == typeof(T))
retValue.Add((T)field.GetValue(parentControl));
}
}


List<TextBox> ctrls = GetAllControlsWithTypeFromControl<TextBox>(this);

下面是我对 Control的扩展方法,使用 LINQ 作为 @ 心理编码器版本的改编:

取而代之的是一个类型列表,它允许您不需要多次调用 GetAll来获得所需的内容。我目前使用它作为一个重载版本。

public static IEnumerable<Control> GetAll(this Control control, IEnumerable<Type> filteringTypes)
{
var ctrls = control.Controls.Cast<Control>();


return ctrls.SelectMany(ctrl => GetAll(ctrl, filteringTypes))
.Concat(ctrls)
.Where(ctl => filteringTypes.Any(t => ctl.GetType() == t));
}

用法:

//   The types you want to select
var typeToBeSelected = new List<Type>
{
typeof(TextBox)
, typeof(MaskedTextBox)
, typeof(Button)
};


//    Only one call
var allControls = MyControlThatContainsOtherControls.GetAll(typeToBeSelected);


//    Do something with it
foreach(var ctrl in allControls)
{
ctrl.Enabled = true;
}
    public static IEnumerable<T> GetAllControls<T>(this Control control) where T : Control
{
foreach (Control c in control.Controls)
{
if (c is T)
yield return (T)c;
foreach (T c1 in c.GetAllControls<T>())
yield return c1;
}
}
   IEnumerable<Control> Ctrls = from Control ctrl in Me.Controls where ctrl is TextBox | ctrl is GroupBox select ctr;

Lambda 短语

IEnumerable<Control> Ctrls = Me.Controls.Cast<Control>().Where(c => c is Button | c is GroupBox);
    public IEnumerable<T> GetAll<T>(Control control) where T : Control
{
var type = typeof(T);
var controls = control.Controls.Cast<Control>().ToArray();
foreach (var c in controls.SelectMany(GetAll<T>).Concat(controls))
if (c.GetType() == type) yield return (T)c;
}

一个简单明了的解决方案(C #) :

static class Utilities {
public static List<T> GetAllControls<T>(this Control container) where T : Control {
List<T> controls = new List<T>();
if (container.Controls.Count > 0) {
controls.AddRange(container.Controls.OfType<T>());
foreach (Control c in container.Controls) {
controls.AddRange(c.GetAllControls<T>());
}
}


return controls;
}
}

获取所有文本框:

List<TextBox> textboxes = myControl.GetAllControls<TextBox>();

这是我的扩展方法。它非常有效,而且很懒。

用法:

var checkBoxes = tableLayoutPanel1.FindChildControlsOfType<CheckBox>();


foreach (var checkBox in checkBoxes)
{
checkBox.Checked = false;
}

密码是:

public static IEnumerable<TControl> FindChildControlsOfType<TControl>(this Control control) where TControl : Control
{
foreach (var childControl in control.Controls.Cast<Control>())
{
if (childControl.GetType() == typeof(TControl))
{
yield return (TControl)childControl;
}
else
{
foreach (var next in FindChildControlsOfType<TControl>(childControl))
{
yield return next;
}
}
}
}

对于任何寻找作为 Control类的扩展编写的 Adam C # 代码的 VB 版本的人:

''' <summary>Collects child controls of the specified type or base type within the passed control.</summary>
''' <typeparam name="T">The type of child controls to include. Restricted to objects of type Control.</typeparam>
''' <param name="Parent">Required. The parent form control.</param>
''' <returns>An object of type IEnumerable(Of T) containing the control collection.</returns>
''' <remarks>This method recursively calls itself passing child controls as the parent control.</remarks>
<Extension()>
Public Function [GetControls](Of T As Control)(
ByVal Parent As Control) As IEnumerable(Of T)


Dim oControls As IEnumerable(Of Control) = Parent.Controls.Cast(Of Control)()
Return oControls.SelectMany(Function(c) GetControls(Of T)(c)).Concat(oControls.Where(Function(c) c.GetType() Is GetType(T) Or c.GetType().BaseType Is GetType(T))
End Function

注意: 我已经为任何派生的自定义控件添加了 BaseType匹配。如果愿意,您可以删除这个参数,甚至将其作为一个可选参数。

用法

Dim oButtons As IEnumerable(Of Button) = Me.GetControls(Of Button)()

您可以使用以下代码

public static class ExtensionMethods
{
public static IEnumerable<T> GetAll<T>(this Control control)
{
var controls = control.Controls.Cast<Control>();


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

简单来说:

For Each ctrl In Me.Controls.OfType(Of Button)()
ctrl.Text = "Hello World!"
Next

创建方法

public static IEnumerable<Control> GetControlsOfType<T>(Control control)
{
var controls = control.Controls.Cast<Control>();
return controls.SelectMany(ctrl => GetControlsOfType<T>(ctrl)).Concat(controls).Where(c => c is T);
}

像这样使用它

Var controls= GetControlsOfType<TextBox>(this);//You can replace this with your control

我用 VB 写了一个扩展方法,它检索一个控件的所有子控件和子控件

Imports System.Runtime.CompilerServices
Module ControlExt


<Extension()>
Public Function GetAllChildren(Of T As Control)(parentControl As Control) As IEnumerable(Of T)
Dim controls = parentControl.Controls.Cast(Of Control)
Return controls.SelectMany(Of Control)(Function(ctrl) _
GetAllChildren(Of T)(ctrl)) _
.Concat(controls) _
.Where(Function(ctrl) ctrl.GetType() = GetType(T)) _
.Cast(Of T)
End Function


End Module

然后您可以像“ btnList”是控件那样使用它

btnList.GetAllChildren(Of HtmlInputRadioButton).FirstOrDefault(Function(rb) rb.Checked)

在这种情况下,它将选择所选的单选按钮。

VB.NET 对于我们中的一些人,他们拒绝将超过230,000行的代码移植到 c # ,这里是我的贡献,如果只需要一个特定的类型,只需要根据需要添加一个“ 哪里”。

Private Shared Function getAll(control As Control) As IEnumerable(Of Control)
Return control.Controls.Cast(Of Control) _
.SelectMany(Function(f) getAll(f).Concat(control.Controls.Cast(Of Control)))
End Function