Proyectos de Aplicación Web vs. Proyectos de Sitio Web (Web Application Projects vs. Web Site Projects)

Los proyectos de Aplicación Web hicieron su aparición como un add-in para Visual Studio 2005, luego debido a su gran difusión se incorporó como parte del Service Pack 1 para visual Studio 2005. Provee un modelo de proyecto Web similar al modelo de proyecto Web de Visual Studio 2003. Proporciona la posibilidad de definir una aplicación Web mediante un proyecto (.csproj o .vbproj), tal como era habitual en las versiones previas del .NET Framework, lo cual garantiza que todo el proyecto se compilará en un único ensamblado.

El objetivo de los proyectos de aplicación Web es dar respuesta a las peticiones de los usuarios. Algunos desarrolladores encontraron que la migración de aplicaciones de Visual Studio 2003 hacia el nuevo modelo de sitios web de Visual Studio 2005 presentaba muchos problemas haciéndolo en algunos casos impracticables, especialmente porque la compilación en el nuevo modelo de sitios Web de Visual Studio 2005 creaba múltiples ensamblados.

El nuevo modelo de proyectos de aplicación Web provee la misma semántica de los proyectos Webs de Visual Studio 2003. Esto incluye la estructura basadas en archivos de proyecto y el modelo de compilación donde todo el código del proyecto es compilado en un único ensamblado. Sin embargo, el nuevo tipo de proyecto pone a nuestra disposición todas las nuevas características de Visual Studio 2005 (Refactoring, diagramas de clase, desarrollo de pruebas, genéricos, etc.) y de ASP .NET 2.0 (páginas maestras, controles de datos, membresía e identidad, administración de roles, Web Parts, personalización, navegación de sitio, temas, etc.).

El modelo de proyectos de aplicación Web en visual Studio 2005 también hace innecesario dos requerimientos de Visual Studio 2003.

• Uso de extensiones de servidor de FrontPage. Ahora ya no es un requisito, aunque aún es soportado si el sitio Web aún utiliza estas extensiones.
• Uso de Internet Information Server (IIS). El nuevo tipo de proyectos soporta tanto IIS como el servidor ASP .NET de desarrollo incluido en Visual Studio 2005.

¿Cuál de los tipos de proyectos Web debemos utilizar?
Las siguientes tablas comparativas describen las diferencias entre los dos tipos de proyectos Web.

Opción o tarea

Proyectos de Aplicación Web

Proyectos de Sitio Web

Necesita migrar aplicaciones grandes de Visual Studio 2003.

X

 

Preferencia por el modelo de página simple sobre el modelo de código detrás.

 

X

Preferencia por la compilación dinámica y por trabajar sin generar todo el sitio por cada página modificada.

 

X

Necesita controlar los nombres de los ensamblados de salida.

X

 

Necesita generar un ensamblado por cada página

 

X

Necesita que clases independientes hagan referencia a páginas y controles de usuario Web.

X

 

Necesita generar una aplicación Web utilizando múltiples proyectos Web.

X

 

Necesita agregar pasos antes de generar (pre-build) y después de generar (post-build), durante el proceso de compilación.

X

 

Necesita abrir y editar cualquier directorio, como un proyecto Web, sin crear un archivo de proyecto.

 

X

Escenario

Proyectos de Aplicación Web

Proyectos de Sitio Web

Definición de proyecto

Similar a Visual Studio 2003. Solo archivos que son referenciados en el archivo de proyecto forman parte del proyecto, se muestran en el explorador de soluciones, y son compilados durante el proceso de generación. Debido a que existe un archivo de proyecto, algunos escenarios son más fáciles de implementar.

Se puede excluir archivos del proyecto y del programa de control de versiones con facilidad.

Los Proyectos de Sitio Web utilizan la estructura de directorios para definir los contenidos del proyecto. No existe archivo de proyecto y todos los archivos de la carpeta forman parte del proyecto.

Este tipo de proyecto es deseable si se tiene una estructura de carpetas que representa una aplicación ASP .NET, que se necesita editar en Visual Studio 2005 sin crear explícitamente un archivo de proyecto.

Compilación y resultado de generación.

El modelo de compilación para proyectos de aplicación Web es muy similar al modelo de Visual Studio 2003.

Todos los archivos de clases de código detrás y los archivos de clases independientes en el proyecto son compilados en un único ensamblado, el cual es colocado en la carpeta Bin. Puesto que se trata de un ensamblado único se puede especificar atributos como el nombre y versión del ensamblado, así como la localización del ensamblado de salida.

