How to: Obtener controles de un formulario con generics

Nota: Es una pregunta recurrente en los foros de MSDN y como he tenido que contestarla en varias ocasiones, me he decidido a hacer este post para en futuras preguntas, poder referenciarlo como respuesta en lugar de contestar una y otra vez (no es que cueste demasiado pero con la edad uno se vuelve más vago :-P).

Pregunta: Cómo recorrer todos los TextBox de un formulario (incluidos los contenedores de controles) y vaciar su contenido.

Respuesta: Mediante el uso de generics, esto es tan sencillo como crear un método extensor de la clase Control, que devuelva una colección de los controles de un tipo determinado. Posteriormente, usamos esta colección para realizar alguna acción sobre cada uno de los elementos devueltos.

Método extensor:

   1: public static List<T> GetControls<T>(this Control container) where T : Control

   2: {

   3:   List<T> controls = new List<T>();

   4:   foreach (Control c in container.Controls)

   5:   {

   6:     if (c is T)

   7:       controls.Add((T)c);

   8:     controls.AddRange(GetControls<T>(c));

   9:   }

  10:   return controls;

  11: }

Básicamente recorre la colección de controles del tipo suministrado en tiempo de compilación, llamándose además recursivamente para cada control contenedor que contiene.

Cómo se usa:

   1: this.GetControls<TextBox>().ForEach(p => p.Text = string.Empty);

Se llama al método extensor para el formulario (si, un formulario hereda de ContainerControl, que a su vez hereda de ScrollableControl, y éste de Control). Dicho de otro modo, un objeto Form va a implementar también el método GetControls. En tiempo de compilación se especifica el tipo de la colección de objetos a buscar y retornar (*), y posteriormente se aplica una acción sobre cada uno de los elementos devueltos mediante el método ForEach.

(*) Evidentemente si deseamos devolver todos los botones del formulario basta sustituir TextBox por Button en el ejemplo anterior.

Por otro lado, si deseamos devolver sólo los controles contenidos dentro de un contenedor específico, por ejemplo un GroupBox, Panel o TabControl, basta con llamar al método extensor para el ese control determinado:

   1: this.GroupBox1.GetControls<TextBox>().ForEach(p => p.Text = "hola");

   2: this.Panel1.GetControls<TextBox>().ForEach(p => p.Text = string.Empty);

Para terminar, en el caso anterior sólo vaciamos el contenido del control, es decir realizamos una sola acción sobre el control. En el caso que deseemos realizar varias acciones para un control, basta con llamar a un método que reciba un parámetro del tipo deseado y realice las acciones oportunas:

   1: this.GetControls<TextBox>().ForEach(p => ApplyFormat(p));

   2:  

   3: private void ApplyFormat(TextBox text)

   4: {

   5:     text.BackColor = Color.LightGray;

   6:     text.ForeColor = Color.Red;

   7:     text.Text = "hello";

   8:     text.TextAlign = HorizontalAlignment.Center;

   9: }

O bien, utilizar un ‘Action Delegate’ para tener el código ‘inline’, ambas opciones son equivalentes.

   1: this.GetControls<TextBox>().ForEach(p => {

   2:                 p.BackColor = Color.LightGray;

   3:                 p.ForeColor = Color.Red;

   4:                 p.Text = "hello";

   5:                 p.TextAlign = HorizontalAlignment.Center;

   6:             });

Saludos y nos leemos!

Leave a Reply

Your email address will not be published. Required fields are marked *