¿Cómo obtener TODOS los controles secundarios de una forma de Windows Forms de un tipo específico (Button / Textbox)?

Necesito obtener todos los controles en un formulario que son de tipo x. Estoy bastante seguro de haber visto ese código una vez en el pasado que usó algo como esto:

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

Sé que puedo repetir todos los controles para que los niños utilicen una función recursiva, pero ¿hay algo más sencillo o más directo, tal vez como el siguiente?

 Dim Ctrls = From ctrl In Me.Controls Where ctrl.GetType Is Textbox 

Aquí hay otra opción para ti. Lo probé creando una aplicación de muestra, luego puse un GroupBox y un GroupBox dentro del GroupBox inicial. Dentro del GroupBox nested pongo 3 controles TextBox y un botón. Este es el código que utilicé (incluso incluye la recursión que estabas buscando)

 public IEnumerable GetAll(Control control,Type type) { var controls = control.Controls.Cast(); return controls.SelectMany(ctrl => GetAll(ctrl,type)) .Concat(controls) .Where(c => c.GetType() == type); } 

Para probarlo en el evento de carga de formulario, quería un recuento de todos los controles dentro del GroupBox inicial

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

Y devolvió el recuento correcto cada vez, así que creo que esto funcionará perfectamente para lo que estás buscando 🙂

En C # (ya que lo etiquetó como tal) podría usar una expresión LINQ como esta:

 List c = Controls.OfType().Cast().ToList(); 

Editar para la recursión:

En este ejemplo, primero crea la lista de controles y luego llama a un método para poblarlo. Como el método es recursivo, no devuelve la lista, solo la actualiza.

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

Es posible hacer esto en una statement LINQ utilizando la función Descendants , aunque no estoy tan familiarizado con ella. Vea esta página para más información sobre eso.

Edite 2 para devolver una colección:

Como sugirió @ProfK, un método que simplemente devuelve los controles deseados es probablemente una mejor práctica. Para ilustrar esto, he modificado el código de la siguiente manera:

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

Esta es una versión mejorada de GetAllControls recursivo () que realmente funciona en vars privados:

  private void Test() { List allTextboxes = GetAllControls(this); } private List GetAllControls(Control container, List 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 GetAllControls(Control container) { return GetAllControls(container, new List()); } 

Combiné un montón de las ideas anteriores en un método de extensión. Los beneficios aquí son que obtiene el elemento enumerable correctamente tipeado, además OfType() maneja correctamente la OfType() .

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

Puede usar una consulta LINQ para hacer esto. Esto consultará todo en el formulario que es tipo TextBox

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

Puede ser la técnica antigua, pero funciona como el encanto. Usé recursión para cambiar el color de todas las tags del control. Funciona muy bien.

 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; } } } 

Me gustaría enmendar la respuesta de PsychoCoders: como el usuario quiere obtener todos los controles de un cierto tipo, podríamos usar los generics de la siguiente manera:

  public IEnumerable FindControls(Control control) where T : Control { // we can't cast here because some controls in here will most likely not be  var controls = control.Controls.Cast(); return controls.SelectMany(ctrl => FindControls(ctrl)) .Concat(controls) .Where(c => c.GetType() == typeof(T)).Cast(); } 

De esta manera, podemos llamar a la función de la siguiente manera:

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

No olvide que también puede tener un TextBox dentro de otros controles que no sean controles de contenedor. Incluso puede agregar un TextBox a un PictureBox.

Entonces también debe verificar si

 someControl.HasChildren = True 

en cualquier función recursiva

Este es el resultado que obtuve de un diseño para probar este código:

 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 

Pruebe esto con un botón y un RichTextBox en un formulario.

 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 

Usando la reflexión:

 // Return a list with all the private fields with the same type List GetAllControlsWithTypeFromControl(Control parentControl) { List retValue = new List(); 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 ctrls = GetAllControlsWithTypeFromControl(this); 

Aquí está la Solución.

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

He escrito este fragmento de código y seleccioné solo los paneles, puede agregar más interruptores o ifs. en eso

 public List GetAllChildControls(Control Root, Type FilterType = null) { List AllChilds = new List(); 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; } 

Aquí está mi método de extensión para Control , usando LINQ, como una adaptación de la versión de @PsychoCoder :

En su lugar, lleva una lista de tipo que le permite no necesitar varias llamadas de GetAll para obtener lo que desea. Actualmente lo uso como una versión de sobrecarga.

 public static IEnumerable GetAll(this Control control, IEnumerable filteringTypes) { var ctrls = control.Controls.Cast(); return ctrls.SelectMany(ctrl => GetAll(ctrl, filteringTypes)) .Concat(ctrls) .Where(ctl => filteringTypes.Any(t => ctl.GetType() == t)); } 

Uso:

 // The types you want to select var typeToBeSelected = new List { 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 Ctrls = from Control ctrl in Me.Controls where ctrl is TextBox | ctrl is GroupBox select ctr; 

Expresiones Lambda

 IEnumerable Ctrls = Me.Controls.Cast().Where(c => c is Button | c is GroupBox); 

Una solución limpia y fácil (C #):

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

Obtener todos los cuadros de texto:

 List textboxes = myControl.GetAllControls(); 

Modifiqué desde @PsychoCoder. Todos los controles se pueden encontrar ahora (incluir nesteds).

 public static IEnumerable GetChildrens(Control control) { var type = typeof (T); var allControls = GetAllChildrens(control); return allControls.Where(c => c.GetType() == type).Cast(); } private static IEnumerable GetAllChildrens(Control control) { var controls = control.Controls.Cast(); return controls.SelectMany(c => GetAllChildrens(c)) .Concat(controls); } 

Esto puede funcionar:

 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 

Creo que la función para obtener todos los controles de los que está hablando solo está disponible para WPF .

Aquí hay una solución genérica probada y funcional:

Tengo un gran número de controles UpDownNumeric, algunos en el formulario principal, algunos en grupos dentro del formulario. Solo quiero que el último control seleccionado cambie el color de fondo a verde, para lo cual primero configuré todos los demás en blanco, usando este método: (también puede expandirse a los nietos)

  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; } } 

Puedes probar esto si quieres 🙂

  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); } 

Aunque otros usuarios han publicado soluciones adecuadas, me gustaría publicar un enfoque más general que pueda ser más útil.

Esto se basa principalmente en la respuesta de JYelton.

 public static IEnumerable AllControls( this Control control, Func filter = null) { if (control == null) throw new ArgumentNullException("control"); if (filter == null) filter = (c => true); var list = new List(); foreach (Control c in control.Controls) { list.AddRange(AllControls(c, filter)); if (filter(c)) list.Add(c); } return list; } 
  public static IEnumerable GetAllControls(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()) yield return c1; } } 
  public IEnumerable GetAll(Control control) where T : Control { var type = typeof(T); var controls = control.Controls.Cast().ToArray(); foreach (var c in controls.SelectMany(GetAll).Concat(controls)) if (c.GetType() == type) yield return (T)c; } 

Aquí está mi método de extensión. Es muy eficiente y es flojo.

Uso:

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

El código es:

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

Para cualquiera que busque una versión VB del código C # de Adam escrito como una extensión de la clase Control :

 ''' Collects child controls of the specified type or base type within the passed control. ''' The type of child controls to include. Restricted to objects of type Control. ''' Required. The parent form control. ''' An object of type IEnumerable(Of T) containing the control collection. ''' This method recursively calls itself passing child controls as the parent control.  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 

NOTA: He agregado coincidencia de BaseType para cualquier control personalizado derivado. Puede eliminar esto o incluso convertirlo en un parámetro opcional si lo desea.

Uso

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