Controles WEB
Controles WEB
Los formularios web (Web Forms) representan la parte más visible de los sitios web ASP.NET y, en consecuencia, la
más popular. Se basan en un reparto de responsabilidades de tipo MVC: modelo, vista, controlador. Cuando se
escribe un formulario utilizando el estilo código independiente, la página HTML .aspx se encarga de la representación
(vista), la clase C# gestiona los datos y los cálculos realizados con ellos (modelo), mientras que el servidor de
aplicaciones ASP.NET coordina el conjunto (controlador). Este análisis resultará familiar, sin duda, a los
desarrolladores Java en lo relativo a la organización de sitios web ASP.NET.
Por otro lado, los formularios web son el resultado de la transposición que realiza Microsoft del modelo Visual Basic 6,
y una forma original y productiva de desarrollar interfaces gráficas para Internet. El éxito de este modelo ha sido tal,
que Sun lo ha replicado por su cuenta en la tecnología de desarrollo web JSF (Java Server Faces).
En el capítulo Los sitios web ASP.NET nos hemos puesto al día con la estructura de una página ASPX desde el punto
de vista de la compilación. Ahora se trata de comprender su estructura lógica.
</div>
</form>
</body>
</html>
Este código está formado por tres partes: una directiva page, una declaración de DTD y código XHTML.
La directiva Page
Las directivas organizan la lectura de una página ASPX en el servidor de aplicaciones. En la página Default.aspx, el
atributo Language define el lenguaje -C#, VB.NET, C++- utilizado para escribir los scriptlets. Hay otros atributos
presentes, que sirven para la comunicación con la página de code behind (AutoEventWireup, CodeFile, Inherits),
para aplicar temas, para la gestión de trazas... Descubriremos el uso de estos atributos conforme avance nuestro
estudio.
Por suerte, Visual Studio proporciona distintos atributos aplicables utilizando la combinación de teclas [Ctrl]
[Espacio].
Existen otras directivas disponibles para incluir recursos en el entorno de la página: estrategia de caché,
componentes, ensamblados, tipos de página maestra...
Las DTD
Las definiciones de tipo de documento (Document Type Definition) las establece el consorcio W3C. Se trata de una
norma aplicable a los documentos SGML, XML y HTML que fija las reglas sintácticas y semánticas de la construcción
de un documento basado en tags (marcadores).
Los navegadores son bastante tolerantes en lo que respecta a las DTS. Con la versión ASP.NET 1.X, el flujo HTML de
salida es compatible con la DTD HTML transicional de nivel 4. Salvo el atributo MS_POSITIONNING que no estaba
filtrado, el código HTML era completamente estándar. Es cierto que una página ASPX contiene etiquetas especiales
(<asp:label>, por ejemplo) que se traducen por una secuencia HTML accesible desde el navegador.
La versión 2.0 aporta una conformidad con XHTML, una declaración mucho más estricta del lenguaje HTML. Los
puristas pueden dirigirse al sitio web de W3C e introducir una página ASP.NET en el motor de verificación ubicado en
la dirección http://validator.w3.org. Las páginas deben estar en conformidad con la DTD correspondiente.
Para el desarrollador de páginas web, la conformidad con una versión específica del lenguaje HTML no es suficiente
para garantizar que una página tenga la misma presentación sea cual sea el navegador. De entrada, los
navegadores tienen la responsabilidad de interpretar las reglas de representación tal y como ellos las entiendan. El
lenguaje HTML describe el contenido, pero no la representación. Además, las páginas incluyen código JavaScript y
estilos CSS, que difieren en su interpretación en función del navegador.
El servidor ASP.NET 2.0 ha introducido otro cambio: desaparece la noción de esquema de navegador de destino. Es
cierto que esta directiva no ha podido estar a la par con la evolución de los navegadores, sin contar con la aparición
de otros dispositivos de navegación. En su lugar, los sitios web ASP.NET poseen una carpeta App_Browsers que
considera las características de cada navegador. Este aspecto se estudiará cuando aparezcan los componentes
personalizados.
Para ciertos navegadores y programas JavaScript que intervienen en el DOM y que no sean compatibles con la
norma XHTML, el servidor de aplicaciones puede configurarse para utilizar el modo HTML transicional. La directiva se
ubica en el archivo Web.config:
<xhtmlConformance mode="Legacy"/>
El código XHTML
Si bien es cierto que el servidor de aplicaciones ASP.NET 1.X emitía un flujo conforme a la DTD HTML 4 transicional, la
propia sintaxis de las páginas ASPX mezclaba secuencias HTML con secuencias XML. Visual Studio 2003 se
encargaba de controlar la coherencia del conjunto y generar advertencias cuando era necesario, y el servidor de
aplicaciones debía realizar una lectura más atenta (y costosa) para separar las secuencias HTML de las secuencias
XML.
<html xmlns="http://www.w3.org/1999/xhtml"
>
Dicho de otro modo, las etiquetas de una página ASPX deben respetar la sintaxis XHTML. De este modo, las
etiquetas que comienzan por asp (controles web), uc (controles de usuario) o cc (controles personalizados) no
forman parte del vocabulario XHTML. Pero, al menos, la sintaxis es mucho más próxima y más precisa. Y el flujo de
salida permanece, en cualquier caso, conforme a la DTD declarada.
Por último, Visual Studio hace todo lo posible para validar de antemano las secuencias HTML que figuran en una
página ASPX. Se generan mensajes de advertencia para llamar la atención del desarrollador acerca de las no
conformidades.
La organización de una página dinámica es una simple cuestión de estilo. Según la naturaleza de la secuencia
HTML que se quiera describir, es preferible optar por la versión anidada o por la versión en línea (inline). Solo el
estilo separado (code behind) supone un cambio radical y aporta una distinción neta entre la presentación y el
cálculo. Éste es el motivo por el que se le da privilegio en Visual Studio.
El estilo anidado
Son las primeras generaciones de las páginas dinámicas (ASP, PHP) las que imponen el estilo anidado. Con los
modelos de componentes web ASP.NET, deja de tener tanto sentido, aunque sigue siendo aplicable. También
puede servir en el caso de controles a base de modelos tales como los Repeater o los Data List.
<body>
<form id="form1" runat="server">
<ul>
<%
int i;
string[] dias = { "lunes", "martes", "miércoles", "jueves",
"viernes", "sábado", "domingo" };
for(i=0; i< dias.Length; i++)
{
%>
<li><%= dias[i] %></li>
<% } %>
</ul>
</form>
</body>
El desarrollador debe trabajar de la mejor forma para alinear su código como si se tratase de un programa escrito
completamente en C#.
</script>
El límite entre ambas secciones de código es responsabilidad del desarrollador, quien tiene la libertad para
respetar o no esta división.
El estilo separado
Cuando el procesamiento es complejo, no es recomendable ubicarlo en una página HTML. La calidad del desarrollo
se verá afectada. El estilo separado (también llamado code-behind) funciona tal y como hemos descrito en el
capítulo Los sitios web ASP.NET: la vista HTML forma una clase que hereda de la clase C# que figura en el archivo
de código subyacente. Esto explica por qué los controladores de eventos están cualificados mediante la palabra
reservada protected (miembro accesible por las subclases).
Más allá de una organización más clara, la clase de código subyacente impone también una cronología de eventos.
Para implementar un procesamiento, el desarrollador debe determinar dónde debe ubicarse su código. Para
responder a esta cuestión, hay que preguntarse cuándo debe ejecutarse el código. A cada momento le corresponde
un evento (init, load, click...).
b. Los scriptlets
Los scriptlets son fragmentos de código que figuran en una página ASPX. Están delimitados por marcadores, que
los distinguen de las secuencias HTML.
<% instructions %> Instrucciones ejecutadas de arriba a abajo, anidadas en el código HTML.
<%# expression %> Expresión evaluada cuando se invoca el método de la página o del control
DataBind() (véase el capítulo El acceso a datos con ADO.NET).
<%$ expression %> Expresión analizada en tiempo de compilación de la página y evaluada tras
cada petición.
Estos bloques de instrucciones se ejecutan durante la visualización de la página. Pueden influir en la fabricación de
secuencias HTML, tal y como muestra el siguiente ejemplo de código anidado:
<%
int i;
string[] dias = { "lunes", "martes", "miércoles", "jueves",
"viernes", "sábado", "domingo" };
for(i=0; i<dias.Length; i++)
{
%>
<li><%= dias[i] %></li>
<% } %>
Se generarán exactamente siete etiquetas <li>...</li>, tantas como iteraciones del bucle for.
Las expresiones que figuran entre <%= y %> se evalúan sistemáticamente en el contexto de ejecución. Puede
tratarse de valores literales, de variables o de llamadas a métodos.
Desde un punto de vista sintáctico, podemos considerar las expresiones anidadas <%# %> como una variación de las
expresiones sistemáticas <%= %>. Ciertos controles, tales como las listas o las tablas de datos, iteran sobre
registros con datos. El origen de los datos se enlaza a estos componentes mediante su propiedad DataSource y,
a continuación, se invoca el método DataBind(). Esto establece el orden de resolución de las expresiones <%
# %>que hacen referencia a las columnas del origen de datos:
El estudio de los controles de datos (véase el capítulo El acceso a datos con ADO.NET) y de los controles basados en
un modelo (véase la sección Componentes personalizados, en este capítulo) detalla esta sintaxis, algo
compleja.
Las expresiones ligadas son útiles a la hora de acceder a las bases de datos. Aunque estas expresiones no se
evalúan hasta el momento en que se produce la llamada al método DataBind(). Pueden aparecer errores de
contexto que se producen demasiado tarde como para ser corregidos.
Además, las expresiones <%= %> no pueden figurar como valor de atributo, de modo que la siguiente línea sería
incorrecta:
Para satisfacer ambos requisitos, Microsoft ha dotado a ASP.NET de las $-expressions. Se trata de expresiones de
análisis en tiempo de compilación, que limitan el riesgo de errores contextuales y que pueden figurar como valor de
un atributo.
El servidor de aplicaciones ASP.NET y Visual Studio explotan, ambos, las $-expressions. Ciertas expresiones
estándar se reconocen directamente en Visual Studio y el desarrollador las aprovecha sin tener que introducir
código; la propiedad (Expressions) es accesible mediante ciertos controles web y reemplaza al anterior sistema de
propiedades dinámicas.
Parent Control que contiene el control en curso, si existe (la Page no tiene control
Parent).
Visible Si esta propiedad vale falso, la renderización HTML del control no se realiza, y
tampoco para los controles siguientes (anidados).
Una página es, por tanto, un control un tanto particular. Es preferible considerar que su colección Controls es el
punto de partida de la jerarquía. Ésta puede, por otro lado, mostrarse activando la traza de un formulario web:
En tiempo de ejecución de la página, se inserta la traza en el flujo HTML, permitiendo seguir el desarrollo de la
petición, y visualizar la jerarquía de controles:
Es, por otro lado, interesante subrayar que las etiquetas HTML forman parte del árbol como controles literales. El
servidor de aplicaciones les asigna, a cada una, un nombre diferente, pero difícil de prever. A este propósito, la
clase Control posee un método FindControl que permite buscar un control a partir de su nombre. Esta operación
se produce cuando no es posible determinar el nombre de un control en tiempo de compilación.
El siguiente programa ilustra cómo crear dinámicamente un botón capaz de responder a un clic.
public partial class ct_dinamico : System.Web.UI.Page
{
private Button b;
b.ID = "boton";
La clase Page expone varias propiedades públicas que llamaremos objetos intrínsecos. Estos objetos se
corresponden, de hecho, con los miembros del contexto http del capítulo Los sitios web ASP.NET. De este modo,
Page.Request es idéntico a HttpContext.Current.Request.
Tres de estos objetos ya estaban disponibles antes de ASP.NET: Request, Form y Response. En el marco del
estudio de los formularios web, vamos a presentar estos tres objetos en detalle.
Request
El objeto Request representa a los parámetros enviados desde el navegador al servidor cuando se produce una
petición de tipo GET. En los formularios web ASP.NET, las peticiones GET se corresponden, a menudo, con enlaces
simples de hipertexto. En el caso de una petición de tipo POST (es decir, un postback en ASP.NET), el objeto
Request puede, también, incluir información de tipo parámetro: se trata de la cadena de interrogación Query
String.
Esta cadena figura en la URL tras el nombre de la página .aspx. Comienza con un signo ?y está formada por una
lista de pares clave=valor separados por el símbolo &.
http://localhost/ventas/articulo.aspx?id_articulo=15
Para determinar qué ficha de producto debemos presentar, la página articulo.aspx decodifica la cadena de
interrogación mediante la expresión:
Request.QueryString["id_articulo"]
⚫ Dirección (URL).
⚫ Agente (Browser).
Incluso si la clase Page y los controles web tienen la potencia suficiente como para presentarnos la petición desde un
enfoque de alto nivel, el desarrollador puede contar con el objeto Request para controlar con detalle las
condiciones de ejecución de una página ASPX.
Form
La clase Page posee una propiedad Form de tipoHtmlForm que representa a la etiqueta <form>; existe un
segundo objeto Form, propiedad esta vez del objeto Request.
Este último objeto, de tipo NameValueCollection, representa los datos enviados. Con la decodificación de los
controles web de servidor existentes, es raro acceder a dicho objeto en ASP.NET, aunque siempre es una posibilidad a
la hora de construir extensiones o para mantener un formato compatible con ASP.
Response
En ASP, el objeto Response se empleaba para escribir en el flujo de salida HTML sin tener que salir de la
programación. Este procedimiento no ha variado con ASP.NET, puesto que los controles web son capaces de
asegurar su propia visualización.
No obstante, el objeto Response sigue estando disponible para escribir otro tipo de flujo -PDF, Excel a partir de una
página ASPX. Además, el objeto Response se utiliza en la gestión de la caché HTML.
a. El ciclo nominal
Una página dinámica ASPX es un programa que se ejecuta según un proceso basado en eventos. Antes de
detallar las diferentes señales que puede recibir un programador para diseñar su programa, veremos este proceso
a grandes rasgos.
Se trata de la primera fase del proceso de ejecución de una página. El framework instancia la página e inicializa
sus controles. El ViewState se carga y, a continuación, se decodifica. Esta fase se corresponde con los eventos
FrameworkInitialize, PreInit e Init. El desarrollador rara vez ubica su código de aplicación a este nivel,
pues los controles todavía no existen.
Esta fase se corresponde con el evento OnLoad y con el método Page_Load. A diferencia de los formularios Windows
Winform, el evento OnLoad se produce tras cada petición. Por lo general, el método Page_Load también se
invoca, a menos que el valor del atributo AutoEventWireUp de la directiva <%@ Page %> tenga un valor igual a
false.
Como Visual Studio crea formularios con el atributo AutoEventWireUp con el valor true, y genera el método
Page_Load(), el código de usuario prácticamente siempre se ubica en este método y no en OnLoad.
ASP.NET dispone de controles integrados para controlar los datos introducidos por los usuarios, según los
formatos estándar (campos obligatorios, fechas bien formadas, números, e-mails...). Estos controles se validan, en
primer lugar, mediante JavaScript. Además, el servidor de aplicaciones les aplica una validación adicional del lado
servidor. Por último, la página dispone de una propiedad IsValid para determinar si se han realizado con éxito
todas las verificaciones.
Gestión de eventos
Los eventos son clases ubicadas en cuatro categorías que se producen en el orden siguiente:
⚫ Eventos que se producen al inicio de un retorno de una llamada a una función JavaScript___doPostBack().
⚫ Eventos de cambio de ubicación en caché, a menos que la propiedad AutoPostBack no valga true.
El desarrollador incluye, habitualmente, controladores para cada uno de estos eventos en el archivo de código
subyacente. Al finalizar las distintas operaciones, se invocan los métodos de enlace, se fija el estado de la vista
ViewState, y se produce la visualización de la página.
Justo antes de la transformación del modelo C# en una secuencia HTML, se produce el evento PreRender: se
trata de una señal que indica que las nuevas modificaciones en el modelo C# ya no se tendrán en cuenta. A
menos que el evento Render no se controle, para ciertos tipos de controles (por ejemplo un calendario en el que
se quiere modificar la apariencia de los días festivos), se produce la visualización en base a los valores de los
atributos y propiedades de los distintos componentes de la página.
Liberación de recursos
Mientras el servidor web IIS dirige la página HTML al navegador, el servidor de aplicaciones libera los recursos. La
instancia de Page se destruye, todos sus campos pierden su valor y el recolector de basura (garbage collector)
recupera la memoria de forma periódica.
A menudo se comprueba la negación de esta propiedad para saber cuándo hay que inicializar los controles de la
página.
Sin esta precaución, la lista ListBox1 se incrementaría de tres en tres ítems tras cada ejecución de la página.
Las páginas ASP.NET tienen como principal objetivo exponer controles web. Éstos interactúan con el usuario
aceptando datos introducidos manualmente y mostrando sus valores. Microsoft ha definido un verdadero DOM
(Document Object Model) del lado servidor, lo que permite explotar estos controles de una forma mucho más
productiva que los objetos Request y Response. De este modo, el objetivo del programador de páginas ASP.NET no
es crear código HTML "a mano" sino crear un servicio funcional de cara al usuario. Los controles web, más o menos
integrados, tienen la responsabilidad de decodificar aquellos elementos de las peticiones que les afectan y fabricar,
bajo demanda, parte de la respuesta que se devuelve al navegador.
Tras las primeras versiones del framework, el código 100% HTML ha encontrado su lugar. Microsoft parece haber
abandonado la representación absoluta (top, left) mediante CSS, sugerente dado que es similar a los métodos de
diseño utilizados en Windows, pero del todo desalineada con las restricciones en la creación de sitios web. Este
modo de crear pantallas sigue siendo aplicable, pero Visual Studio 2005 invita al programador a diseñar sus
páginas creando un reparto del espacio con la ayuda de tablas (<table>) y de divisiones (<div>).
Las etiquetas HTML se integran en el DOM bajo la forma de controles LiteralControl. Es, de este modo, posible
agregar nuevas secuencias HTML instanciando esta clase, pero esta operación tiene, a menudo, lugar en el
método de visualización del control personalizado.
Cuando se edita una página ASPX en modo Diseño, Visual Studio trabaja como un procesador de texto web: los
retornos de carro se convierten en etiquetas <br>, la negrita ([Ctrl] B) delimita el texto mediante los tags <b>...
Sin esperar grandes maravillas en lo relativo a la ergonomía, los menús y los cuadros de herramientas resultan
bastante prácticos para diseñar una página HTML. Los más valientes pasarán a la vista Código para editar
directamente el código HTML.
En la práctica, se combinan, habitualmente, ambos enfoques. A menudo, el diseño general se realiza con ayuda
del ratón en modo Diseño, mientras que las definiciones se realizan introduciéndolas directamente en las
etiquetas. También es habitual utilizar otras herramientas de diseño gráfico de páginas web, tales como
Dreamweaver, y programar el funcionamiento de las páginas con Visual Studio.
En cuanto al cuadro de herramientas de Visual Studio, está más orientado a los controles que a las etiquetas. Éste
es el motivo por el que la sección HTML no contiene prácticamente campos de formulario HTML:
a. El atributo runat="server"
Consideremos la siguiente página HTML:
Se trata de una página ASPX que incluye un formulario, el cual contiene dos campos HTML nombre y boton1. Si
quisiéramos determinar el valor de estos campos, podríamos hacerlo mediante un scriptlet o en el code-behind. En
ambos casos, solo el objeto Request estaría disponible para esta opción:
<%
string p = Request["nombre"]; string b =
Request["boton1"]; if (b != null && b != "")
Response.Write("El nombre es "+ p);
%>
Este código, representativo del estilo ASP, dista mucho de ser cómodo; la decodificación de la petición, la detección
del clic en el botón, son operaciones que podrían sistematizarse. De hecho, el 90% del código de una página web
dinámica prácticamente se duplica de página en página y de proyecto en proyecto. Éste es el motivo por el que
Microsoft define los controles de servidor.
El atributo runat="server" indica al framework ASP.NET que las etiquetas correspondientes adoptan un
funcionamiento en el servidor o, dicho de otro modo, la decodificación de sus valores es sistemática y el
programador puede aplicarla a sus tareas.
Como los controles de servidor heredan de la clase System.Web.UI.Control, la presencia del atributo
runat="server" está ligada al atributo ID (identificador), que reemplaza al atributo HTML name.
Desde dicho momento, el control se vuelve accesible a través del objeto instancia de una clase que deriva de
HtmlControl. Es, entonces, posible trabajar con este objeto desde el code-behind:
Los controles HTML exponen menos propiedades que los controles web. Los métodos y eventos disponibles están,
también, menos extendidos. En ASP.NET 1.X, estos controles estaban destinados a facilitar la migración de
páginas ASP. Tras la versión 2.0, solo aquellos que no tienen un equivalente en la categoría de controles web de
servidor presentan, todavía, cierto interés.
Los controles web poseen, también, la propiedad ClientID derivada de la propiedad ID. Cuando se agrega un
control a la colección Controls de la página o del fragmento de página que la instancia, la propiedad ClientID
se calcula para garantizar su unicidad en el seno de la jerarquía. Cuando el componente se manipula mediante
código JavaScript hay que prestar atención y utilizar ClientID, mejor que ID.
El cuadro de herramientas está organizado en pestañas. La primera de ellas contiene los controles más frecuentes
(estándar):
CheckboxList, RadioButtonList, Lista de opciones a marcar, de botones radio o de bullets, asociados con
BulletedList columnas de datos SQL.
Table Tabla dinámica. Es mejor dar preferencia a la tabla HTML clásica para
utilizarla en la página, salvo si las columnas deben ocultarse,
modificarse...
Calendar, AdRotator, FileUpload Calendario, panel publicitario, control para subir archivos: controles
llamados "ricos" dado que generan secuencias HTML complejas.
MultiView Conjunto de paneles donde solo existe uno activo (que se muestra) a la
vez.
Los eventos de los controles web son señales que se emiten desde el navegador -generalmente
haciendo clic sobre un enlace o un botón, aunque existen otras ergonomías- y que provocan
una rellamada a la página: el famoso postback. Esta señal se decodifica y produce un evento en
el control afectado. Se habrán registrado uno o varios controladores para gestionar el evento de
este control, y se les invoca uno tras otro.
Existen dos técnicas a la hora de registrar un controlador. Una de ellas utiliza un atributo, en
la etiqueta, que designa el método que se invoca cuando se produce el evento.
<asp:Button ID="Button1"
runat="server"
OnClick="Button1_Click"
Text="Button" />
w
protected void Button1_Click(object sender, EventArgs e)
{
}
El programador debe respetar, absolutamente, el formato del delegado impuesto por el evento. La
escritura de un método según una supuesta firma es un ejercicio arriesgado. Es preferible utilizar
el modo Diseño de Visual Studio, seleccionar el control, editar sus propiedades ([F4]), pasar a
la visualización de la lista de eventos (botón que representa un relámpago amarillo), y hacer
doble clic en el evento que se quiere manejar.