Otros tipos de escenarios son mejor implementados, como el patrón Modelo Vista Controlador (MVC), puesto que permite que las clases independientes en el proyecto hagan referencia a páginas y controles de usuario web.

El comando de Generación compila el proyecto de sitio Web únicamente para verificar errores de sintaxis. Para ejecutar el proyecto de sitio Web, se despliega los archivos de código fuente y se confía en la compilación dinámica de ASP .NET para compilar las páginas y clases de la aplicación.

Alternativamente, se puede precompilar el sitio para mejorar la performance, para lo cual se utiliza la misma semántica de la compilación dinámica.

Desarrollo iterativo

Para ejecutar y depurar páginas, se necesita generar todo el proyecto de aplicación Web. Generar todo el proyecto de aplicación Web usualmente es rápido, puesto que Visual Studio emplea un modelo de generación incremental, el cual compila solo los archivos que han sido modificados.

Se pueden configurar las opciones de generación de Visual Studio 2005 para cuando se ejecute un sitio web: Generar sitio web, una página individual, o ninguno en absoluto, en el último caso, cuando se ejecuta un sitio Web, Visual Studio simplemente lanza el explorador y le pasa la página actual o la página de inicio. La solicitud luego invoca la compilación dinámica de ASP .NET.

Por defecto, Visual Studio compila el proyecto de sitio Web completo en cualquier momento que se ejecute o depure una página. Esto se efectúa para identificar los errores de tiempo de compilación en cualquier lugar del sitio. Sin embargo una generación completa del sitio puede disminuir significativamente la velocidad del proceso iterativo de desarrollo, por tal motivo se recomienda cambiar las opciones de generación del proyecto para que compile sólo la página actual cuando se ejecute o depure.

Despliegue

Peusto que todos los archivos de clases son compilados en un ensamblado único, solo se necesita desplegar este ensamblado, junto con los archivos .aspx y .ascx, además de otros archivos estáticos de contenido.

En este modelo, los archivos .aspx no son compilados hasta que son ejecutados en el navegador. Sin embargo cuando se utiliza un proyecto de instalación Web, los archivos .aspx también pueden ser compilados e incluidos en el único ensamblado para su despliegue.

Cada vez que se despliegue el ensamblado único producido en este modelo, se reemplaza el código para todas las páginas del proyecto.

Tanto los archivos .aspx como los archivos de código detrás pueden ser compilados en ensamblados utilizando el comando “Publicar Sitio Web” de Visual Studio (El comando “Generar” no crea un conjunto de ensamblados que se puedan desplegar). Las opciones actualizables de publicación soportan únicamente compilación de archivos de código detrás, mientras que deja sin cambios a los archivos .aspx para el despliegue.

El modo de precompilado por defecto produce varios ensamblados en la carpeta Bin, típicamente uno por carpeta. La opción de nombres-fijos produce un ensamblado por página o control de usuario Web y pueden ser utilizadas para crear versiones desplegables de páginas individuales. Sin embargo, la opción de nombres-fijos incrementa el número de ensamblados y puede resultar en un incremento del uso de memoria.

Migración a partir de Visual Studio .NET 2003

Puesto que el modelo de proyecto de aplicación Web es el mismo que el de Visual Studio .NET 2003, la migración es generalmente simple y usualmente no requiere ninguna reestructuración de la aplicación.

La opción de compilación para Proyectos de Sitio Web es significativamente diferente a Visual Studio .NET 2003. Un asistente de conversión está disponible para migrar proyectos web existentes de Visual Studio 2003 a proyectos de sitios Web. Por razones obvias es usual la necesidad de arreglos manuales adicionales después de la conversión.

Para la mayoría de los escenarios, es preferible migrar a proyectos de aplicación Web.

Los cuadros comparativos muestran en detalle las diferencias entre los dos tipos de proyectos, para proyectos nuevos yo prefiero usar el nuevo modelo: Proyectos de Aplicación Web (Web Application Projects), en el caso de tener proyectos ya avanzados con el modelo de Proyectos de Sitio Web (Web Site Projects) no recomiendo la migración al nuevo modelo porque esta labor no es trivial y a menudo hay que realizar cambios manuales.

Puede consultar el artículo original en Ingles en el siguiente enlace: Introduction to Web Application Projects

Anuncios

Evento Load de Página ASPX Solo se Ejecuta la Primera Vez, Cuando se Abre la Página con JavaScript en Modo Modal.

