Archive for 30 mayo 2007|Monthly archive page

Un Momento de Reflexión (Reflection) en la Vida Diaria

Esta vez un problema que se me presento me obligó a reflexionar, reflexionar y reflexionar…, el caso es el siguiente:

Tengo una clase que representa a una entidad, mas específicamente hablando la clase es la que se genera utilizando la plantilla NetTiers para CodeSmith, que se corresponde uno a uno con las tablas de la base de datos y que expone mediante sus propiedades las columnas de la tabla correspondiente, lo que necesito es cargar automáticamente las propiedades de la clase a sus correspondientes controles (TextBox) de un WinForm.

Necesito crear un método general que funcione con cualquier tipo de entidad, y como no utilizaré DataBinding, lo que se me ocurrió es utilizar la propiedad Tag que tienen todos los controles para poner ahí el nombre de la propiedad de la clase entidad que le corresponde.

El siguiente paso es recorrer las propiedades del objeto entidad e ir copiando los valores en el componente que le corresponde, pero ¿cómo recorrer las propiedades de una clase?; esto fue lo que me llevo a la Reflexión.

Reflexión.- “La reflexión es uno de los pilares de .NET. Esta característica permite almacenar y obtener información en tiempo de ejecución sobre casi cualquier objeto o tipo presente en un módulo. Es gracias a esto que es posible implementar técnicas fundamentales como la recolección de basura o la serialización en distintos formatos. Y aunque es cierto que la mayoría de los entornos de programación modernos proporcionan algún tipo de RTTI (runtime type information, el pariente pobre de la reflexión), nunca antes se había visto un uso tan extenso y generalizado de este recurso como en .NET.”

La definición anterior la tomé prestada de la página de uno de mis maestros Ian Marteens, les recomiendo que consulten un artículo muy ilustrativo con ejemplo incluido sobre reflexión en el siguiente link: Calling Dr. Marteens: Reflexión.

Utilizando Reflexión podemos recorrer con facilidad las propiedades de una clase:

public void CargarDatosAControles(Object entidad, Type tipo,
    Control contenedor)
{
    foreach (PropertyInfo info in tipo.GetProperties())
    {
        Attribute atributo = Attribute.GetCustomAttribute(info,
            typeof(DataObjectFieldAttribute));
        if (atributo != null)
        {
            foreach (Control control in contenedor.Controls)
            {
                 if (control.Tag != null &&
                    control.Tag.ToString() == info.Name)
                {
                    control.Text = info.GetValue(entidad,
                        null).ToString();
                }//if
            }//foreach
        }//if
    }//foreach
}

El método que se presenta en el ejemplo, utiliza tres parámetros, el primero es una referencia a un objeto entidad, el segundo es el tipo de la clase entidad y el tercer parámetro es la referencia a un control que contiene a los controles (TextBox) donde queremos cargar los datos de la clase entidad, por ejemplo un Panel.

Para comenzar podemos obtener información de las propiedades de un tipo utilizando el método GetProperties() que nos devuelve una colección de objetos PropertyInfo que contiene la información de todas las propiedades de un tipo en este caso una clase entidad.

Un problema que se me presento es que cuando recorres las propiedades de un tipo, el método GetProperties() devuelve todos las propiedades, y resulta que la clase entidad con la que estoy trabajando aparte de las propiedades que se corresponden con las columnas de la tabla de base de datos que representa, tiene mas propiedades.

Entones tengo que discriminar cuales son las propiedades que corresponden a una columna de una tabla de base de datos y cuales no. Para hacer eso tengo que utilizar la información que proporcionan los atributos que se asocian a la declaración de las propiedades en el código fuente. Revisando el código de la clase entidad encuentro que las propiedades que me interesan tienen asociado a ellas un atributo llamado DataObjectFieldAttribute que indica que la propiedad es un campo de datos, la definición de la entidad luce así:

[DataObjectField(false, false, false, 300)]
public virtual System.String RazonSocial

