Retrieve all WinForms controls using generics
Sometimes you just want to grab all controls of a specific type in a Form
(or inside any container like Panel
, GroupBox
, TabPage
, user control, etc.). Typical asks:
“How do I clear all the text boxes on this form?”
“How do I disable every button inside this panel?”
Below are two small approaches:
- Version 1 (easy): a tiny recursive extension method that returns a
List<T>
. - Version 2 (LINQ twist): a composable version that returns
IEnumerable<T>
and avoids building an internal list.
Version 1 — The simple recursive method
We’ll add an extension method on Control
that walks the tree and collects matches. It’s short, readable, and gets the job done.
using System.Collections.Generic;
using System.Windows.Forms;
public static class ControlExtensions
{
public static List<T> GetControls<T>(this Control container) where T : Control
{
var controls = new List<T>();
foreach (Control c in container.Controls)
{
if (c is T)
controls.Add((T)c);
// Recurse into children
controls.AddRange(GetControls<T>(c));
}
return controls;
}
}
Usage
// Clear all text boxes on the current form
this.GetControls<TextBox>().ForEach(tb => tb.Text = string.Empty);
// Disable all buttons inside a specific panel
myPanel.GetControls<Button>().ForEach(b => b.Enabled = false);
Applying several actions
You can centralize formatting/logic in a helper, then call it for each control:
// Apply a consistent look-and-feel to text boxes
this.GetControls<TextBox>().ForEach(ApplyFormat);
private void ApplyFormat(TextBox tb)
{
tb.BackColor = System.Drawing.Color.LightGray;
tb.ForeColor = System.Drawing.Color.Red;
tb.Text = "hello";
tb.TextAlign = HorizontalAlignment.Center;
}
Or inline with a lambda:
this.GetControls<TextBox>().ForEach(tb =>
{
tb.BackColor = System.Drawing.Color.LightGray;
tb.ForeColor = System.Drawing.Color.Red;
tb.Text = "hello";
tb.TextAlign = HorizontalAlignment.Center;
});
Version 2 — A LINQ-powered variant
If you prefer pipelines, we can lean into LINQ by first exposing Control.ControlCollection
as IEnumerable<Control>
, then composing from there. This version doesn’t allocate an internal list; it just streams items.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows.Forms;
public static class ControlLinqExtensions
{
// Make Control.ControlCollection LINQ-friendly
public static IEnumerable<Control> AsEnumerable(this Control.ControlCollection @this)
{
foreach (var control in @this)
yield return (Control)control;
}
// Get all controls of type T (exact type match, see note below)
public static IEnumerable<T> GetAllControls<T>(this Control @this) where T : Control
{
return @this.Controls.AsEnumerable().Where(x => x.GetType() == typeof(T)).Select(y => (T)y)
.Union(
@this.Controls.AsEnumerable()
.SelectMany(child => GetAllControls<T>(child))
.Select(y => (T)y)
);
}
// Optional: ForEach for IEnumerable<T>
public static void ForEach<T>(this IEnumerable<T> @this, Action<T> action)
{
foreach (var t in @this) action(t);
}
}
Usage
// Disable all text boxes on the current form
this.GetAllControls<TextBox>().ForEach(tb => tb.Enabled = false);
// Apply multiple changes inline
this.GetAllControls<TextBox>().ForEach(tb =>
{
tb.BackColor = System.Drawing.Color.LightGray;
tb.ForeColor = System.Drawing.Color.Red;
tb.Text = "hello";
tb.TextAlign = HorizontalAlignment.Center;
});
Notes & tiny improvements
-
The example uses
x.GetType() == typeof(T)
which matches the exact type only.
If you want to include subclasses (e.g., a customMyTextBox : TextBox
), swap the filter to:// Include derived types too return @this.Controls.AsEnumerable() .OfType<T>() // equivalent to "is T" + cast .Union(@this.Controls.AsEnumerable().SelectMany(child => child.GetAllControls<T>()));
-
Returning
IEnumerable<T>
is nice for composability (filter more,Take(n)
, etc.), while theList<T>
variant is convenient for a quick.ForEach(...)
.
Which one should I use?
- Want straightforward and familiar? Use Version 1 (
List<T>
)—it’s very readable and easy to step through in a debugger. - Prefer lazy, pipeline-y code? Use Version 2 (LINQ)—no internal list, and it composes nicely with other LINQ operators.
Either way, you’ll be able to grab controls quickly and apply whatever operation you need.
Happy coding! 😄

Resistance is futile.