Nuevamente uno de esos problemas que a uno lo ponen a pensar, y como ya es costumbre proviene de una amiga (que bueno que me hagan estas consultas, así tengo material para el Blog).

El Problema
Cuando abrimos una página ASP .Net en modo modal (diálogo), mediante la instrucción de JavaScript:

showModalDialog();

El código que ponemos en el controlador del evento load de la página que estamos abriendo, se ejecuta solo la primera vez que abrimos la página; en las siguientes veces, simplemente no ejecuta nada, es decir no se produce el evento load de la página.

La Solución
Como es costumbre lo primero que hice, fue reproducir el problema, y me pareció bastante extraño el comportamiento que tenía, luego pase a depurar el programa poniendo un punto de interrupción en la primera instrucción del controlador del evento Load de la página, ejecute el programa y como se esperaba se detuvo en el punto de interrupción, desde ahí comencé a ejecutarlo paso a paso presionando la tecla F10, hasta que se muestre la página por completo, luego cerré la página para abrirla de nuevo, y al abrir de nuevo la página, ¡o sorpresa!, ingresó nuevamente al controlador del evento Load donde estaba el punto de interrupción, y donde se suponía ya no debería de entrar. Así que detuve el proceso de depuración, ejecuté el programa y todo funcionó a la perfección.

Le comenté esto a mi amiga y le dije que después de depurar el programa, todo funcionaba a la perfección, pero ella no puedo reproducir lo que yo había logrado. Rato después cuando probé nuevamente el programa, surgió nuevamente el problema y como era de esperar, mi amiga se rió en mi cara.

Así que me puse a buscar la solución definitiva, luego de probar con muchas posibles soluciones y claro después de buscar en San Google, recordé que cuando una página es servida desde la caché de salida, no se ejecuta ningún código del lado del servidor. Así que procedí a desactivar la caché de salida de la página en cuestión, y que creen, eso solucionó el problema definitivamente, resulta que en las páginas que se muestran como diálogo (Modal), la caché de salida se activa de forma automática y claro no hay manera que se ejecute nuestro controlador de evento Load. Para desactivar la caché de salida de una página basta con colocar la siguiente declaración en el archivo apsx de la página:

<%@ OutputCache Location=”None” VaryByParam=”None” %>

Justo después de la directiva @Page.

Error de Codificación al Recuperar Datos con Caracteres Especiales Como la Letra Ñ de Gridview a un TextBox

Como es costumbre este post se origina con la consulta de una amiga, que me solicitó ayuda con un pequeño problema que se le presentó (el problema parecía tan pequeño que hasta se sonrojaba al hacer la consulta).

El Problema
Al recuperar los datos de un campo de la fila seleccionada en un GridView, a un control TextBox, manda caracteres extraños cuando se tratan de letras especiales como la “ñ” o las vocales con tilde, para recuperar los datos se utiliza el siguiente código en el evento RowCommand, del GridView.

protected void GridView1_RowCommand(object sender,
    GridViewCommandEventArgs e)
{
     if(e.CommandName == “Select”)
     {
        //Seleccionamos la Fila Actual
        GridView1.SelectedIndex =
            Convert.ToInt32(e.CommandArgument.ToString());

         //Recuperamos el valor de la segunda celda
        TextBox1.Text = GridView1.SelectedRow.Cells[1].Text;
     }
}

La Solución
Desde el inicio sospeché que el problema se trataba de la codificación de la página, recordé un post que había leído solo hace un momento donde se mostraba la forma de poner la codificación por defecto, seguí los pasos que se indicaban, pero no solucionó el problema.

Entonces hice la prueba cambiando el control TextBox por un control Label, grande fue mi sorpresa cuando todo funcionó a la perfección, entonces el problema estaba en el TextBox que por alguna razón no decodificaba los caracteres especiales.

Luego recordé que había una función para codificar texto que se utilizaba en seguridad, para evitar que el usuario ingrese caracteres especiales HttpUtility.HtmlEncode() y su correspondiente método para decodificarlo HttpUtility.HtmlDecode(). Como el problema es que los caracteres están codificados y por alguna razón extraña que no llego a entender no se decodifica al asignarlo a un TextBox, para solucionarlo tenemos que utilizar HttpUtility.HtmlDecode(), para decodificarlo manualmente.

protected void GridView1_RowCommand(object sender,
    GridViewCommandEventArgs e)
{
    if(e.CommandName == “Select”)
    {
        //Seleccionamos la Fila Actual
        GridView1.SelectedIndex =
            Convert.ToInt32(e.CommandArgument.ToString());

        //Recuperamos el valor de la segunda celda decodificándola
        TextBox1.Text =
            HttpUtility.HtmlDecode(GridView1.SelectedRow.Cells[1].Text);

    }
}

