如何获取特定types(button/文本框)的Windows窗体窗体的所有子控件?
我需要获得一个types为x的表单上的所有控件。 我很确定我在过去曾经看到过这样的代码:
dim ctrls() as Control ctrls = Me.Controls(GetType(TextBox))
我知道我可以遍历所有使用recursion函数获取子项的控件,但有没有更容易或更直接的,可能如下所示?
Dim Ctrls = From ctrl In Me.Controls Where ctrl.GetType Is Textbox
这是另一个select。 我通过创build一个示例应用程序来testing它,然后在最初的GroupBox中放置一个GroupBox和一个GroupBox。 在嵌套的GroupBox内部,我放置了3个TextBox控件和一个button。 这是我使用的代码(甚至包括你正在寻找的recursion)
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); }
为了在表单加载事件中testing它,我希望计算初始GroupBox中的所有控件
private void Form1_Load(object sender, EventArgs e) { var c = GetAll(this,typeof(TextBox)); MessageBox.Show("Total Controls: " + c.Count()); }
而且它每次都返回正确的计数,所以我认为这将适用于你正在寻找的东西:)
在C#中(因为你标记为这样),你可以使用这样的LINQexpression式:
List<Control> c = Controls.OfType<TextBox>().Cast<Control>().ToList();
编辑recursion:
在这个例子中,你首先创build控件列表,然后调用一个方法来填充它。 由于该方法是recursion的,它不返回列表,它只是更新它。
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语句中可以这样做,尽pipe我并不熟悉它。 看到这个页面的更多信息。
编辑2返回一个集合:
正如@ProfK所build议的那样,简单地返回所需控件的方法可能是更好的做法。 为了说明这一点,我修改了代码如下:
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; }
这是一个改进版本的recursionGetAllControls(),实际上在私人variables上工作:
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>()); }
我把一堆以前的想法合并成一个扩展方法。 这里的好处是你得到了正确types的枚举,加上inheritance由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))); }
您可以使用LINQ查询来执行此操作。 这将查询types为TextBox的表单上的所有内容
var c = from controls in this.Controls.OfType<TextBox>() select controls;
这可能是古老的技术,但它的作品像魅力。 我用recursion来改变控件的所有标签的颜色。 它工作很好。
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; } } }
我想修改PsychoCoders答案:因为用户想要获得某种types的所有控件,我们可以按照以下方式使用generics:
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()); }
不要忘记,您也可以在容器控件以外的其他控件中包含一个TextBox。 你甚至可以添加一个文本框到一个PictureBox。
所以你还需要检查是否
someControl.HasChildren = True
在任何recursion函数中。
这是我从布局testing这个代码的结果:
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
使用reflection:
// 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);
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; }
这里是我的Control
扩展方法,使用LINQ,作为@PsychoCoder版本的改编:
它需要一个types的列表,而不需要多次调用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; }
IEnumerable<Control> Ctrls = from Control ctrl in Me.Controls where ctrl is TextBox | ctrl is GroupBox select ctr;
Lambdaexpression式
IEnumerable<Control> Ctrls = Me.Controls.Cast<Control>().Where(c => c is Button | c is GroupBox);
我从@PsychoCoder修改。 现在可以find所有控件(包括嵌套)。
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); }
这可能工作:
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 。
这是一个经过testing和工作的通用解决scheme
我有大量的UpDownNumeric控件,一些在主窗体中,一些在groupboxes窗体中。 我只想要最后一个选定的控件将背景颜色变成绿色,为此我首先使用这种方法将所有其他颜色设置为白色(也可以展开给孙子们)
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; } }
你可以试试这个,如果你想:)
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); }
虽然其他几个用户已经发布了适当的解决scheme,但是我想发布一个更加通用的方法,可能会更有用。
这主要基于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; }
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; } }
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; }
清洁和简单的解决scheme(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; } } } }