提问人:Luis 提问时间:8/6/2010 最后编辑:Vadim OvchinnikovLuis 更新时间:1/8/2022 访问量:240687
如何获取特定类型(按钮/文本框)的 Windows 窗体窗体的所有子控件?
How to get ALL child controls of a Windows Forms form of a specific type (Button/Textbox)?
问:
我需要获取类型为 x 的窗体上的所有控件。我很确定我过去曾经看到过使用过这样的代码:
dim ctrls() as Control
ctrls = Me.Controls(GetType(TextBox))
我知道我可以使用递归函数遍历所有控件,但是 有没有更简单或更直接的东西,也许像下面这样?
Dim Ctrls = From ctrl In Me.Controls Where ctrl.GetType Is Textbox
答:
在 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);
}
}
尽管我对它不太熟悉,但可以使用该函数在一个 LINQ 语句中执行此操作。有关这方面的更多信息,请参阅此页面。Descendants
编辑 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;
评论
var c = this.Controls.OfType<TextBox>()
这可能有效:
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。您甚至可以将 TextBox 添加到 PictureBox。
所以你还需要检查一下是否
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
在窗体上使用一个 Button 和一个 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;
}
}
}
我从@PsychoCoder修改。 现在可以找到所有控件(包括嵌套控件)。
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
我写了这段代码,只选择了面板,可以添加更多的开关或ifs。在其中
下面是一个经过测试且有效的通用解决方案:
我有大量的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;
}
}
评论
我想修改一下 PsychoCoders 的答案:由于用户想要获得某种类型的所有控件,我们可以通过以下方式使用泛型:
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());
}
评论
.OfType<T>()
.Where(c => c.GetType() == typeof(T)).Cast<T>();
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);
这是我使用 LINQ 作为@PsychoCoder版本的改编的扩展方法:Control
它采用一个类型的列表,使你不需要多次调用来获得你想要的东西。我目前将其用作重载版本。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;
}
}
}
}
评论
对于任何寻找作为类扩展编写的 Adam C# 代码的 VB 版本的人:Control
''' <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
评论
Create 方法
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)
在这种情况下,它将选择选定的单选按钮。
VISUAL BASIC VB.NET对于我们中的一些人来说,他们拒绝将 230,000+ 行代码移植到 c#,这是我的贡献,如果只需要一个特定的类型,只需根据需要添加一个“where”。
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
评论