Come ottenere TUTTI i controlli figlio di un modulo Windows Form di un tipo specifico (Pulsante / Casella di testo)?

Devo ottenere tutti i controlli su un modulo di tipo x. Sono abbastanza sicuro di aver visto quel codice una volta in passato che usava qualcosa del genere:

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

So che posso scorrere tutti i controlli per far sì che i bambini usino una funzione ricorsiva, ma c’è qualcosa di più semplice o più diretto, forse come il seguente?

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

Ecco un’altra opzione per te. L’ho provato creando un’applicazione di esempio, quindi ho inserito un GroupBox e un GroupBox all’interno del GroupBox iniziale. All’interno del GroupBox nidificato ho inserito 3 controlli TextBox e un pulsante. Questo è il codice che ho usato (include anche la ricorsione che stavi cercando)

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

Per testarlo nell’evento di caricamento del modulo, volevo un conteggio di tutti i controlli all’interno del GroupBox iniziale

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

E ha restituito il conteggio corretto ogni volta, quindi penso che funzionerà perfettamente per quello che stai cercando 🙂

In C # (dato che l’hai taggato come tale) potresti usare un’espressione LINQ come questa:

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

Modifica per ricorsione:

In questo esempio, devi prima creare l’elenco dei controlli e quindi chiamare un metodo per popolare. Poiché il metodo è ricorsivo, non restituisce l’elenco, lo aggiorna semplicemente.

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

Potrebbe essere ansible farlo in una istruzione LINQ usando la funzione Descendants , sebbene io non abbia familiarità con essa. Vedi questa pagina per maggiori informazioni su questo.

Modifica 2 per restituire una raccolta:

Come suggerito da @ProfK, un metodo che restituisce semplicemente i controlli desiderati è probabilmente una pratica migliore. Per illustrare questo ho modificato il codice come segue:

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

Questa è una versione migliorata del GetAllControls () ricorsivo che funziona effettivamente su private vars:

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

Ho combinato un gruppo di idee precedenti in un unico metodo di estensione. I vantaggi qui sono che si ottiene il back enumerable correttamente digitato, inoltre l’ereditarietà viene gestita correttamente da OfType() .

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

È ansible utilizzare una query LINQ per eseguire questa operazione. Ciò interrogherà tutto sul modulo che è il tipo TextBox

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

Potrebbe essere la tecnica antica, ma funziona come fascino. Ho usato la ricorsione per cambiare il colore di tutte le etichette del controllo. Funziona alla grande.

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

Vorrei modificare la risposta di PsychoCoders: poiché l’utente desidera ottenere tutti i controlli di un certo tipo, potremmo utilizzare i generici nel seguente modo:

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

In questo modo, possiamo chiamare la funzione come segue:

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

Non dimenticare che puoi anche avere un controllo TextBox all’interno di altri controlli oltre ai controlli del contenitore. È anche ansible aggiungere un controllo TextBox a PictureBox.

Quindi è anche necessario controllare se

 someControl.HasChildren = True 

in ogni funzione ricorsiva.

Questo è il risultato che ho avuto da un layout per testare questo codice:

 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 

Prova questo con un pulsante e un RichTextBox su un modulo.

 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 riflessione:

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

Ecco la soluzione.

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

Ho scritto questo pezzo di codice e selezionato solo i pannelli, è ansible aggiungere più interruttori o if. dentro

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

Ecco il mio metodo di estensione per Control , usando LINQ, come adattamento della versione di @PsychoCoder :

Prende invece un elenco di tipi che ti consente di non aver bisogno di più chiamate di GetAll per ottenere ciò che desideri. Attualmente lo uso come una versione di sovraccarico.

 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; 

Lambda Expressions

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

Una soluzione semplice e pulita (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; } } 

Ottieni tutte le caselle di testo:

 List textboxes = myControl.GetAllControls(); 

Ho modificato da @PsychoCoder. Ora è ansible trovare tutti i controlli (includere nidificati).

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

Questo potrebbe funzionare:

 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 

Penso che la funzione per ottenere tutti i controlli di cui parli sia disponibile solo per WPF .

Ecco una soluzione generica testata e funzionante:

Ho un numero elevato di controlli UpDownNumeric, alcuni nel modulo principale, alcuni in groupbox all’interno del modulo. Voglio solo l’ultimo controllo selezionato per cambiare il back-colore in verde, per il quale ho prima impostato tutti gli altri su bianco, usando questo metodo: (può anche espandersi ai nipoti)

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

Puoi provare questo se vuoi 🙂

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

Sebbene molti altri utenti abbiano pubblicato soluzioni adeguate, vorrei pubblicare un approccio più generale che potrebbe essere più utile.

Questo è in gran parte basato sulla risposta di 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; } 

Ecco il mio metodo di estensione. È molto efficiente ed è pigro.

Uso:

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

Il codice è:

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

Per chiunque cerchi una versione VB del codice di Adam C # scritto come estensione della class 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: ho aggiunto la corrispondenza BaseType per tutti i controlli personalizzati derivati. Puoi rimuoverlo o persino renderlo un parametro facoltativo, se lo desideri.

uso

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