Gettings all controls from a container v2
A better approach
In my previous post I created a recursive function to retrieve all controls inside a form and its containers. Today, my colleague and friend Eduard Tomàs sent me another solution to the same topic based on LINQ. It’s simply and really, really pretty ❤️
The power of LINQ
Before starting, we need to solve this: LINQ works over IEnumerable, but Control.Controls property returns a ControlCollection type. In fact, nowadays, since we have Generics this class has no sense (like other 1.000 similar classes) but remember that Generics doesn’t appeared until .NET Framework 2.0. So, our first step will be retrieve an IEnumerable from a ControlCollection property of a Control. This could be accomplished using an extensor method over the class ControlCollection:
1
2
3
4
5
|
public static IEnumerable<Control> AsEnumerable(this Control.ControlCollection @this)
{
foreach (var control in @this)
yield return (Control)control;
}
|
Note that using this method we are able to transform CollectionControl to IEnumerable and get full access to the power of Linq. Now, let’s create a method to retrieve all controls of a type:
1
2
3
4
5
6
7
8
|
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(
x => GetAllControls<T>(x)).Select(y => (T)y));
}
|
It looks cool, isn’t it? No loops, no ifs… only pure LINQ power! :-)
Note that converting Controls property into an Enumerable allow us to use all those IEnumerable LINQ methods like Select, Where, Union or SelectMany… in just a single line of code! This is amazing, this is functional programming style!
Applying several actions
There’s also a small difference with my original solution. In the previous one I used an internal List to copy all controls references. The new one only iterates over the original collection. There’s no internal lists.
Another difference present in LINQ solution is we are returning an IEnumerable, so we loose the ForEach method (because IEnumerable doesn’t implements this method). But building our own ForEach method is as trivial as:
1
2
3
4
|
public static void ForEach<T>(this IEnumerable<T> @this, Action<T> action)
{
foreach (T t in @this) { action(t); }
}
|
Now, we’ve all the necessary ingredients to get all controls and do whatever we need :D
1
2
3
4
5
6
7
8
9
|
this.GetAllControls<TextBox>().ForEach(p => p.Enabled = false);
this.GetAllControls<TextBox>().ForEach(p =>
{
p.BackColor = Color.LightGray;
p.ForeColor = Color.Red;
p.Text = "hello";
p.TextAlign = HorizontalAlignment.Center;
});
|
HYEI, happy coding! :-)
November 2010