Lo cual indica que la propiedad RazonSocial es un campo de datos, por los 4 parámetros del constructor del atributo podemos deducir que: no es clave primaria, no es un campo de identidad es decir autogenerado, no acepta valores nulos, y tiene una longitud de 300.

Ahora regresemos a nuestro método CargarDatosAControles, en el cual tenemos la siguiente instrucción:

Attribute atributo = Attribute.GetCustomAttribute(info,
    typeof(DataObjectFieldAttribute));

El código anterior obtiene un atributo de tipo DataObjectFieldAttribute a partir del objeto PropertyInfo de una propiedad.

Luego verificamos que el atributo obtenido sea diferente de null lo cual indica que la propiedad posee ese atributo y por tanto es un campo de datos, solo así procedemos a recorrer los controles del contendedor y buscar a que componente (TextBox) le corresponde la propiedad que estamos tratando, obtenemos el valor de la propiedad a través del método GetValue(entidad, null) de la clase PropertyInfo, para lo cual necesitamos como primer parámetro una instancia de la clase entidad y como segundo parámetro le pasamos null porque no estamos tratando con propiedades indexadas.

La manera de llamar al método seria la siguiente:

CargarDatosAControles(cliente, typeof(Cliente), panelControl1);

De manera similar se pude implementar el otro método que hace la operación inversa CargarDatosAClases.

Anuncios

Cómo Buscar Controles de ASP .Net con JavaScript Dentro de ContentPages

Ayer como a medio día, uno de mis viejos amigos, (uhmm esa ultima frase no me gusto mucho, me recuerda que estoy cada vez más viejo) me visitó y me comentó que tenia un problema cuando trabajaba con Master Pages y que no lograba que funcione una llamada a un calendario utilizando JavaScript.

Como yo no soy de escapar a estos desafíos, me dispuse a revisar el código y le dije a mi amigo, esto lo soluciono en un par de minutos, luego de media hora de hacer todas las pruebas posibles, tuve que decirle (no tengo la más mínima idea de cómo solucionar esto) que esto necesita ser revisado con paciencia y que lo solucionaría cuando tenga más tiempo.

El problema
Ahora que ya se fue mi amigo, comienzo a depurar con más calma el programa y localizo la fuente del problema, cuando se ejecuta el código desde un content page, la función de JavaScript:

document.getElementById(‘txtFecha’).Value

Devuelve null, es decir no encuentra el control txtFecha que como supondrán es un control TextBox, lo extraño es que cuando se ejecuta el mismo código desde una página Aspx normal que no utiliza master pages, funciona a la perfección.

Al parecer cuando se realiza la combinación del master page con el content page ya no se utilizan los nombres (ID) que les colocamos a los controles de nuestro formulario, esto para evitar posibles conflictos de nombres entre los controles que están en el master y content page.

La solución
Después de buscar información en muchas páginas y de probar infructuosamente muchas posibles soluciones, llegue a la conclusión de que como la función no encontraba el nombre (ID), que le había asignado al control, tendría que buscarlo por otro nombre, y ese nombre es el que esta en la propiedad ClientID de los controles. El valor de ClientID es generado automáticamente concatenando el valor de la propiedad (ID) del control con el valor de la propiedad UniqueID del control padre.

Un pequeño ejemplo
Suponiendo que tenemos un Content Page que se ha creado a partir de un Master Page, y que colocamos dentro un TextBox de nombre: txtFecha y un Button, en la página de código tendremos el método que maneja el evento click del botón:

protected void Button1_Click(object sender, EventArgs e)
{
    string codigo = “document.getElementById(‘”+
        txtFecha.ClientID + “‘).value = ‘Hola'”;
    ClientScript.RegisterStartupScript(GetType(), “prueba”, codigo);
}

Noten que como parámetro de la función document.getElementById utilizamos la propiedad ClientID del TextBox. El fragmento de código anterior solo coloca en la propiedad Text del control txtFecha el valor de “Hola”.

De esta manera solucionamos el problema de los nombres de los controles cuando trabajamos con JavaScript y Master Pages.