Temas y Skins con CSS para Mostrar una Interfaz de Usuario Consistente en ASP .NET 2.0

Una primera aproximación para lograr un interfaz de usuario consistente en ASP NET son las hojas de estilo CSS (Cascading Style Sheets), que nos ofrecen una gran mejora al centralizar la información sobre el estilo que nuestros Controles Web lucirán en archivos de hoja de estilos, en lugar de estar poniendo la información de estilo directamente en las propiedades de los controles, colocamos el nombre de estilo CSS. La ventaja de esta aproximación es que al cambiar el archivo de hojas de estilo, el cambio se verá reflejado en todos los controles que utilicen dicha hoja de estilos. La desventaja es que aún hay que colocar en cada propiedad de los controles el nombre del estilo CSS.

Para superar las limitaciones de las hojas de estilo CSS, utilizaremos Temas y Skins.

Tema.- Un tema es una colección de valores de propiedades que nos permiten definir la apariencia de páginas y controles, para luego aplicar esta apariencia a través de toda la aplicación Web asegurando la consistencia de la interfaz de usuario.

Los temas están compuestos por un conjunto de elementos como: Skins, hojas de estilo (CSS), imágenes y otros recursos; como mínimo un tema debe contener skins; los temas se crean en carpetas especiales del Sitio Web (App_Themes).

Skin.- Son archivos con extensión .skin y contiene valores para propiedades de controles individuales como GridView, DetailsView, etc. En la definición del skin se indica a qué control le corresponde.

Ejemplo.- El ejemplo que presentamos es bastante sencillo para ver como agregar temas a un Sitio Web.

1. Agregar un nuevo archivo de hoja de estilos, le podemos poner como nombre “Estilos.css”, en este archivo definimos los estilos que tendrá nuestro sitio web por ejemplo:

.SelectedDayStyle
{
    background-color: #41519A;
    font-weight: bold;
    color: White;
}

Así todos los estimos que vamos a necesitar para definir nuestros skins.

2. Luego agregamos un nuevo skin mediante, agregar nuevo elemento de Visual Studio, como nombre le podemos poner “GridView.skin”, visual estudio nos alertara que estamos intentando crear un skin dentro del sitio web, y nos sugiere crear una carpeta especial para los temas a lo cual respondemos que si; si todavía no existía ningún skin, visual Studio creará la carpeta “App_Themes” y dentro de esta carpeta creará un tema con el mismo nombre del skin, y el nuevo skin dentro del tema, el contenido del skin es similar al siguiente:

<asp:GridView runat="server" CssClass="DataWebControlStyle">
     <AlternatingRowStyle CssClass="AlternatingRowStyle" />
     <RowStyle CssClass="RowStyle" />
     <HeaderStyle CssClass="HeaderStyle" />
     <FooterStyle CssClass="FooterStyle" />
     <PagerStyle CssClass="PagerStyle" />  
</asp:GridView>

Todos los estilos a los que se hace referencia en el skin deben estar previamente definidos en el archivo de hojas de estilo.

Este skin se encargará de poner automáticamente los estilos a los GridView de nuestro sitio web.

3. Una vez creado el tema con sus respectivos skins, es hora de aplicar los temas a nuestro sitio web, podemos aplicar los temas página por página o aplicarlo a todo el sitio web, mediante el archivo de configuración, utilizaremos la segunda forma para lo cual tenemos que agregar en el archivo de configuración la siguiente línea, suponiendo que el tema se llama “Estandar”.

<pages styleSheetTheme="Estandar" />

4. Finalmente tenemos que hacer una referencia al archivo de hojas de estilo desde nuestra página:

<head runat="server">
     <title>Demos Skins</title>
     <link href="Estilos.css" rel="stylesheet" type="text/css" />
</head>

Ahora dependiendo de los skins que hayamos creado para los temas al agregar los controles a nuestra página tomarán automáticamente los estilos del tema.

Temas

Descargar Ejemplo: Ejemplo Temas y Skins
Favor de darle clic derecho y guardar destino como…, una vez descargado cambiar la extensión a .zip, y descomprimirlo (todo esto porque no me dejan subir archivos zip).

De la siguiente página se puede descargar un sitio web con algunos temas listos para utilizar: .Net Treats & Tricks

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.

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.