Cómo recorrer todos los controles de una página aspx

by Marc Cortada 19/05/2013

Queremos recorrer todos los controles de una página para establecer ciertas propiedades a un determinado valor. Sin embargo, solamente queremos establecer estos valores a controles de un determinado tipo (por ejemplo, las etiquetas Label de la página). La función FindControl del elemento Page nos servirá para encontrar un elemento a partir de su ID, pero no servirá para recorrer varios elementos. Por otra parte, esta función a veces no encuentra los controles dependiendo de cómo estén ubicados dentro de una página. Como se verá más adelante, tampoco servirá la propiedad Controls del objeto Page para recorrer todos los elementos contenidos en una página.

Como añadido a esta entrada y para quién trabaje con forms en lugar de WebForms, la estructura de almacenamiento de los controles es exactamente la misma. Los controles no cuelgan de Form.Controls, si no que cuelgan de la propiedad Controls del contenedor donde están ubicados. Así que lo único que se tendrá que hacer es adaptar este recorrido a los tipos System.Windows.Forms, en lugar de los System.Web.UI(…). Por lo tanto, la idea de esta entrada es válida tanto para Windows Forms como para WebForms, aunque se tendrán que retocar los tipos usados en el código de ejemplo.

Por ejemplo, vamos a establecer por programación los textos de todas las etiquetas (Label) que hay dentro de una página desde una fuente externa (una BD, un XML…). Esta situación puede darse al desarrollar una aplicación multi-idioma, donde el texto de una determinada etiqueta se guarda en distintos idiomas en un recurso externo al código y al archivo de etiquetas (aspx).

Si intentamos recorrer todos los elementos de la propiedad Controls disponible en Page, solamente encontraremos aquellos controles ubicados directamente en la página. Todas aquellas etiquetas que estén incluidas en otros elementos, como un Panel o cualquier otro contenedor de elementos, no las tendremos accesibles desde la propiedad Controls en Page.

Para ello hay que hacer un recorrido de todos los elementos que están en la propiedad Controls de cada control de la página. En nuestro caso, para cada elemento que encontremos, comprobaremos si se trata de un control Label. Si es así, llamaremos a la función que sea necesaria para rellenar el texto y seguiremos recorriendo los controles. Si no es una Label, no haremos nada y seguiremos recorriendo los demás controles.

Si estamos utilizando una máster page y también queremos recorrer los elementos incluídos en ésta, se tendrá que retocar el código para comenzar el recorrido con los controles que hay en la MasterPage (propiedad Controls de MasterPage), en lugar de comenzar el recorrido con los controles que hay en la propiedad Controls de Page tal y como se muestra en el ejemplo. Sin embargo, la idea del recorrido es la misma.

El código que os muestro a continuación recorre todos los controles de una página, evalúa si se trata de una Label y establece el valor de la propiedad Text (está en inglés por un tema de internacionalización).

Algunos comentarios sobre el código:

  • Las listas con tipos genéricos son más eficientes que las no genéricas. Sin embargo, aquí se han utilizado no genéricas porque con las genéricas no se puede utilizar el método AddRange para insertar de golpe todos los elementos de la propiedad Controls. La propiedad Controls no implementa la interfaz IEnumerable<T>.
  • La idea del código es utilizar el ArrayList como una cola. Entonces ¿por qué no he utilizado la cola no genérica? Pues muy sencillo: porque la cola no genérica tampoco permite agregar una lista de elementos de una sola vez.

Como conclusión, se utilizan las listas no genéricas y un Array para poder aprovechar la funcionalidad InsertRange.

        protected void setLabels(System.Web.UI.Page p)
        {
            System.Collections.ArrayList a = new System.Collections.ArrayList();
            a.AddRange(p.Controls);
            System.Web.UI.Control element = null;
            while (a.Count > 0)
            {
                element = (System.Web.UI.Control)a[0];
                a.RemoveAt(0);               
                if (element is Label)
                {
                    ((Label)element).Text = "Get text set from some source, you can use the property ID to get name";
                }
                a.AddRange(element.Controls);
            }
        }

 

Tags:

Categorías: ASP.NET | C# | How to | Programación y desarrollo | Windows Forms

Comentarios (4) -

Steve Lara
30/07/2014 17:40:02 #

Tengo una duda, en el caso de que fueran más controles y no sólo uno.
¿Cuál sería la mejor forma de abordarlos? Es decir, existe alguna forma de acceder a la clase general de los controles o tiene que ser necesariamente uno por uno.
Porque en cuestión de código, sería muy extenso

Marc Cortada
30/07/2014 18:33:42 #

Entiendo que lo que preguntas es cómo recorrer todos los controles y hacer algo no solo en caso de ser una Label, si no en caso de ser una Label o un TextBox o un Check. ¿Es esa tu pregunta?

Steve Lara
30/07/2014 18:46:03 #

Si, verás, tengo un proyecto de una pagina web que usa múltiples controles, no solo label, textbox o check. Entonces mi pregunta es, ¿es necesario un if para cada tipo de control existente (que la verdad son muchos) o hay alguna forma de obtener todos desde un "común denominador", por ejemplo desde la clase "Control" o algún similar.
Digámoslo así, en tu ejemplo recorres todos los controles de la página, cómo obtendrías todos los "textos" mostrados en la página.
En mi caso particular estoy haciendo una página multi-lenguaje y necesito obtener todos los textos que aparecen en pantalla el problema es que no todos son labels.

Marc Cortada
30/07/2014 19:06:25 #

Pues sí, aparentemente lo tienes a tiro.
Todos los controles que contienen texto derivan de la interface ITextControl. Por lo que si cambias Label por ITextControl en el código del ejemplo, ya deberías tenerlo resuelto.
Esto es así porque en programación orientada a objetos, todo objeto de una clase derivada es también objeto de la clase base. En este caso, un objeto de tipo TextBox es también un objeto de tipo ITextControl. Una interface no es instanciable, pero los objetos instanciados desde las clases derivadas de esa interface también son del tipo de la interface por herencia.

No se aceptan más comentarios

Entradas por mes