How to get ALL child controls of a Windows Forms form of a specific type (Button/Textbox)?

C#.Netvb.netWinformsControls

C# Problem Overview


I need to get all controls on a form that are of type x. I'm pretty sure I saw that code once in the past that used something like this:

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

I know I can iterate over all controls getting children using a recursive function, but is there something easier or more straightforward, maybe like the following?

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

C# Solutions


Solution 1 - C#

Here's another option for you. I tested it by creating a sample application, I then put a GroupBox and a GroupBox inside the initial GroupBox. Inside the nested GroupBox I put 3 TextBox controls and a button. This is the code I used (even includes the recursion you were looking for)

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

To test it in the form load event I wanted a count of all controls inside the initial GroupBox

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

And it returned the proper count each time, so I think this will work perfectly for what you're looking for :)

Solution 2 - C#

In C# (since you tagged it as such) you could use a LINQ expression like this:

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

Edit for recursion:

In this example, you first create the list of controls and then call a method to populate it. Since the method is recursive, it doesn't return the list, it just updates it.

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

It may be possible to do this in one LINQ statement using the Descendants function, though I am not as familiar with it. See this page for more information on that.

Edit 2 to return a collection:

As @ProfK suggested, a method that simply returns the desired controls is probably better practice. To illustrate this I have modified the code as follows:

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

Solution 3 - C#

This is an improved version of the recursive GetAllControls() that actually works on private vars:

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

Solution 4 - C#

I combined a bunch of the previous ideas into one extension method. The benefits here are that you get the correctly typed enumerable back, plus inheritance is handled correctly by 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)));
}

Solution 5 - C#

You can use a LINQ query to do this. This will query everything on the form that is type TextBox

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

Solution 6 - C#

It might be the ancient technique, but it works like charm. I used recursion to change the color of all labels of the control. It works great.

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

Solution 7 - C#

I'd like to amend PsychoCoders answer: as the user wants to get all controls of a certain type we could use generics in the following way:

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

This way, we can call the function as follows:

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

Solution 8 - C#

A clean and easy solution (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;
    }
}

Get all textboxes:

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

Solution 9 - C#

Don't forget that you can also have a TextBox within other controls other than container controls too. You can even add a TextBox to a PictureBox.

So you also need to check if

someControl.HasChildren = True

in any recursive function.

This is the result I had from a layout to test this code:

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

Try this with one Button and one RichTextBox on a form.

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

Solution 10 - C#

Here is my Extension method. It's very efficient and it's lazy.

Usage:

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

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

The code is:

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

Solution 11 - C#

You can use the below Code

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

Solution 12 - C#

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

Solution 13 - C#

Here is my extension method for Control, using LINQ, as an adaptation of @PsychoCoder version:

It takes a list of type instead that allows you to not need multiple calls of GetAll to get what you want. I currently use it as an overload version.

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

Usage:

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

Solution 14 - C#

Here is the Solution.

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

I have written this piece of code and selected only the panels, you can add more switches or ifs. in it

Solution 15 - C#

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

Solution 16 - C#

   IEnumerable<Control> Ctrls = from Control ctrl in Me.Controls where ctrl is TextBox | ctrl is GroupBox select ctr;

Lambda Expressions

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

Solution 17 - C#

Create Method

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

And use it Like

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

Solution 18 - C#

I'm saldy using VB so, I wrote an extension method. That retrieve all children and sub children of a control

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

Then you can use it like, where "btnList" is a control

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

In this case, it will select the selected radio button.

Solution 19 - C#

This may work:

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

I think the function to get all controls you are talking about is only available to WPF.

Solution 20 - C#

I modified from @PsychoCoder. All controls could be found now (include nested).

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

Solution 21 - C#

Here is a tested and working generic solution:

I have a large number UpDownNumeric controls, some in the main form, some in groupboxes within the form. I want only the one last selected control to change back-color to green, for which I first set all others to white, using this method: (can also expand to grandchildren)

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

Solution 22 - C#

You can try this if you want :)

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

Solution 23 - C#

Although several other users have posted adequate solutions, I'd like to post a more general approach that may be more useful.

This is largely based on JYelton's response.

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

Solution 24 - C#

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

Solution 25 - C#

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

Solution 26 - C#

For anyone looking for a VB version of Adam's C# code written as an extension of the Control class:

''' <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

NOTE: I've added BaseType matching for any derived custom controls. You can remove this or even make it an optional parameter if you wish.

Usage

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

Solution 27 - C#

VISUAL BASIC VB.NET For some of us, who refuse to port 230,000+ lines of code to c# here is my contribution, if only an specific type required, just add a 'where' as needed.

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

Solution 28 - C#

Simply:

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

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionLuisView Question on Stackoverflow
Solution 1 - C#PsychoCoderView Answer on Stackoverflow
Solution 2 - C#JYeltonView Answer on Stackoverflow
Solution 3 - C#VictorEspinaView Answer on Stackoverflow
Solution 4 - C#EntiatView Answer on Stackoverflow
Solution 5 - C#PsychoCoderView Answer on Stackoverflow
Solution 6 - C#Aditya BokadeView Answer on Stackoverflow
Solution 7 - C#AdamView Answer on Stackoverflow
Solution 8 - C#OmarView Answer on Stackoverflow
Solution 9 - C#John Anthony OliverView Answer on Stackoverflow
Solution 10 - C#Jone PolvoraView Answer on Stackoverflow
Solution 11 - C#Santanu SarkarView Answer on Stackoverflow
Solution 12 - C#MiMendaView Answer on Stackoverflow
Solution 13 - C#gfacheView Answer on Stackoverflow
Solution 14 - C#DareDevilView Answer on Stackoverflow
Solution 15 - C#Top SystemsView Answer on Stackoverflow
Solution 16 - C#Memo ArfaaView Answer on Stackoverflow
Solution 17 - C#LuciferView Answer on Stackoverflow
Solution 18 - C#Iannick View Answer on Stackoverflow
Solution 19 - C#Alex RouillardView Answer on Stackoverflow
Solution 20 - C#tedView Answer on Stackoverflow
Solution 21 - C#samtalView Answer on Stackoverflow
Solution 22 - C#rashiView Answer on Stackoverflow
Solution 23 - C#JamesFaixView Answer on Stackoverflow
Solution 24 - C#KorayView Answer on Stackoverflow
Solution 25 - C#Valeh MikayilzadehView Answer on Stackoverflow
Solution 26 - C#SteveCinqView Answer on Stackoverflow
Solution 27 - C#fcmView Answer on Stackoverflow
Solution 28 - C#LeebeedevView Answer on Stackoverflow