8. Componentes del servidor ASP - 2
8.1. Introduction
Continuamos nuestro trabajo en la interfaz de usuario descubriendo:
- componentes de validación de datos
- cómo vincular datos a componentes de servidor
- los componentes de servidor HTML
8.2. Los componentes de validación de datos
8.2.1. Introducción
En las aplicaciones con formularios, es fundamental comprobar la validez de los datos introducidos en ellos. En el ejemplo sobre el cálculo de impuestos, teníamos el siguiente formulario:

Al recibir los valores de este formulario, debemos comprobar la validez de los datos introducidos: el número de hijos y el salario deben ser números enteros positivos o cero. Si no es así, el formulario se devuelve al cliente tal y como se ha introducido, con mensajes que indican los errores. Habíamos tratado este caso con dos vistas, una para el formulario anterior y otra para los errores:

ASP.NET ofrece componentes denominados «componentes de validación» que permiten verificar los siguientes casos:
Componente | Función |
comprueba que un campo no esté vacío | |
comprueba si dos valores coinciden | |
comprueba que un valor esté comprendido entre dos límites | |
comprueba que un campo cumpla una expresión regular | |
permite al desarrollador establecer sus propias reglas de validación; este componente podría sustituir a todos los demás | |
permite agrupar los mensajes de error generados por los controles anteriores en un único lugar de la página |
A continuación, vamos a presentar cada uno de estos componentes.
8.2.2. RequiredFieldValidator
Creamos la siguiente página [requiredfieldvalidator1.aspx]:
![]() |
n.º | nombre | tipo | propiedades | función |
1 | TextBox | EnableViewState=true | campo de entrada | |
2 | RequiredFieldValidator | EnableViewState=false EnableClientScript=true ErrorMessage=El campo [nombre] es obligatorio | componente de validación | |
3 | Botón | EnableViewState=false CausesValidation=true | Botón [submit] |
El campo [RequiredFieldValidator1] sirve para mostrar un mensaje de error si el campo [txtNom] está vacío o contiene una secuencia de espacios. Sus propiedades son las siguientes:
campo cuyo valor debe ser comprobado por el componente. ¿Qué significa el valor de un componente? Es el valor del atributo [value] de la etiqueta HTML correspondiente. Para un [TextBox] será el contenido del campo de entrada; para un [DropDownList], el valor del elemento seleccionado. | |
booleano: si es verdadero, indica que el contenido del campo anterior también debe validarse en el lado del cliente. En este caso, el navegador solo enviará el formulario si este no contiene errores. No obstante, las comprobaciones de validación también se realizan en el servidor, por si acaso el cliente no fuera un navegador, por ejemplo. | |
El mensaje de error que el componente debe mostrar en caso de detectarse un error |
La verificación de los datos de la página mediante los controles de validación solo se realiza si el botón o el enlace que ha provocado el [POST] de la página tiene la propiedad [CausesValidation=true]. [true] es el valor por defecto de esta propiedad.
Ejecutemos esta aplicación. En primer lugar, utilizaremos un navegador [Mozilla]:

El código HTML recibido por [Mozilla] es el siguiente:
<html>
<head>
</head>
<body>
<form name="_ctl0" method="post" action="requiredfieldvalidator1.aspx" id="_ctl0">
<input type="hidden" name="__VIEWSTATE" value="dDwxNDI1MDc1NTU1Ozs+SGtdZvVxefDCDxnsqbDnqCaROsk=" />
<p>
Demande du dossier de candidature au DESS
</p>
<fieldset>
<legend>[--Identité--]</legend>
<table>
<tbody>
<tr>
<td>
Nom*</td>
<td>
<input name="txtNom" type="text" id="txtNom" />
</td>
</tr>
</tbody>
</table>
</fieldset>
<p>
<input type="submit" name="btnEnvoyer" value="Envoyer" onclick="if (typeof(Page_ClientValidate) == 'function') Page_ClientValidate(); " language="javascript" id="btnEnvoyer" />
</p>
</form>
</body>
</html>
Se observa que el botón [btnEnvoyer] está vinculado a una función de JavaScript a través de su atributo [onclick]. También se puede observar que la página no contiene ningún código [Javascript]. La prueba [typeof(Page_ClientValidate) == 'function'] fallará y no se llamará a la función de JavaScript. El formulario se enviará entonces al servidor y será este quien realice las comprobaciones de validez. Utilicemos el botón [Envoyer] sin asignarle ningún nombre. La respuesta del servidor es la siguiente:

¿Qué ha pasado? El formulario se ha enviado al servidor. Este ha ejecutado el código de todos los componentes de validación que se encuentran en la página. En este caso solo hay uno. Si al menos un componente de validación detecta un error, el servidor devuelve el formulario tal y como se ha rellenado (de hecho, para los componentes que tienen [EnableViewState=true], además de un mensaje de error por cada componente de validación que haya detectado un error). Este mensaje es el atributo [ErrorMessage] del componente de validación. Este es el mecanismo que vemos en acción más arriba. Si introducimos algo en el campo [nom], el mensaje de error ya no aparece al validar el formulario:

Ahora, utilicemos Internet Explorer y accedamos a la URL [http://localhost/requiredfieldvalidator1.aspx]. Obtenemos la siguiente página:

El código HTML recibido por IE es el siguiente:
<html>
<head>
</head>
<body>
<form name="_ctl0" method="post" action="requiredfieldvalidator1.aspx" language="javascript" onsubmit="ValidatorOnSubmit();" id="_ctl0">
<input type="hidden" name="__VIEWSTATE" value="dDwxNDI1MDc1NTU1Ozs+SGtdZvVxefDCDxnsqbDnqCaROsk=" />
<script language="javascript" src="/aspnet_client/system_web/1_1_4322/WebUIValidation.js"></script>
<p>
Demande du dossier de candidature au DESS
</p>
<fieldset>
<legend>[--Identité--]</legend>
<table>
<tbody>
<tr>
<td>
Nom*</td>
<td>
<input name="txtNom" type="text" id="txtNom" />
<span id="RequiredFieldValidator1" controltovalidate="txtNom" errormessage="Le champ [nom] est obligatoire" evaluationfunction="RequiredFieldValidatorEvaluateIsValid" initialvalue="" style="color:Red;visibility:hidden;">Le champ [nom] est obligatoire</span>
</td>
</tr>
</tbody>
</table>
</fieldset>
<p>
<input type="submit" name="btnEnvoyer" value="Envoyer" onclick="if (typeof(Page_ClientValidate) == 'function') Page_ClientValidate(); " language="javascript" id="btnEnvoyer" />
</p>
<script language="javascript">
<!--
var Page_Validators = new Array(document.all["RequiredFieldValidator1"]);
// -->
</script>
<script language="javascript">
<!--
var Page_ValidationActive = false;
if (typeof(clientInformation) != "undefined" && clientInformation.appName.indexOf("Explorer") != -1) {
if (typeof(Page_ValidationVer) == "undefined")
alert("Impossible de trouver la bibliothèque de scripts /aspnet_client/system_web/1_1_4322/WebUIValidation.js. Essayez de placer ce fichier manuellement ou effectuez une réinstallation en exécutant 'aspnet_regiis -c'.");
else if (Page_ValidationVer != "125")
alert("Cette page utilise une version incorrecte de WebUIValidation.js. La page requiert la version 125. La bibliothèque de scripts est " + Page_ValidationVer + ".");
else
ValidatorOnLoad();
}
function ValidatorOnSubmit() {
if (Page_ValidationActive) {
ValidatorCommonOnSubmit();
}
}
// -->
</script>
</form>
</body>
</html>
Se observa que este código es mucho más extenso que el recibido por [Mozilla]. No entraremos en detalles. La diferencia se debe a que el servidor ha incluido funciones de JavaScript en el documento HTML enviado. ¿Por qué hay dos códigos HTML diferentes si la URL solicitada por ambos navegadores es la misma? Ya hemos mencionado que la tecnología ASP.NET hace que el servidor adapte el documento HTML enviado al cliente al nivel de dicho cliente. En el mercado existen diferentes navegadores que no tienen todos las mismas capacidades. Los navegadores de Microsoft y Netscape, por ejemplo, no utilizan el mismo modelo de objetos para el documento que reciben. Por ello, el código JavaScript para manipular este documento en el navegador presenta diferencias entre ambos navegadores. Del mismo modo, los fabricantes han creado versiones sucesivas de sus navegadores, mejorando constantemente sus capacidades. Por lo tanto, un documento HTML que IE5 es capaz de interpretar puede que no lo sea para IE3. Arriba tenemos un ejemplo de esta adaptación del servidor en función de su cliente. Por una razón que no se ha profundizado aquí, el servidor web consideró que el cliente [Mozilla] no tenía la capacidad de gestionar el código JavaScript de validación. Por ello, dicho código no se incluyó en el documento HTML que se le envió y la validación se realizó en el lado del servidor. En el caso de [IE6], este código JavaScript se incluyó en el documento HTML enviado, como podemos ver. Para verlo en acción, hagamos el siguiente experimento:
- detengamos el servidor web
- utilicemos el botón [Envoyer] sin rellenar el campo [nom]
Obtenemos la siguiente página nueva:

Efectivamente, ha sido el navegador el que ha mostrado el mensaje de error. De hecho, el servidor está detenido. Podemos comprobarlo introduciendo algo en el campo [nom] y validando. Esta vez, la respuesta es la siguiente:

¿Qué ha pasado? El navegador ha ejecutado el código JavaScript de validación. Este código ha indicado que la página era válida. Por lo tanto, el navegador ha enviado el formulario al servidor web, que estaba apagado. El navegador se ha dado cuenta de ello y ha mostrado la página anterior. Si reiniciamos el servidor, todo vuelve a la normalidad.
¿Qué podemos aprender de este ejemplo?
- la importancia del concepto de «componente de validación», que permite devolver al cliente cualquier formulario incorrecto
- la utilidad del código [VIEWSTATE], que permite devolver este formulario tal y como se ha rellenado
- la capacidad de adaptación del servidor a su cliente. Este se identifica mediante el encabezado HTTP [User-Agent:] que envía al servidor. Por lo tanto, no es el servidor el que «adivina» con quién está tratando. Esta capacidad de adaptación resulta de gran interés para el desarrollador, que no tiene que preocuparse por el tipo de cliente de su aplicación.
8.2.3. CompareValidator
Creamos la siguiente página [comparevalidator1.aspx]:

n.º | nombre | tipo | propiedades | función |
1 | DropDownList | EnableViewState=true | lista desplegable | |
2 | DropDownList | EnableViewState=true | lista desplegable | |
3 | CompareValidator | EnableViewState=false EnableClientScript=true ErrorMessage=Opción 1 no válida ValorParaComparar=? Operador=No igual Tipo=cadena | componente de validación | |
4 | CompareValidator | EnableViewState=false EnableClientScript=true ErrorMessage=Opción 2 no válida ControlToCompare=? Operator=NotEqual Tipo=cadena | componente de validación | |
5 | Botón | EnableViewState=false CausesValidation=true | Botón [submit] |
El componente [CompareValidator] sirve para comparar dos valores entre sí. Los operadores que se pueden utilizar son [Equal, NotEqual, LessThan, LessThanEqual, GreaterThan, GreaterThanEqual]. El primer valor se establece mediante la propiedad [ControlToValidate], y el segundo mediante [ValueToCompare] si el valor anterior debe compararse con una constante, o mediante [ControlToCompare] si debe compararse con el valor de otro componente. Las propiedades importantes del componente [CompareValidator] son las siguientes:
campo cuyo contenido debe ser comprobado por el componente | |
booleano: si es verdadero, indica que el contenido del campo anterior también debe comprobarse en el lado del cliente | |
el mensaje de error que el componente debe mostrar en caso de detectarse un error | |
valor con el que se debe comparar el valor del campo [ControlToValidate] | |
componente con cuyo valor debe compararse el valor del campo [ControlToValidate] | |
operador de comparación entre ambos valores | |
tipo de los valores que se van a comparar |
El código de presentación de esta página es el siguiente:
<html>
<head>
</head>
<body>
<form runat="server">
<p>
Demande du dossier de candidature au DESS
</p>
<fieldset>
<legend>[--Options du DESS pour lesquelles vous candidatez--]</legend>
<table>
<tbody>
<tr>
<td>
Option1*</td>
<td>
Option2</td>
</tr>
<tr>
<td>
<asp:DropDownList id="cmbChoix1" runat="server">
<asp:ListItem Value="?" Selected="True">?</asp:ListItem>
<asp:ListItem Value="Automatique">Automatique</asp:ListItem>
<asp:ListItem Value="Informatique">Informatique</asp:ListItem>
</asp:DropDownList>
</td>
<td>
<asp:DropDownList id="cmbChoix2" runat="server">
<asp:ListItem Value="?">?</asp:ListItem>
<asp:ListItem Value="Automatique">Automatique</asp:ListItem>
<asp:ListItem Value="Informatique">Informatique</asp:ListItem>
</asp:DropDownList>
</td>
</tr>
<tr>
<td>
<asp:CompareValidator id="CompareValidator1" runat="server" ErrorMessage="Choix 1 invalide" ControlToValidate="cmbChoix1" ValueToCompare="?" Operator="NotEqual"></asp:CompareValidator>
</td>
<td>
<asp:CompareValidator id="CompareValidator2" runat="server" ErrorMessage="Choix 2 invalide" ControlToValidate="cmbChoix2" Operator="NotEqual" ControlToCompare="cmbChoix1"></asp:CompareValidator>
</td>
</tr>
</tbody>
</table>
</fieldset>
<p>
<asp:Button id="btnEnvoyer" runat="server" Text="Envoyer"></asp:Button>
</p>
</form>
</body>
</html>
Las restricciones de validación de la página son las siguientes:
- el valor seleccionado en [cmbChoix1] debe ser diferente de la cadena «?». Esto implica las siguientes propiedades para el componente [CompareValidator1]: Operator=NotEqual, ValueToCompare=?, Type=string
- el valor seleccionado en [cmbChoix2] debe ser diferente del seleccionado en [cmbChoix1]. Esto implica las siguientes propiedades para el componente [CompareValidator2]: Operador=NotEqual, ControlToCompare=cmbChoix1, Tipo=cadena
Ejecutamos esta aplicación. A continuación se muestra un ejemplo de página recibida durante las comunicaciones entre el cliente y el servidor:

Si se solicita la misma página desde Internet Explorer 6, se incluye en ella código JavaScript que hace que su funcionamiento sea ligeramente diferente. Los posibles errores se señalan en cuanto el usuario cambia el valor de alguna de las listas desplegables. Se trata de una mejora en la comodidad del usuario.
8.2.4. CustomValidator, RangeValidator
Creamos la siguiente página [customvalidator1.aspx]:

n.º | nombre | tipo | propiedades | función |
DropDownList | EnableViewState=true | lista desplegable | ||
CompareValidator | EnableViewState=false EnableClientScript=true ErrorMessage=Título no válido Operator=NotEqual ValueToCompare=? Tipo=Cadena | Componente de validación del control [1] | ||
TextBox | EnableViewState=false | campo de entrada | ||
CustomValidator | EnableViewState=false EnableClientScript=true ErrorMessage=Precisión del título no válida ClientValidationFunction=chkAutreDiplome | Componente de validación del campo [3] | ||
TextBox | EnableViewState=false | campo de entrada | ||
RangeValidator | EnableViewState=false EnableClientScript=true ErrorMessage=El año de obtención del título debe estar comprendido entre [1990, 2004] MinValue=1990 MaxValue=2004 Tipo=Entero ControlToValidate=txtAnDiplome | Componente de validación del campo [5] | ||
RequiredFieldValidator | EnableViewState=false EnableClientScript=true ErrorMessage=Se requiere el año de obtención del título ControlToValidate=txtAnDiplome | Componente de validación del campo [5] | ||
Botón | EnableViewState=false Causas de validación=true | Botón [submit] |
El campo [RangeValidator] sirve para comprobar que el valor de un control se encuentra entre los límites [MinValue] y [MaxValue]. Sus propiedades son las siguientes:
campo cuyo valor debe ser comprobado por el componente | |
booleano: si es verdadero, indica que el contenido del campo anterior también debe validarse en el lado del cliente | |
el mensaje de error que el componente debe mostrar en caso de que se detecte un error | |
valor mínimo del campo que se va a comprobar | |
valor máximo del campo que se va a comprobar | |
tipo del valor del campo que se va a comprobar |
El campo [CustomValidator] permite realizar validaciones que no pueden llevar a cabo los componentes de validación que ofrece ASP.NET. Esta validación se realiza mediante una función escrita por el desarrollador. Dicha función se ejecuta en el servidor. Dado que la comprobación también puede realizarse en el lado del cliente, es posible que el desarrollador tenga que crear una función JavaScript que incluirá en el documento HTML. Sus propiedades son las siguientes:
campo cuyo valor debe ser validado por el componente | |
booleano: si es verdadero, indica que el contenido del campo anterior también debe validarse en el lado del cliente | |
el mensaje de error que el componente debe mostrar en caso de que se detecte un error | |
la función que se debe ejecutar en el lado del cliente |
El código de presentación de la página es el siguiente:
<%@ Page Language="VB" %>
<script runat="server">
...
</script>
<html>
<head>
</head>
<body>
<form runat="server">
<p align="left">
Demande du dossier de candidature au DESS
</p>
<fieldset>
<legend>[--Votre dernier diplôme--]</legend>
<table>
<tbody>
<tr>
<td>
Diplôme*</td>
<td>
<asp:DropDownList id="cmbDiplomes" runat="server">
<asp:ListItem Value="?">?</asp:ListItem>
<asp:ListItem Value="[Autre]">[Autre]</asp:ListItem>
<asp:ListItem Value="Maîtrise">Maîtrise</asp:ListItem>
<asp:ListItem Value="DESS">DESS</asp:ListItem>
<asp:ListItem Value="DEA">DEA</asp:ListItem>
</asp:DropDownList>
</td>
<td>
Si [Autre], précisez</td>
<td>
<asp:TextBox id="txtAutreDiplome" runat="server" EnableViewState="False"></asp:TextBox>
</td>
</tr>
<tr>
<td>
</td>
<td>
<p>
<asp:CompareValidator id="CompareValidator2" runat="server" ErrorMessage="Diplôme invalide" ControlToValidate="cmbDiplomes" ValueToCompare="?" Operator="NotEqual" ></asp:CompareValidator>
</p>
</td>
<td>
</td>
<td>
<asp:CustomValidator id="CustomValidator1" runat="server" ErrorMessage="Précision diplôme invalide" OnServerValidate="CustomValidator1_ServerValidate_1" EnableViewState="False" ClientValidationFunction="chckAutreDiplome"></asp:CustomValidator>
</td>
</tr>
<tr>
<td>
Année d'obtention*</td>
<td colspan="3">
<asp:TextBox id="txtAnDiplome" runat="server" Columns="4"></asp:TextBox>
<asp:RangeValidator id="RangeValidator1" runat="server" ErrorMessage="Année diplôme doit être dans l'intervalle [1990,2004]" ControlToValidate="txtAnDiplome" MinimumValue="1990" MaximumValue="2004" Type="Integer"></asp:RangeValidator>
<asp:RequiredFieldValidator id="RequiredFieldValidator2" runat="server" ErrorMessage="Année diplôme requise" ControlToValidate="txtAnDiplome"></asp:RequiredFieldValidator>
</td>
</tr>
</tbody>
</table>
</fieldset>
<p>
<asp:Button id="btnEnvoyer" runat="server" Text="Envoyer"></asp:Button>
</p>
</form>
</body>
</html>
El atributo [OnServerValidate] del componente [CustomValidator] permite especificar la función encargada de la validación en el servidor. En el ejemplo anterior, el componente [CustomValidator1] recurre al siguiente procedimiento [CustomValidator1_ServerValidate_1]:
<%@ Page Language="VB" %>
<script runat="server">
Sub CustomValidator1_ServerValidate_1(sender As Object, e As ServerValidateEventArgs)
' el campo [txtAutreDiplome] debe estar rellenado si [cmbDiplomes]=[autre]
e.isvalid=not (cmbDiplomes.selecteditem.text="[Autre]" and txtAutreDiplome.text.trim="") _
and not (cmbDiplomes.selecteditem.text<>"[Autre]" and cmbDiplomes.selecteditem.text<>"?" and txtAutreDiplome.text.trim<>"")
End Sub
</script>
....
Una función de validación asociada a un componente [CustomValidator] recibe dos parámetros:
- sender: objeto que ha provocado el evento
- e: el evento. El procedimiento debe establecer el atributo [e.IsValid] en «verdadero» si los datos verificados son correctos, y en «falso» en caso contrario.
En este caso, las comprobaciones realizadas son las siguientes:
- la lista desplegable [cmbDiplomes] no puede tener como valor [?]. Esto lo comprueba el componente [CompareValidator2]
- el usuario selecciona un título en la lista desplegable [cmbDiplomes]. Si su título no figura en la lista, tiene la posibilidad de seleccionar la opción [Autre] de la lista. A continuación, debe indicar su título en el campo de entrada [txtAutreDiplome]. Si se selecciona [Autre] en [cmbDiplomes], el campo [txtAutreDiplome] no debe estar vacío. Si en [cmbDiplomes] no se selecciona ni [Autre] ni [?], el campo [txtAutreDiplome] debe estar vacío. Esto se comprueba mediante la función asociada al componente [CustomValidator1].
- El año de obtención del título debe estar dentro del intervalo de [1900-2004]. Esto se comprueba mediante [RangeValidator1]. Sin embargo, si el usuario no introduce ningún valor en el campo, no se utiliza la función de validación de [RangeValidator1]. Por ello, se añade el componente [RequiredFieldValidator2] para comprobar si hay contenido. Esta es una regla general. El contenido de un campo no se comprueba si está vacío. El único caso en el que sí se comprueba es cuando está asociado a un componente [RequiredFieldValidator].
A continuación se muestra un ejemplo de ejecución en el navegador [Mozilla]:

Si utilizamos el navegador [IE6], podemos añadir una función de validación del lado del cliente para el componente [CustomValidator1]. Para ello, este componente tiene las siguientes propiedades:
- EnableClientScript=true
- ClientValidationFunction=chkAutreDiplome
La función [chkAutreDiplome ] es la siguiente:
<head>
<meta http-equiv="pragma" content="no-cache" />
<script language="javascript">
function chkAutreDiplome(source,args){
// comprueba la validez del campo txtAutreDiplome
with(document.frmCandidature){
diplome=cmbDiplomes.options[cmbDiplomes.selectedIndex].text;
args.IsValid= !(diplome=="[Autre]" && txtAutreDiplome.value=="")
&& ! (diplome!="[Autre]" && diplome!="?" && txtAutreDiplome.value!="");
}
}
</script>
</head>
La función [chkAutreDiplome] recibe los mismos dos parámetros que la función de servidor [CustomValidator1_ServerValidate_1]:
- fuente: el objeto que ha provocado el evento
- args: el evento. Este tiene un atributo [IsValid] que la función debe establecer en «verdadero» si los datos verificados son correctos. Un valor «falso» mostrará, en el lugar del componente de validación, el mensaje de error asociado al mismo y el formulario no se enviará al servidor.
8.2.5. RegularExpressionValidator
Creamos la siguiente página [regularexpressionvalidator1.aspx]:

n.º | nombre | tipo | propiedades | función |
TextBox | EnableViewState=true | campo de entrada | ||
RequiredFieldValidator | ControlToValidate=txtMel Display=Dynamic | Componente de validación del control [1] | ||
RegularExpressionValidator | ControlToValidate=txtMel Display=Dynamic | Componente de validación del control [1] | ||
Botón | EnableViewState=false CausesValidation=true | Botón [submit] |
Aquí queremos comprobar el formato de una dirección de correo electrónico. Lo hacemos con un componente [RegularExpressionValidator] que permite comprobar la validez de un campo mediante una expresión regular. Recordemos que una expresión regular es un patrón de cadena de caracteres. Por lo tanto, se comprueba el contenido de un campo con respecto a un patrón. Dado que el contenido de un campo no se comprueba si está vacío, también necesitamos un componente [RequiredFieldValidator] para comprobar que la dirección de correo electrónico no esté vacía. La clase [RegularExpressionValidator] tiene propiedades similares a las de las clases de los componentes ya estudiados:
campo cuyo valor debe ser comprobado por el componente | |
booleano: si es verdadero, indica que el contenido del campo anterior también debe comprobarse en el lado del cliente | |
el mensaje de error que el componente debe mostrar en caso de que se detecte un error | |
la expresión regular con la que se comparará el contenido de [ControlToValidate] | |
modo de visualización: Static: el campo siempre está presente aunque no muestre ningún mensaje de error; Dynamic: el campo solo está presente si hay un mensaje de error; None: no se muestra el mensaje de error. Este campo también existe para los demás componentes, pero hasta ahora no se había utilizado. |
La expresión regular modelo de una dirección de correo electrónico podría ser la siguiente: \s*[\w-]+(\.[\w-]+)*@[\w-]+(\.[\w-]+)+\s*
Una dirección de correo electrónico tiene el formato [champ1.champ2....@champA.champB...]. Debe haber al menos un campo antes del signo @ y al menos dos campos después. Estos campos están formados por caracteres alfanuméricos y el signo -. El carácter alfanumérico se representa con el símbolo \w. La secuencia [\w-] significa el carácter \w o el carácter -. El signo + después de una secuencia S significa que esta puede repetirse una o varias veces; el signo * significa que puede repetirse cero o varias veces; el signo ? significa que puede repetirse cero o una vez.
Un campo de la dirección de correo electrónico se ajusta al patrón [\w-]+. El hecho de que deba haber obligatoriamente al menos un campo antes del signo @ y al menos dos después, separados por el signo ., se ajusta al patrón: [\w-]+(\.[\w-]+)*@[\w-]+(\.[\w-]+)+. Se puede permitir que el usuario introduzca espacios antes y después de la dirección. Estos se eliminarán durante el procesamiento. Una secuencia de espacios, que puede estar vacía, se representa mediante el patrón \s*. De ahí la expresión regular de la dirección de correo electrónico: \s*[\w-]+(\.[\w-]+)*@[\w-]+(\.[\w-]+)+\s*.
El código de presentación de la página es, por tanto, el siguiente:
<html>
<head>
</head>
<body>
<form runat="server">
<p align="left">
Demande du dossier de candidature au DESS
</p>
<fieldset>
<legend>[--Votre adresse électronique où sera envoyé le dossier de candidature--]</legend>
<table>
<tbody>
<tr>
<td>
<asp:TextBox id="txtMel" runat="server" Columns="60"></asp:TextBox>
</td>
</tr>
<tr>
<td>
<p>
<asp:RequiredFieldValidator id="RequiredFieldValidator3" runat="server" Display="Dynamic" ControlToValidate="txtMel" ErrorMessage="Votre adresse électronique est requise"></asp:RequiredFieldValidator>
</p>
<p>
<asp:RegularExpressionValidator id="RegularExpressionValidator1" runat="server" Display="Dynamic" ControlToValidate="txtMel" ErrorMessage="Votre adresse électronique n'a pas un format valide" ValidationExpression="\s*[\w-]+(\.[\w-]+)*@[\w-]+(\.[\w-]+)+\s*"></asp:RegularExpressionValidator>
</p>
</td>
</tr>
</tbody>
</table>
</fieldset>
<p>
<asp:Button id="btnEnvoyer" runat="server" Text="Envoyer"></asp:Button>
</p>
</form>
</body>
</html>
8.2.6. ValidationSummary
Por motivos estéticos, puede ser conveniente agrupar los mensajes de error en un único lugar, como en el siguiente ejemplo [summaryvalidator1.aspx], donde hemos reunido en una sola página todos los ejemplos anteriores:

Todos los controles de validación tienen las siguientes propiedades:
- [Display=Dynamic], lo que hace que los controles no ocupen espacio en la página si su mensaje de error está vacío
- [EnableClientScript=false] para impedir cualquier validación del lado del cliente
- [Text=*]. Este será el mensaje que se mostrará en caso de error, mientras que el contenido del atributo [ErrorMessage] se mostrará mediante el control [ValidationSummary] que presentamos a continuación.
Este conjunto de controles se coloca en un componente [Panel] denominado [vueFormulaire]. En otro componente [Panel] denominado [vueErreurs], colocamos un control [ValidationSummary]:

n.º | nombre | tipo | propiedades | función |
1 | ValidationSummary | HeaderText=Se han producido los siguientes errores, EnableClientScript=false, ShowSummary=true | muestra los errores de todos los controles de validación de la página | |
2 | LinkButton | CausasValidación=false | enlace de vuelta al formulario |
En el caso del enlace [lnkErreursToFormulaire], no es necesario activar la validación, ya que este enlace no envía ningún valor que deba verificarse.
Cuando todos los datos son válidos, se muestra al usuario la siguiente vista [infos]:

1
n.º | nombre | tipo | propiedades | función |
1 | Etiqueta | mensaje informativo dirigido al usuario |
El código de presentación de esta página es el siguiente:
<html>
<head>
</head>
<body>
<form runat="server">
<p align="left">
Demande du dossier de candidature au DESS
</p>
<p>
<hr />
<asp:panel id="vueErreurs" runat="server">
<p align="left">
<asp:ValidationSummary id="ValidationSummary1" runat="server" ShowMessageBox="True" BorderColor="#C04000" BorderWidth="1px" BackColor="#FFFFC0" HeaderText="Les erreurs suivantes se sont produites"></asp:ValidationSummary>
</p>
<p>
<asp:LinkButton id="lnkErreursToFormulaire" onclick="lnkErreursToFormulaire_Click" runat="server" CausesValidation="False">Retour au formulaire</asp:LinkButton>
</p>
</asp:panel>
<asp:panel id="vueFormulaire" runat="server">
....
<asp:Button id="btnEnvoyer" onclick="btnEnvoyer_Click" runat="server" Text="Envoyer"></asp:Button>
</p>
</asp:panel>
<asp:panel id="vueInfos" runat="server">
<asp:Label id="lblInfo" runat="server"></asp:Label>
</asp:panel>
</form>
</body>
</html>
A continuación se muestran algunos ejemplos de los resultados obtenidos. Validamos la vista [formulaire] sin introducir ningún valor:

El botón [Envoyer] nos ofrece la siguiente respuesta:

Se ha mostrado la vista [erreurs]. Si utilizamos el enlace de vuelta al formulario, lo encontramos en el siguiente estado:

Se trata de la vista [formulaire]. Obsérvese el carácter [*] junto a los datos erróneos. Se ha mostrado el campo [Text] de los controles de validación. Si rellenamos correctamente los campos, obtendremos la vista [infos]:

El código de control de la página es el siguiente:
<%@ Page Language="VB" %>
<script runat="server">
' procedimiento ejecutado al cargar la página
Sub page_Load(sender As Object, e As EventArgs)
' en la primera solicitud, se muestra la vista [formulaire]
if not ispostback then
afficheVues(true,false,false)
end if
end sub
sub afficheVues(byval formulaireVisible as boolean, _
erreursVisible as boolean, infosVisible as boolean)
' conjunto de vistas
vueFormulaire.visible=formulaireVisible
vueErreurs.visible=erreursVisible
vueInfos.visible=infosVisible
end sub
Sub CustomValidator1_ServerValidate(sender As Object, e As ServerValidateEventArgs)
...
End Sub
Sub CustomValidator2_ServerValidate(sender As Object, e As ServerValidateEventArgs)
...
End Sub
Sub CustomValidator1_ServerValidate_1(sender As Object, e As ServerValidateEventArgs)
...
End Sub
Sub lnkErreursToFormulaire_Click(sender As Object, e As EventArgs)
' muestra la vista de formulario
afficheVues(true,false,false)
' vuelve a realizar las comprobaciones de validez
Page.validate
End Sub
Sub btnEnvoyer_Click(sender As Object, e As EventArgs)
' ¿Es válida la página?
if not Page.IsValid then
' se muestra la vista [erreurs]
afficheVues(false,true,false)
else
' si no, la vista [infos]
lblInfo.Text="Le dossier de candidature au DESS IAIE a été envoyé à l'adresse ["+ _
txtMel.Text + "]. Nous vous en souhaitons bonne réception.<br><br>Le secrétariat du DESS."
afficheVues(false,false,true)
end if
end sub
</script>
<html>
...
</html>
- En la rutina [Page_Load], que se ejecuta con cada solicitud del cliente, mostramos la vista [formulaire], mientras que las demás permanecen ocultas. Esto solo se hace en la primera solicitud. El procedimiento llama a un procedimiento de utilidad, [afficheVues], al que se le pasan tres valores booleanos para determinar si se muestran o no las tres vistas.
- Cuando se llama al procedimiento [btnEnvoyer_Click], ya se han realizado las comprobaciones de datos. El botón [btnEnvoyer], con la propiedad [CausesValidation=true], es el que fuerza esta comprobación de datos. Se han ejecutado todos los controles de validación y se ha establecido su propiedad [IsValid]. Esta indica si los datos verificados por el control eran válidos o no. Por otra parte, también se ha establecido la propiedad [IsValid] de la propia página. Esta propiedad toma el valor [vrai] únicamente si la propiedad [IsValid] de todos los controles de validación de la página tiene el valor [vrai]. Así pues, el procedimiento [btnEnvoyer_Click] comienza comprobando si la página es válida o no. Si no lo es, se muestra la vista [erreurs]. Esta contiene el control [ValidationSummary], que recoge los atributos [ErrorMessage] de todos los controles (véase la captura de pantalla anterior). Si la página es válida, se muestra la vista [infos] junto con un mensaje informativo.
- El procedimiento [lnkErreursToFormulaire_Click] se encarga de mostrar la vista [formulaire] tal y como se ha validado. Todos los campos de entrada de la vista [formulaire] que tienen la propiedad [EnableViewState=true] ven su estado regenerado automáticamente. Curiosamente, el estado de los componentes de validación no se restaura. De hecho, cabría esperar que, al volver a la vista [erreurs], los controles con errores mostraran su campo [Text]. Pero no es así. Por lo tanto, se ha forzado la validación de los datos utilizando el método [Page.Validate] de la página. Esto debe hacerse una vez que el panel [vueFormulaire] se haya hecho visible. Por lo tanto, hay un total de dos validaciones. En la práctica, esto debería evitarse. En este caso, el ejemplo nos permitía introducir nuevos conceptos sobre la validación de páginas.
8.3. Componentes ListControl y enlace de datos
Algunos de los componentes de servidor analizados permiten mostrar una lista de valores (DropDownList, ListBox). Otros, que aún no hemos presentado, permiten mostrar varias listas de valores en tablas (HTML). En todos los casos, es posible, mediante programación, asociar uno a uno los valores de las listas al componente correspondiente. También es posible asociar objetos más complejos a estos componentes, como los de tipo [Array], [ArrayList], [DataSet], [HashTable], ... lo que simplifica el código que asocia los datos al componente. A esta asociación se le denomina «vínculo de datos».
Todos los componentes derivados de la clase [ListControl] pueden asociarse a una lista de datos. Se trata de los componentes [DropDownList], [ListBox], [CheckButtonList] y [RadioButtonList]. Cada uno de estos componentes puede vincularse a una fuente de datos. Esta puede ser de diversos tipos: [Array], [ArrayList], [DataTable], [DataSet], [HashTable],...., en general, un objeto que implemente una de las interfaces IEnumerable, ICollection o IListSource. Solo presentaremos algunos de ellos. Un objeto [DataSet] es un objeto de imagen de una base de datos relacional. Se trata, por tanto, de un conjunto de tablas vinculadas mediante relaciones. El objeto [DataTable] representa una de esas tablas. La fuente de datos asigna las propiedades [Text] y [Value] a cada uno de los miembros [Item] del objeto [ListControl]. Si T es el valor de [Text] y V el valor de [Value], la etiqueta HTML generada para cada elemento de [ListControl] es la siguiente:
<option value="V">T</option> | |
<input type="checkbox" value="V">T | |
<input type="radio" value="V">T |
La asociación de un componente [ListControl] a una fuente de datos se realiza mediante las siguientes propiedades:
una fuente de datos [Array], [ArrayList], [DataTable], [DataSet], [HashTable], ... | |
en el caso de que la fuente de datos sea un [DataSet], representa el nombre de la tabla que se utilizará como fuente de datos. La verdadera fuente de datos es, por tanto, una tabla. | |
en el caso de que la fuente de datos sea una tabla ([DataTable], [DataSet]), representa el nombre de la columna de la tabla que proporcionará sus valores al campo [Text] de los elementos de [ListControl] | |
en el caso de que la fuente de datos sea una tabla ([DataTable], [DataSet]), representa el nombre de la columna de la tabla que proporcionará sus valores al campo [Value] de los elementos de [ListControl] |
La asociación de un componente [ListControl] a una fuente de datos no inicializa el componente con los valores de la fuente de datos. Esto lo hace la operación [ListControl].DataBind.
Dependiendo del tipo de fuente de datos, la asociación de esta a un componente [ListControl] se realizará de forma diferente:
[ListControl].DataSource=A los campos [Text] y [Value] de los elementos de [ListControl] tendrán como valores los elementos de A | |
[ListControl].DataSource=AL los campos [Text] y [Value] de los elementos de [ListControl] tendrán como valores los elementos de AL | |
[ListControl].DataSource = DT, [ListControl].DataTextField = «col1», [ListControl]. DatavalueField = «col2» donde col1 y col2 son dos columnas de la tabla DT. Los campos [Text] y [Value] de los elementos de [ListControl] tendrán como valores los de las columnas col1 y col2 de la tabla DT | |
[ListControl].DataSource=DS, [ListControl].DataSource="table", donde «table» es el nombre de una de las tablas de DS. [ListControl].DataTextField = «col1», [ListControl]. DatavalueField = «col2», donde col1 y col2 son dos columnas de la tabla «table». Los campos [Text] y [Value] de los elementos de [ListControl] tendrán como valores los de las columnas col1 y col2 de la tabla «table» | |
[ListControl].DataSource = HT, [ListControl].DataTextField="key", [ListControl]. DatavalueField ="value", donde [key] y [value] son, respectivamente, las claves y los valores de HT. |
Aplicamos esta información al siguiente ejemplo: [databind1.aspx]:
![]() |
Aquí tenemos cinco enlaces de datos, cada uno con cuatro controles de tipo [DropDownList], [ListBox], [CheckBoxList] y [RadioButtonList]. Los cinco enlaces analizados se diferencian por sus fuentes de datos:
conexión | fuente de datos |
Array | |
ArrayList | |
DataTable | |
DataSet | |
HashTable |
8.3.1. Código de presentación de los componentes
El código de presentación de los controles del enlace 1 es el siguiente:
<td>
<asp:DropDownList id="DropDownList1" runat="server"></asp:DropDownList>
</td>
<td>
<asp:ListBox id="ListBox1" runat="server" SelectionMode="Multiple"></asp:ListBox>
</td>
<td>
<asp:CheckBoxList id="CheckBoxList1" runat="server"></asp:CheckBoxList>
</td>
<td>
<asp:RadioButtonList id="RadioButtonList1" runat="server"></asp:RadioButtonList>
</td>
El de las conexiones 2 a 5 es idéntico, salvo por el número de conexión.
8.3.2. Conexión a una fuente de datos de tipo matriz
La conexión de los cuatro controles [ListBox] anteriores se realiza de la siguiente manera en el procedimiento [Page_Load] del código de control:
' los datos globales
dim textes() as string={"un","deux","trois","quatre"}
dim valeurs() as string={"1","2","3","4"}
dim myDataListe as new ArrayList
dim myDataTable as new DataTable("table1")
dim myDataSet as new DataSet
dim myHashTable as new HashTable
' procedimiento ejecutado al cargar la página
Sub page_Load(sender As Object, e As EventArgs)
if not IsPostBack then
' se crean las fuentes de los datos que se van a vincular a los componentes
createDataSources
' vinculación a una tabla [Array]
bindToArray
' vinculación a una lista [ArrayList]
bindToArrayList
' vinculación a una tabla [DataTable]
bindToDataTable
' enlace a un grupo de datos [DataSet]
bindToDataSet
' vinculación a un diccionario [HashTable]
bindToHashTable
end if
End Sub
sub createDataSources
' crea las fuentes de datos que se vincularán a los componentes
' arraylist
dim i as integer
for i=0 to textes.length-1
myDataListe.add(textes(i))
next
' tabla de datos
' se definen sus dos columnas
myDataTable.Columns.Add("id",Type.GetType("System.Int32"))
myDataTable.Columns.Add("texte",Type.GetType("System.String"))
' se rellena la tabla
dim ligne as DataRow
for i=0 to textes.length-1
ligne=myDataTable.NewRow
ligne("id")=i
ligne("texte")=textes(i)
myDataTable.Rows.Add(ligne)
next
' conjunto de datos: una sola tabla
myDataSet.Tables.Add(myDataTable)
' tabla hash
for i=0 to textes.length-1
myHashTable.add(valeurs(i),textes(i))
next
end sub
' vinculación de tablas
sub bindToArray
' la asociación a los componentes
with DropDownList1
.DataSource=textes
.DataBind
end with
with ListBox1
.DataSource=textes
.DataBind
end with
with CheckBoxList1
.DataSource=textes
.DataBind
end with
with RadioButtonList1
.DataSource=textes
.DataBind
end with
' selección de elementos
ListBox1.Items(1).Selected=true
ListBox1.Items(3).Selected=true
CheckBoxList1.Items(0).Selected=true
CheckBoxList1.Items(3).Selected=true
DropDownList1.SelectedIndex=2
RadioButtonList1.SelectedIndex=1
end sub
sub bindToArrayList
....
end sub
sub bindToDataTable
...
end sub
sub bindToDataSet
...
end sub
La vinculación de los controles con los datos solo se realiza aquí en la primera consulta. A partir de entonces, los controles conservarán sus elementos mediante el mecanismo del procedimiento [VIEWSTATE]. La vinculación se realiza en el procedimiento [bindToArray]. Dado que la fuente de datos es de tipo [Array], solo se inicializa el campo [DataSource] de los componentes [ListControl]. El rellenado del control con los valores de la fuente de datos asociada se realiza mediante el método [ListControl].DataBind. Solo después de esto, los objetos [ListControl] contienen elementos. Entonces se pueden seleccionar algunos de ellos.
8.3.3. Enlace a una fuente de datos de tipo ArrayList
La fuente de datos [myDataListe] se inicializa en el procedimiento [createDataSources]:
' datos globales
dim textes() as string={"un","deux","trois","quatre"}
dim myDataListe as new ArrayList
..
' procedimiento ejecutado al cargar la página
Sub page_Load(sender As Object, e As EventArgs)
if not IsPostBack then
' se crean las fuentes de datos que se van a vincular a los componentes
createDataSources
...
end if
End Sub
sub createDataSources
' se crean las fuentes de datos que se vincularán a los componentes
' arraylist
dim i as integer
for i=0 to textes.length-1
myDataListe.add(textes(i))
next
...
end sub
La vinculación de los cuatro controles [ListBox] de la vinculación 2 se realiza de la siguiente manera en el procedimiento [bindToArrayList] del código de control:
' vinculación de la lista de matrices
sub bindToArrayList
' la asociación a los componentes
with DropDownList2
.DataSource=myDataListe
.DataBind
end with
with ListBox2
.DataSource=myDataListe
.DataBind
end with
with CheckBoxList2
.DataSource=myDataListe
.DataBind
end with
with RadioButtonList2
.DataSource=myDataListe
.DataBind
end with
' la selección de elementos
ListBox2.Items(1).Selected=true
ListBox2.Items(3).Selected=true
CheckBoxList2.Items(0).Selected=true
CheckBoxList2.Items(3).Selected=true
DropDownList2.SelectedIndex=2
RadioButtonList2.SelectedIndex=1
end sub
Dado que la fuente de datos es de tipo [ArrayList], solo se inicializa el campo [DataSource] de los componentes [ListControl].
8.3.4. Fuente de datos de tipo DataTable
La fuente de datos [myDataTable] se inicializa en el procedimiento [createDataSources]:
' los datos globales
dim textes() as string={"un","deux","trois","quatre"}
dim myDataTable as new DataTable("table1")
...
' procedimiento ejecutado al cargar la página
Sub page_Load(sender As Object, e As EventArgs)
if not IsPostBack then
' se crean las fuentes de datos que se van a vincular a los componentes
createDataSources
...
end if
End Sub
sub createDataSources
' se crean las fuentes de datos que se vincularán a los componentes
...
' tabla de datos
' se definen sus dos columnas
myDataTable.Columns.Add("id",Type.GetType("System.Int32"))
myDataTable.Columns.Add("texte",Type.GetType("System.String"))
' se rellena la tabla
dim ligne as DataRow
for i=0 to textes.length-1
ligne=myDataTable.NewRow
ligne("id")=i
ligne("texte")=textes(i)
myDataTable.Rows.Add(ligne)
next
...
end sub
Empezamos creando un objeto [DataTable] con dos columnas: [id] y [texte]. La columna [id] alimentará el campo [Value] de los elementos de [ListControl], y la columna [texte] alimentará sus campos [Text]. La construcción de [DataTable] con dos columnas se realiza de la siguiente manera:
myDataTable.Columns.Add("id",Type.GetType("System.Int32"))
myDataTable.Columns.Add("texte",Type.GetType("System.String"))
Por lo tanto, se crea una tabla con dos columnas:
- la primera, denominada «id», es de tipo entero
- la segunda, denominada «texto», es de tipo cadena de caracteres
Una vez creada la estructura de la tabla, podemos rellenarla con el siguiente código:
dim ligne as DataRow
for i=0 to textes.length-1
ligne=myDataTable.NewRow
ligne("id")=i
ligne("texte")=textes(i)
myDataTable.Rows.Add(ligne)
next
La columna [id] contendrá números enteros de [0,1,..,n], mientras que la columna [texte] contendrá los valores de la tabla [data]. Una vez hecho esto, se rellena la tabla [dataListe]. La vinculación de los cuatro controles [ListBox] de la vinculación 3 se realiza de la siguiente manera en el procedimiento [bindToDataTable] del código de control:
sub bindToDataTable
' la asociación a los componentes
with DropDownList3
.DataSource=myDataTable
.DataValueField="id"
.DataTextField="texte"
.DataBind
end with
with ListBox3
.DataSource=myDataTable
.DataValueField="id"
.DataTextField="texte"
.DataBind
end with
with CheckBoxList3
.DataSource=myDataTable
.DataValueField="id"
.DataTextField="texte"
.DataBind
end with
with RadioButtonList3
.DataSource=myDataTable
.DataValueField="id"
.DataTextField="texte"
.DataBind
end with
' selección de los elementos
ListBox3.Items(1).Selected=true
ListBox3.Items(3).Selected=true
CheckBoxList3.Items(0).Selected=true
CheckBoxList3.Items(3).Selected=true
DropDownList3.SelectedIndex=2
RadioButtonList3.SelectedIndex=1
end sub
Cada componente [ListControl] está vinculado a la fuente [myDataTable], asignándose a cada uno de ellos:
La tabla [myDataTable] es la fuente de datos. La columna [id] de esta tabla alimentará los campos [Value] de los elementos de los componentes, mientras que la columna [texte] alimentará sus campos [Text].
8.3.5. Fuente de datos de tipo DataSet
La fuente de datos [myDataSet] se inicializa en el procedimiento [createDataSources]:
' los datos globales
dim myDataTable as new DataTable("table1")
dim myDataSet as new DataSet
...
' procedimiento que se ejecuta al cargar la página
Sub page_Load(sender As Object, e As EventArgs)
if not IsPostBack then
' se crean las fuentes de datos que se van a vincular a los componentes
createDataSources
...
end if
End Sub
sub createDataSources
' se crean las fuentes de datos que se vincularán a los componentes
' conjunto de datos: una sola tabla
myDataSet.Tables.Add(myDataTable)
...
end sub
Un objeto [DataSet] representa un conjunto de tablas de tipo [DataTable]. Se añade la tabla [myDataTable], creada anteriormente, a la tabla [DataSet]. La vinculación de los cuatro controles [ListBox] de la vinculación 4 se realiza de la siguiente manera en el procedimiento [bindToDataSet] del código de control:
sub bindToDataSet
' la asociación a los componentes
with DropDownList4
.DataSource=myDataSet
.DataMember="table1"
.DataValueField="id"
.DataTextField="texte"
.DataBind
end with
with ListBox4
.DataSource=myDataSet
.DataMember="table1"
.DataValueField="id"
.DataTextField="texte"
.DataBind
end with
with CheckBoxList4
.DataSource=myDataSet
.DataMember="table1"
.DataValueField="id"
.DataTextField="texte"
.DataBind
end with
with RadioButtonList4
.DataSource=myDataSet
.DataMember="table1"
.DataValueField="id"
.DataTextField="texte"
.DataBind
end with
' selección de elementos
ListBox4.Items(1).Selected=true
ListBox4.Items(3).Selected=true
CheckBoxList4.Items(0).Selected=true
CheckBoxList4.Items(3).Selected=true
DropDownList4.SelectedIndex=2
RadioButtonList4.SelectedIndex=1
end sub
Cada componente [ListControl] está vinculado a la fuente de datos de la siguiente manera:
El grupo de datos [myDataSet] es la fuente de datos. Dado que esta puede incluir varias tablas, en [DataMember] se especifica el nombre de la que se va a utilizar. La columna [id] de esta tabla alimentará los campos [Value] de los elementos de los componentes, mientras que la columna [texte] alimentará sus campos [Text].
8.3.6. Fuente de datos de tipo HashTable
La fuente de datos [myHashTable] se inicializa en el procedimiento [createDataSources]:
' los datos globales
dim textes() as string={"un","deux","trois","quatre"}
dim valeurs() as string={"1","2","3","4"}
dim myHashTable as new HashTable
...
' procedimiento ejecutado al cargar la página
Sub page_Load(sender As Object, e As EventArgs)
if not IsPostBack then
' se crean las fuentes de datos que se van a vincular a los componentes
createDataSources
...
end if
End Sub
sub createDataSources
' se crean las fuentes de datos que se vincularán a los componentes
...
' tabla hash
for i=0 to textes.length-1
myHashTable.add(valeurs(i),textes(i))
next
end sub
El diccionario [myHashTable] puede considerarse como una tabla con dos columnas denominadas «key» y «value». La columna [key] representa las claves del diccionario y la columna [value], los valores asociados a ellas. La columna [key] está formada aquí por el contenido de la tabla [valeurs], y la columna [value], por el contenido de la tabla [textes]. La vinculación de esta fuente a los controles se realiza en el procedimiento [bindToHashTable]:
sub bindToHashTable
' la asociación a los componentes
with DropDownList5
.DataSource=myHashTable
.DataValueField="key"
.DataTextField="value"
.DataBind
end with
with ListBox5
.DataSource=myHashTable
.DataValueField="key"
.DataTextField="value"
.DataBind
end with
with CheckBoxList5
.DataSource=myHashTable
.DataValueField="key"
.DataTextField="value"
.DataBind
end with
with RadioButtonList5
.DataSource=myHashTable
.DataValueField="key"
.DataTextField="value"
.DataBind
end with
' la selección de los elementos
ListBox5.Items(1).Selected=true
ListBox5.Items(3).Selected=true
CheckBoxList5.Items(0).Selected=true
CheckBoxList5.Items(3).Selected=true
DropDownList5.SelectedIndex=2
RadioButtonList5.SelectedIndex=1
end sub
Para cada uno de los componentes, la conexión se realiza mediante las instrucciones:
La fuente de datos es el diccionario [myHashTable]. Los valores de los controles se obtienen de la columna [key] del diccionario y los textos, de la columna [value]. La inserción de los elementos del diccionario en los controles se realiza siguiendo el orden de las claves, que a priori es aleatorio.
8.3.7. Las directivas de importación de espacios de nombres
Hay una serie de espacios de nombres que se importan automáticamente a una página ASP.NET. No es el caso de «System.Data», donde se encuentran las clases [DataTable] y [DataSet]. Por lo tanto, es necesario importar esta clase. Esto se hace de la siguiente manera:
<%@ Page Language="VB" %>
<%@ import Namespace="System.Data" %>
<script runat="server">
...
</script>
<html>
...
</html>
8.4. Componente DataGrid y enlace de datos
El componente [DataGrid] permite mostrar datos en forma de tablas, pero va mucho más allá de esta simple visualización:
- ofrece la posibilidad de configurar con precisión la «presentación visual» de la tabla
- permite actualizar la fuente de datos
El componente [DataGrid] es un componente potente y complejo a la vez. Lo presentaremos por puntos sucesivos.
8.4.1. Visualización de una fuente de datos de tipo Array: ArrayList, DataTable, DataSet
El componente [DataGrid] permite visualizar en una tabla HTML fuentes de datos de tipo [Array], [ArrayList], [DataTable] y [DataSet]. Para estos cuatro tipos de datos, basta con asociar la fuente a la propiedad [DataSource] del componente [DataGrid]:
una fuente de datos [Array], [ArrayList], [DataTable], [DataSet], ... | |
en el caso de que la fuente de datos sea un [DataSet], representa el nombre de la tabla que se utilizará como fuente de datos. La verdadera fuente de datos es, por tanto, una tabla. Si este campo no se rellena, se muestran todas las tablas del [DataSet]. |
A continuación, presentamos la página [datagrid1.aspx], que muestra la asociación de un [DataGrid] con cuatro fuentes de datos diferentes:

La página contiene cuatro componentes [DataGrid] creados con [WebMatrix] de la siguiente manera. Se coloca el componente en su ubicación en la pestaña [Design]:

A continuación, se dibuja una tabla HTML genérica. Las propiedades de un [DataGrid] se pueden definir en la fase de diseño. Eso es lo que hacemos aquí con sus propiedades de formato. Para ello, seleccionamos el [DataGrid] que queremos configurar. Sus propiedades aparecen en una ventana situada en la parte inferior derecha:

Vamos a utilizar los dos enlaces anteriores. El enlace [Générateur de propriétés] da acceso a las principales propiedades del [DataGrid]:

Desmarcamos la opción [Afficher l'en-tête] para los cuatro componentes [DataGrid] y validamos la página. El otro enlace, [Mise en forme automatique], permite elegir entre varios estilos para la tabla HTML que se mostrará:

Elegimos [couleur i] para el [DataGrid] n.º i. Estas opciones de diseño se reflejan en el código de presentación de la página:
<html>
<head>
</head>
<body>
<form runat="server">
<p>
Liaison de données avec un DataGrid
</p>
<hr />
<p>
<table>
<tbody>
</tbody>
</table>
<table border="1">
<tbody>
<tr>
<td>
Array</td>
<td>
ArrayList</td>
<td>
DataTable</td>
<td>
DataSet</td>
</tr>
<tr>
<td>
<asp:DataGrid id="DataGrid1" runat="server" ShowHeader="False" CellPadding="4" BackColor="White" BorderColor="#CC9966" BorderWidth="1px" BorderStyle="None">
<FooterStyle forecolor="#330099" backcolor="#FFFFCC"></FooterStyle>
<HeaderStyle font-bold="True" forecolor="#FFFFCC" backcolor="#990000"></HeaderStyle>
<PagerStyle horizontalalign="Center" forecolor="#330099" backcolor="#FFFFCC"></PagerStyle>
<SelectedItemStyle font-bold="True" forecolor="#663399" backcolor="#FFCC66"></SelectedItemStyle>
<ItemStyle forecolor="#330099" backcolor="White"></ItemStyle>
</asp:DataGrid>
</td>
<td>
<asp:DataGrid id="DataGrid2" runat="server" ShowHeader="False" CellPadding="4" BackColor="White" BorderColor="#3366CC" BorderWidth="1px" BorderStyle="None">
<FooterStyle forecolor="#003399" backcolor="#99CCCC"></FooterStyle>
<HeaderStyle font-bold="True" forecolor="#CCCCFF" backcolor="#003399"></HeaderStyle>
<PagerStyle horizontalalign="Left" forecolor="#003399" backcolor="#99CCCC" mode="NumericPages"></PagerStyle>
<SelectedItemStyle font-bold="True" forecolor="#CCFF99" backcolor="#009999"></SelectedItemStyle>
<ItemStyle forecolor="#003399" backcolor="White"></ItemStyle>
</asp:DataGrid>
</td>
<td>
<asp:DataGrid id="DataGrid3" runat="server" ShowHeader="False" CellPadding="3" BackColor="#DEBA84" BorderColor="#DEBA84" BorderWidth="1px" BorderStyle="None" CellSpacing="2">
<FooterStyle forecolor="#8C4510" backcolor="#F7DFB5"></FooterStyle>
<HeaderStyle font-bold="True" forecolor="White" backcolor="#A55129"></HeaderStyle>
<PagerStyle horizontalalign="Center" forecolor="#8C4510" mode="NumericPages"></PagerStyle>
<SelectedItemStyle font-bold="True" forecolor="White" backcolor="#738A9C"></SelectedItemStyle>
<ItemStyle forecolor="#8C4510" backcolor="#FFF7E7"></ItemStyle>
</asp:DataGrid>
</td>
<td>
<asp:DataGrid id="DataGrid4" runat="server" ShowHeader="False" CellPadding="3" BackColor="White" BorderColor="#E7E7FF" BorderWidth="1px" BorderStyle="None" GridLines="Horizontal">
<FooterStyle forecolor="#4A3C8C" backcolor="#B5C7DE"></FooterStyle>
<HeaderStyle font-bold="True" forecolor="#F7F7F7" backcolor="#4A3C8C"></HeaderStyle>
<PagerStyle horizontalalign="Right" forecolor="#4A3C8C" backcolor="#E7E7FF" mode="NumericPages"></PagerStyle>
<SelectedItemStyle font-bold="True" forecolor="#F7F7F7" backcolor="#738A9C"></SelectedItemStyle>
<AlternatingItemStyle backcolor="#F7F7F7"></AlternatingItemStyle>
<ItemStyle forecolor="#4A3C8C" backcolor="#E7E7FF"></ItemStyle>
</asp:DataGrid>
</td>
</tr>
</tbody>
</table>
</p>
<asp:Button id="Button1" runat="server" Text="Envoyer"></asp:Button>
</form>
</body>
</html>
En el modo de diseño, solo hemos establecido propiedades de formato. Es en el código de control donde asociamos datos a los cuatro componentes:
<%@ Page Language="VB" %>
<%@ import Namespace="system.data" %>
<script runat="server">
' datos globales
dim textes1() as string={"un","deux","trois","quatre"}
dim textes2() as string={"one","two","three","for"}
dim valeurs() as string={"1","2","3","4"}
dim myDataListe as new ArrayList
dim myDataTable as new DataTable("table1")
dim myDataSet as new DataSet
' procedimiento ejecutado al cargar la página
Sub page_Load(sender As Object, e As EventArgs)
if not IsPostBack then
' se crean las fuentes de los datos que se van a vincular a los componentes
createDataSources
' vinculación a una tabla [Array]
bindToArray
' vinculación a una lista [ArrayList]
bindToArrayList
' vinculación a una tabla [DataTable]
bindToDataTable
' vinculación a un grupo de datos [DataSet]
bindToDataSet
end if
End Sub
sub createDataSources
' crea las fuentes de datos que se vincularán a los componentes
' arraylist
dim i as integer
for i=0 to textes1.length-1
myDataListe.add(textes1(i))
next
' tabla de datos
' se definen sus dos columnas
myDataTable.Columns.Add("id",Type.GetType("System.Int32"))
myDataTable.Columns.Add("texte1",Type.GetType("System.String"))
myDataTable.Columns.Add("texte2",Type.GetType("System.String"))
' se rellena la tabla
dim ligne as DataRow
for i=0 to textes1.length-1
ligne=myDataTable.NewRow
ligne("id")=valeurs(i)
ligne("texte1")=textes1(i)
ligne("texte2")=textes2(i)
myDataTable.Rows.Add(ligne)
next
' conjunto de datos: una sola tabla
myDataSet.Tables.Add(myDataTable)
end sub
' vinculación de tabla
sub bindToArray
with DataGrid1
.DataSource=textes1
.DataBind
end with
end sub
' vinculación de una lista de matrices
sub bindToArrayList
with DataGrid2
.DataSource=myDataListe
.DataBind
end with
end sub
' enlace de tabla de datos
sub bindToDataTable
with DataGrid3
.DataSource=myDataTable
.DataBind
end with
end sub
' vinculación de conjunto de datos
sub bindToDataSet
with DataGrid4
.DataSource=myDataSet
.DataBind
end with
end sub
</script>
<html>
...
</html>
El código es bastante similar al del ejemplo anterior, por lo que no lo comentaremos en detalle. Sin embargo, cabe destacar cómo se realiza la vinculación de datos. Para cada uno de los cuatro controles, basta con la siguiente secuencia:
donde [source de données] es del tipo [Array], [ArrayList], [DataTable] o [DataSet]. Así pues, vemos que se trata de una potente herramienta para mostrar datos en tablas:
- el formato se establece con [WebMatrix] o cualquier otro IDE en el momento del diseño
- la vinculación de datos se realiza en el código. Con [WebMatrix], puede realizarse en el momento del diseño si la fuente de datos es una base de datos SQL o una base de datos ACCESS. En este caso, se colocará un componente de tipo [SqlDataSource] o [AccessDataSource] en la hoja. Este componente puede vincularse, en el momento del diseño, a la fuente física de datos, ya sea una base de datos SQL Server o una base de datos ACCESS, según el caso. Si se asigna a la propiedad [DataSource] de un componente [DataGrid] un objeto [SqlDataSource] o [AccessDataSource] vinculado a una fuente física, aparecerán, en modo de diseño, los datos reales en el componente [DataGrid].
8.5. ViewState de los componentes de listas de datos
Aquí nos proponemos explicar el mecanismo del [VIEWSATE] para los componentes de listas de datos. Podemos dudar entre dos métodos para mantener el estado de un componente de lista de datos entre dos solicitudes del cliente:
- establecer su atributo [VIEWSTATE] en «verdadero»
- establecer su atributo [VIEWSTATE] en «falso» y almacenar su fuente de datos en la sesión para poder vincular el componente a dicha fuente en la siguiente solicitud.
El siguiente ejemplo ilustra algunos aspectos del mecanismo [VIEWSTATE] de los contenedores de datos. En la primera solicitud del cliente, la aplicación muestra la siguiente vista:
![]() |
n.º | nombre | tipo | propiedades | función |
1 | DataGrid | EnableViewState=true | muestra una fuente de datos S | |
2 | DataGrid | EnableViewState=true | muestra la misma fuente S que [DataGrid1] | |
3 | Botón | EnableViewState=false | botón [submit] |
El componente [DataGrid1] se gestiona mediante el mecanismo de [VIEWSTATE]. Se quiere saber si este mecanismo, que regenera la visualización de [DataGrid1] en cada solicitud, también regenera su fuente de datos. Para ello, esta está vinculada al componente [DataGrid2]. La generación de este último en cada consulta se realiza mediante un enlace explícito a la fuente de datos de [DataGrid1]. También tiene su atributo [EnableViewState] en [vrai].
El código de presentación [main.aspx] de la aplicación es el siguiente:
<%@ page src="main.aspx.vb" inherits="main" autoeventwireup="false" %>
<HTML>
<HEAD>
<title></title>
</HEAD>
<body>
<form runat="server">
<table>
<tr>
<td align="center">DataGrid 1</td>
<td align="center">
DataGrid 2</td>
</tr>
<tr>
<td>
<asp:DataGrid id="DataGrid1" runat="server" ...>
<SelectedItemStyle ...></SelectedItemStyle>
....
</asp:DataGrid></td>
<td>
<asp:DataGrid id="Datagrid2" runat="server" ...>
....
</asp:DataGrid></td>
</tr>
</table>
<asp:Button id="Button1" runat="server" Text="Envoyer"></asp:Button>
</form>
</body>
</HTML>
El controlador [main.aspx.vb] es el siguiente:
Imports System.Data
Public Class main
Inherits System.Web.UI.Page
Protected WithEvents DataGrid1 As System.Web.UI.WebControls.DataGrid
Protected WithEvents Datagrid2 As System.Web.UI.WebControls.DataGrid
Protected WithEvents Button1 As System.Web.UI.WebControls.Button
Private Sub Page_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
' en la primera consulta, la fuente de datos se define y se vincula a la primera cuadrícula de datos
If Not IsPostBack Then
'definir la fuente de datos
With DataGrid1
.DataSource = createDataSource()
.DataBind()
End With
End If
' En cada consulta, se vincula la cuadrícula de datos 2 con la fuente de la primera cuadrícula de datos
With Datagrid2
.DataSource = DataGrid1.DataSource
.DataBind()
End With
End Sub
Private Function createDataSource() As DataTable
' Se inicializa la fuente de datos
Dim thèmes As New DataTable
' columnas
With thèmes.Columns
.Add("id", GetType(System.Int32))
.Add("thème", GetType(System.String))
.Add("description", GetType(System.String))
End With
' la columna «id» será la clave primaria
thèmes.Constraints.Add("cléprimaire", thèmes.Columns("id"), True)
' filas
Dim ligne As DataRow
For i As Integer = 0 To 4
ligne = thèmes.NewRow
ligne.Item("id") = i.ToString
ligne.Item("thème") = "thème" + i.ToString
ligne.Item("description") = "description du thème " + i.ToString
thèmes.Rows.Add(ligne)
Next
Return thèmes
End Function
Private Sub InitializeComponent()
End Sub
End Class
El método [createDataSource] crea una fuente S de tipo [DataTable]. No nos detendremos en su código, ya que no es el objeto de este ejemplo. Lo que nos interesa es la forma en que se construyen los dos componentes [DataGrid]:
- el componente [DataGrid1] se vincula una vez a la tabla S, durante la primera consulta. A partir de entonces, ya no lo está.
- El componente [DataGrid2] se vincula a la fuente [DataGrid1.DataSource] en cada nueva consulta.
En la primera consulta, obtenemos la siguiente vista:

Como es lógico, ambos componentes muestran la fuente de datos a la que se han vinculado. Utilizamos el botón [Envoyer] para enviar una solicitud [PostBack] al servidor. La vista obtenida es entonces la siguiente:

Observamos que el componente [DataGrid1] ha conservado su valor, pero no así el componente [DataGrid2]. Explicaciones:
- antes incluso de que se inicie el procedimiento [Page_Load], los objetos [DataGrid1] y [DataGrid2] han recuperado el valor que tenían en la consulta anterior, debido al mecanismo del [viewstate]. De hecho, ambos tienen su propiedad [EnableViewState] establecida en [vrai].
- Se ejecuta el procedimiento [Page_Load]. Dado que se trata de una operación [PostBack], el componente [DataGrid1] no se ve modificado por [Page_Load] (véase el código). Por lo tanto, conserva el valor recuperado mediante [viewstate]. Así lo muestra la pantalla anterior.
- Por su parte, el componente [DataGrid2] está vinculado a [DataBind] y a la fuente de datos [DataGrid1.DataSource]. Por lo tanto, se reconstruye y se pierde el valor que acababa de recuperar mediante [viewstate]. Por lo tanto, habría sido conveniente que su propiedad pasara de [EnableViewState] a [faux] para evitar una gestión innecesaria de su estado. La pantalla anterior muestra que [DataGrid2] se ha vinculado a una fuente vacía. Dado que dicha fuente es [DataGrid1.DataSource], se deduce que, aunque el mecanismo de [viewstate] restaura correctamente la visualización del componente [DataGrid1], no restaura por ello sus propiedades, como [DataSource].
¿Qué conclusión se puede extraer de este ejemplo? Hay que evitar establecer la propiedad [EnableViewState] de un contenedor de datos en [vrai] si este debe vincularse (DataBind) a una fuente de datos en cada consulta. Sin embargo, hay ciertos casos en los que, incluso en esta situación, la propiedad [EnableViewState] del contenedor debe mantenerse en [vrai]; de lo contrario, no se activarán los eventos que se desea gestionar. Tendremos ocasión de ver un ejemplo de ello.
Con frecuencia, la fuente de datos de un contenedor de datos cambia a lo largo de las consultas. Por lo tanto, en cada consulta es necesario vincular el contenedor a la fuente de datos. Es habitual que esta se active en la sesión para que las consultas puedan acceder a ella. Nuestro segundo ejemplo ilustra este mecanismo. La aplicación solo proporciona una única vista:
![]() |
n.º | nombre | tipo | propiedades | función |
1 | DataGrid | EnableViewState=true | muestra una fuente de datos S | |
2 | DataGrid | EnableViewState=false | muestra la misma fuente S que [DataGrid1] | |
3 | Botón | EnableViewState=false | botón [submit] | |
4 | Etiqueta | EnableViewState=false | textos informativos |
El componente [DataGrid1] se vincula a los datos únicamente en la primera consulta. Mantendrá su valor a lo largo de las consultas gracias al mecanismo del [viewstate]. El componente [DataGrid2] se vincula en cada consulta a una fuente de datos a la que se añade un elemento en cada nueva consulta. Por lo tanto, es necesario vincular (DataBind) el componente [DataGrid2] en cada consulta. Por lo tanto, hemos establecido su atributo [EnableViewState] en [faux], tal y como se recomendó anteriormente. Así, en la segunda consulta (al utilizar el botón [Envoyer]), obtenemos la siguiente respuesta:

El componente [DataGrid1] ha conservado su valor inicial. El componente [DataGrid2] tiene un elemento más. Los tres datos [1,2,2] representan el número de solicitud. Se observa que uno de los datos es erróneo. Intentaremos comprender por qué.
El código de presentación [main.aspx] de la aplicación es el siguiente:
<%@ Page src="main.aspx.vb" inherits="main" autoeventwireup="false" Language="vb" %>
<HTML>
<HEAD>
<title></title>
</HEAD>
<body>
<form runat="server">
<table>
<tr>
<td align="center" bgColor="#ccffcc">DataGrid 1</td>
<td align="center" bgColor="#ffff99">DataGrid 2</td>
</tr>
<tr>
<td vAlign="top">
<asp:DataGrid id="DataGrid1" runat="server" ...>
...
</asp:DataGrid>
</td>
<td vAlign="top">
<asp:DataGrid id="Datagrid2" runat="server" ... EnableViewState="False">
....
</asp:DataGrid>
</td>
</tr>
</table>
<P>Numéro de requête :
<asp:Label id="lblInfo1" runat="server" EnableViewState="False"></asp:Label>,
<asp:Label id="lblInfo2" runat="server" EnableViewState="False"></asp:Label>,
<asp:Label id="lblInfo3" runat="server" EnableViewState="False"></asp:Label></P>
<P>
<asp:Button id="Button1" runat="server" Text="Envoyer" EnableViewState="False"></asp:Button></P>
</form>
</body>
</HTML>
El código del controlador [main.aspx.vb] es el siguiente:
Imports System.Data
Imports System
Public Class main
Inherits System.Web.UI.Page
Protected WithEvents DataGrid1 As System.Web.UI.WebControls.DataGrid
Protected WithEvents Datagrid2 As System.Web.UI.WebControls.DataGrid
Protected WithEvents Button1 As System.Web.UI.WebControls.Button
Protected WithEvents lblInfo1 As System.Web.UI.WebControls.Label
Protected WithEvents lblInfo2 As System.Web.UI.WebControls.Label
Protected WithEvents lblInfo3 As System.Web.UI.WebControls.Label
Dim dtThèmes As DataTable
Dim numRequête1 As Integer
Dim numRequête2 As Integer
Dim numRequête3 As New entier
Private Sub Page_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
' En la primera consulta, la fuente de datos está definida y vinculada a la primera tabla de datos
If Not IsPostBack Then
'definir la fuente de datos
dtThèmes = createDataSource()
With DataGrid1
.DataSource = dtThèmes
.DataBind()
End With
' almacenar información en la sesión
Session("source") = dtThèmes
numRequête1 = 0 : Session("numRequête1") = numRequête1
numRequête2 = 0 : Session("numRequête2") = numRequête2
numRequête3.valeur = 0 : Session("numRequête3") = numRequête3
End If
' En cada consulta, se añade un nuevo tema
dtThèmes = CType(Session("source"), DataTable)
Dim nbThèmes = dtThèmes.Rows.Count
Dim ligne As DataRow = dtThèmes.NewRow
With ligne
.Item("id") = nbThèmes.ToString
.Item("thème") = "thème" + nbThèmes.ToString
.Item("description") = "description du thème " + nbThèmes.ToString
End With
dtThèmes.Rows.Add(ligne)
': vincula la tabla de datos 2 con la fuente de datos
With Datagrid2
.DataSource = dtThèmes
.DataBind()
End With
' información sobre el número de consultas
numRequête1 = CType(Session("numRequête1"), Integer)
numRequête1 += 1
lblInfo1.Text = numRequête1.ToString
numRequête2 = CType(Session("numRequête2"), Integer)
numRequête2 += 1
lblInfo2.Text = numRequête2.ToString
numRequête3 = CType(Session("numRequête3"), entier)
numRequête3.valeur += 1
lblInfo3.Text = numRequête3.valeur.ToString
' se almacena cierta información en la sesión
Session("numRequête2") = numRequête2
End Sub
Private Function createDataSource() As DataTable
....
End Function
End Class
Public Class entier
Private _valeur As Integer
Public Property valeur() As Integer
Get
Return _valeur
End Get
Set(ByVal Value As Integer)
_valeur = Value
End Set
End Property
End Class
No hemos reproducido el código del método [createDataSource]. Es el mismo que en la aplicación anterior, con la única diferencia de que en el código fuente solo se incluyen tres líneas. Centrémonos primero en la gestión de la fuente de datos y de los dos contenedores:
Private Sub Page_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
' En la primera consulta, se define la fuente de datos y se vincula a la primera tabla de datos
If Not IsPostBack Then
'definir la fuente de datos
dtThèmes = createDataSource()
With DataGrid1
.DataSource = dtThèmes
.DataBind()
End With
' Almacenar información en la sesión
Session("source") = dtThèmes
...
End If
' En cada consulta, se añade un nuevo tema
dtThèmes = CType(Session("source"), DataTable)
Dim nbThèmes = dtThèmes.Rows.Count
Dim ligne As DataRow = dtThèmes.NewRow
With ligne
.Item("id") = nbThèmes.ToString
.Item("thème") = "thème" + nbThèmes.ToString
.Item("description") = "description du thème " + nbThèmes.ToString
End With
dtThèmes.Rows.Add(ligne)
': vincula la cuadrícula de datos 2 con la fuente de datos
With Datagrid2
.DataSource = dtThèmes
.DataBind()
End With
...
End Sub
El componente [DataGrid1] solo se vincula a la fuente de datos S durante la primera consulta (no IsPostBack). Esta se coloca entonces en la sesión. A partir de entonces, ya no se volverá a colocar. La fuente de datos S incluida en la sesión se recupera en cada consulta y se le añade una nueva línea. El componente [DataGrid2] se vincula explícitamente, en cada consulta, a la fuente S. Por eso su contenido aumenta en una línea con cada consulta. Se observa que, tras modificar el contenido de la fuente S, esta no se vuelve a incluir explícitamente en la sesión mediante una operación:
¿Por qué? Cuando se inicia una consulta, la sesión contiene un objeto Session(«fuente») de tipo [DataTable], que es la fuente de datos tal y como estaba en la última consulta. Llamemos S al objeto Session(«fuente»). Cuando escribimos:
dtThèmes = CType(Session("source"), DataTable)
[dtThèmes] y [S] son dos referencias al mismo objeto [DataTable]. Así, cuando el código de [Page_Load] añade un elemento a la tabla a la que hace referencia [dtThèmes], lo añade al mismo tiempo a la tabla a la que hace referencia [S]. Al finalizar la ejecución de la página, se guardarán todos los objetos presentes en la sesión y, por lo tanto, el objeto Session("source"), c.a.d. S, c.a.d y [dtThèmes]. Por lo tanto, lo que se guarda es efectivamente el nuevo contenido de la fuente de datos. No ha sido necesario escribir:
para realizar esta copia de seguridad, ya que Session("source") ya es igual a [dtThèmes]. Esto deja de ser así cuando los datos almacenados en la sesión no son objetos, como las estructuras [Integer, Float, ...]. Así lo muestra la gestión de los contadores de solicitudes:
Dim numRequête1 As Integer
Dim numRequête2 As Integer
Dim numRequête3 As New entier
Private Sub Page_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
' En la primera consulta, se define la fuente de datos y se vincula a la primera tabla de datos
....
' almacenar información en la sesión
Session("source") = dtThèmes
numRequête1 = 0 : Session("numRequête1") = numRequête1
numRequête2 = 0 : Session("numRequête2") = numRequête2
numRequête3.valeur = 0 : Session("numRequête3") = numRequête3
End If
' En cada consulta, se añade un nuevo tema
....
' Información sobre el número de consultas
numRequête1 = CType(Session("numRequête1"), Integer)
numRequête1 += 1
lblInfo1.Text = numRequête1.ToString
numRequête2 = CType(Session("numRequête2"), Integer)
numRequête2 += 1
lblInfo2.Text = numRequête2.ToString
numRequête3 = CType(Session("numRequête3"), entier)
numRequête3.valeur += 1
lblInfo3.Text = numRequête3.valeur.ToString
' se almacena cierta información en la sesión
Session("numRequête2") = numRequête2
End Sub
Private Function createDataSource() As DataTable
....
End Function
Private Sub InitializeComponent()
End Sub
End Class
Public Class entier
Private _valeur As Integer
Public Property valeur() As Integer
Get
Return _valeur
End Get
Set(ByVal Value As Integer)
_valeur = Value
End Set
End Property
Los contadores de solicitudes se almacenan en tres elementos:
- numRequête1 y numRequête2, de tipo [Integer]; [Integer] no es una clase, sino una estructura
- numRequête3, de tipo [entier]; [entier] es una clase definida específicamente para este caso
Cuando se escribe:
numRequête1 = CType(Session("numRequête1"), Integer)
..
numRequête2 = CType(Session("numRequête2"), Integer)
..
numRequête3 = CType(Session("numRequête3"), entier)
..
- La estructura [Session("numRequête1")] se copia en [numRequête1]. Así, cuando se modifica el elemento [numRequête1], el elemento [Session("numRequête1")] no se modifica
- Lo mismo ocurre con [Session("numRequête2")] y [numRequête2]
- Los elementos [Session("numRequête3")] y [numRequête3] son, ambos, referencias a un mismo objeto de tipo [entier]. El objeto al que se hace referencia puede modificarse indistintamente mediante cualquiera de las dos referencias.
De ello se deduce que es innecesario escribir:
para guardar el nuevo valor de [numRequête3] en la sesión. En cambio, habría que escribir:
para guardar los nuevos valores de las estructuras [numRequête1] y [numRequête2]. Solo lo hacemos para [numRequête2], lo que explica que, en la captura de pantalla obtenida tras la segunda consulta, el contador [numRequête1] sea erróneo.
Por lo tanto, hay que tener en cuenta que, una vez introducidos en la sesión, los datos no tienen que volver a introducirse repetidamente si están representados por un objeto. En los demás casos, hay que hacerlo si se han modificado.
8.6. Visualización de una lista de datos mediante un DataGrid paginado y ordenado
El componente [DataGrid] permite visualizar el contenido de un [DataSet]. Hasta ahora, siempre hemos creado nuestros [DataSet] «a mano». En esta ocasión, utilizamos un [DataSet] procedente de una base de datos. Creamos la siguiente aplicación MVC:
![]() |
Las tres vistas se incorporarán al código de presentación del controlador [main.aspx] en forma de contenedores. Por lo tanto, esta aplicación tiene una única página [main.aspx].
8.6.1. Las clases de negocio
La clase [produits] da acceso a la base de datos ACCESS siguiente:

El código de la clase [produits] es el siguiente:
Imports System
Imports System.Data
Imports System.Data.OleDb
Imports System.Xml
Namespace st.istia.univangers.fr
Public Class produits
Private chaineConnexionOLEDB As String
Public Sub New(ByVal chaineConnexionOLEDB As String)
' se almacena la cadena de conexión
Me.chaineConnexionOLEDB = chaineConnexionOLEDB
End Sub
Public Function getDataSet(ByVal commande As String) As DataSet
' se crea un objeto DataAdapter para leer los datos de la fuente OLEDB
Dim adaptateur As New OleDbDataAdapter(commande, chaineConnexionOLEDB)
' se crea una imagen en memoria del resultado de la consulta SELECT
Dim contenu As New DataSet
Try
adaptateur.Fill(contenu)
Catch e As Exception
Throw New Exception("Erreur d'accès à la base de données (" + e.Message + ")")
End Try
' se devuelve el resultado
Return contenu
End Function
End Class
End Namespace
La base ACCESS se gestionará mediante un controlador OLEDB. Al constructor de la clase [produits] se le proporciona la cadena de conexión, el identificador, el controlador OLEDB y la base de datos que se debe gestionar. La clase [produits] tiene un método [getDataSet] que devuelve un [DataSet] obtenido al ejecutaruna consulta SQL [select] cuyo texto se pasa como parámetro. Durante la ejecución del método, tienen lugar varias operaciones: creación de la conexión a la base de datos, ejecución de la consulta [select] y cierre de la conexión. Todo esto puede generar una excepción que aquí se gestiona. Un programa de prueba podría ser el siguiente:
Option Explicit On
Option Strict On
' espacios de nombres
Imports System
Imports System.Data
Imports Microsoft.VisualBasic
Namespace st.istia.univangers.fr
' página de prueba
Module testproduits
Sub Main(ByVal arguments() As String)
' muestra el contenido de una tabla de productos
' la tabla se encuentra en una base de datos ACCESS, cuya página recibe el nombre del archivo
Const syntaxe1 As String = "pg bdACCESS"
' comprobación de los parámetros del programa
If arguments.Length <> 1 Then
' mensaje de error
Console.Error.WriteLine(syntaxe1)
' fin
Environment.Exit(1)
End If
' Se prepara la cadena de conexión
Dim chaineConnexion As String = "Provider=Microsoft.Jet.OLEDB.4.0; Ole DB Services=-4; Data Source=" + arguments(0)
' creación de un objeto de productos
Dim objProduits As produits = New produits(chaineConnexion)
' Se recupera la tabla de productos en un conjunto de datos
Dim contenu As DataSet
Try
contenu = objProduits.getDataSet("select id,nom,prix from liste")
Catch ex As Exception
Console.Error.WriteLine(("L'erreur suivante s'est produite : " + ex.Message))
Environment.Exit(2)
End Try
' se muestra su contenido
Dim lignes As DataRowCollection = contenu.Tables(0).Rows
For i As Integer = 0 To lignes.Count - 1
' fila i de la tabla
Console.Out.WriteLine(lignes(i).Item("id").ToString + "," + lignes(i).Item("nom").ToString + _
"," + lignes(i).Item("prix").ToString)
Next
End Sub
End Module
End Namespace
La compilación de los distintos elementos de esta aplicación se realiza de la siguiente manera:
dos>vbc /t:library /r:system.dll /r:system.data.dll /r:system.xml.dll produits.vb
dos>vbc /r:produits.dll /r:system.data.dll /r:system.xml.dll /r:system.dll testproduits.vb
dos>dir
06/05/2004 16:52 118 784 produits.mdb
07/05/2004 11:07 902 produits.vb
07/04/2004 07:01 1 532 testproduits.vb
07/05/2004 14:21 3 584 produits.dll
07/05/2004 14:22 4 608 testproduits.exe
dos>testproduits produits.mdb
1,produit1,10
2,produit2,20
3,produit3,30
8.6.2. Las vistas
Ahora que ya tenemos la clase de acceso a los datos, vamos a escribir los controladores y las vistas de esta aplicación web. Veamos primero cómo funciona. La primera vista es la siguiente:
![]() |
n.º | nombre | tipo | propiedades | función |
1 | TextBox | EnableViewState=true | campo de introducción de la consulta SELECT | |
2 | RequiredFieldValidator | EnableViewState=false | comprueba si hay un 1 | |
3 | TextBox | EnableViewState=true | campo de entrada: indica el número de líneas de datos que se mostrarán por página de resultados | |
4 | RequiredFieldValidator | EnableViewState=false | comprueba si hay 3 | |
5 | RangeValidator | EnableViewState=false | comprueba que (3) esté dentro del intervalo [1,30] | |
6 | Botón | EnableViewState=false | botón [submit] |
A esta vista la llamaremos «[formulaire]». Permite al usuario ejecutar una consulta SELECT «SQL» en la base de datos «[produits.mdb]». La ejecución de la consulta da lugar a una nueva vista:
![]() |
n.º | nombre | tipo | propiedades | función |
1 | Etiqueta | EnableViewState=false | campo de información | |
2 | RadioButton | EnableViewState=false GroupName=rdTri | permite elegir un orden de clasificación | |
3 | DataGrid | EnableViewState=true AllowPaging=true AllowSorting=true | tabla de visualización del resultado de la selección | |
4 | LinkButton | EnableViewState=false | botón-enlace [submit] |
A esta vista la llamaremos «[résultats]». Es esta la que contiene el [DataGrid] que mostrará el resultado del comando «select» de SQL. El usuario puede cometer un error en su consulta. Algunos errores se le señalarán en la vista [erreurs] gracias a los controles de validación.

Otros errores se le indicarán en la vista [erreurs]:

Se muestra la vista [erreurs]:
![]() |
n.º | nombre | tipo | propiedades | función |
1 | variable | código HTML necesario para mostrar los errores | ||
3 | LinkButton | EnableViewState=false | botón-enlace [submit] |
Las tres vistas de la aplicación son tres contenedores (paneles) diferentes dentro de la misma página [main.aspx]. Su código de presentación es el siguiente:
<%@ Page src="main.aspx.vb" inherits="main" autoeventwireup="false" Language="vb" %>
<HTML>
<HEAD>
</HEAD>
<body>
<P>Liaison de données avec un DataGrid</P>
<HR width="100%" SIZE="1">
<form runat="server">
<asp:panel id="vueFormulaire" runat="server">
<P>Commande [select] à exécuter sur la table LISTE</P>
<P> Exemple : select id, nom, prix from LISTE
</P>
<P>
<asp:TextBox id="txtSelect" runat="server" Columns="60"></asp:TextBox></P>
<P>
<asp:RequiredFieldValidator id="RequiredFieldValidator1" runat="server" EnableViewState="False" EnableClientScript="False"
ErrorMessage="Indiquez la requête [select] à exécuter" ControlToValidate="txtSelect" Display="Dynamic"></asp:RequiredFieldValidator></P>
<P>Nombre de lignes par page :
<asp:TextBox id="txtPages" runat="server" Columns="3"></asp:TextBox></P>
<P>
<asp:RequiredFieldValidator id="RequiredFieldValidator2" runat="server" EnableViewState="False" EnableClientScript="False"
ErrorMessage="Indiquez le nombre de lignes par page désirées" ControlToValidate="txtPages" Display="Dynamic"></asp:RequiredFieldValidator>
<asp:RangeValidator id="RangeValidator1" runat="server" EnableViewState="False" EnableClientScript="False"
ErrorMessage="Vous devez indiquer un nombre entre 1 et 30" ControlToValidate="txtPages" Display="Dynamic"
Type="Integer" MinimumValue="1" MaximumValue="30"></asp:RangeValidator></P>
<P>
<asp:Button id="btnExécuter" runat="server" EnableViewState="False" Text="Exécuter"></asp:Button></P>
</asp:panel>
<asp:Panel id="vueRésultats" runat="server">
<P>Résultats de la requête
<asp:Label id="lblSelect" runat="server"></asp:Label></P>
<P>Tri
<asp:RadioButton id="rdCroissant" runat="server" Text="croissant" Checked="True" GroupName="rdTri"></asp:RadioButton>
<asp:RadioButton id="rdDécroissant" runat="server" Text="décroissant" GroupName="rdTri"></asp:RadioButton></P>
<P>
<asp:DataGrid id="DataGrid1" runat="server" BorderColor="#CC9966" BorderStyle="None" BorderWidth="1px"
BackColor="White" CellPadding="4" AllowPaging="True" PageSize="4" AllowSorting="True">
<SelectedItemStyle Font-Bold="True" ForeColor="#663399" BackColor="#FFCC66"></SelectedItemStyle>
<ItemStyle ForeColor="#330099" BackColor="White"></ItemStyle>
<HeaderStyle Font-Bold="True" ForeColor="#FFFFCC" BackColor="#990000"></HeaderStyle>
<FooterStyle ForeColor="#330099" BackColor="#FFFFCC"></FooterStyle>
<PagerStyle NextPageText="Suivant" PrevPageText="Précédent" HorizontalAlign="Center"
ForeColor="#330099" BackColor="#FFFFCC"></PagerStyle>
</asp:DataGrid></P>
<P>
<asp:LinkButton id="lnkRésultats" runat="server" EnableViewState="False">Retour au formulaire</asp:LinkButton></P>
</asp:Panel>
<asp:Panel id="vueErreurs" runat="server">
<P>Les erreurs suivantes se sont produites :</P>
<% =erreursHTML %>
<P>
<asp:LinkButton id="lnkErreurs" runat="server" EnableViewState="False">Retour vers le formulaire</asp:LinkButton></P>
</asp:Panel>
</form>
</body>
</HTML>
8.6.3. Configuración del DataGrid
Centrémonos en la paginación del componente [DataGrid], una opción con la que nos encontramos por primera vez. En el código anterior, esta paginación se controla mediante los siguientes atributos:
<asp:DataGrid id="DataGrid1" runat="server" PageSize="4" AllowPaging="True" ...>
...
<PagerStyle NextPageText="Suivant" PrevPageText="Précédent" ...></PagerStyle>
</asp:DataGrid></P>
permite la paginación | |
cuatro líneas de datos por página | |
Texto del enlace para ir a la página siguiente de la fuente de datos | |
El texto del enlace para ir a la página anterior de la fuente de datos |
Esta información se puede introducir directamente en los atributos de la etiqueta <asp:datagrid>. También se puede utilizar [WebMatrix]. En la ventana de propiedades de [DataGrid], hay que seguir el enlace [Générateur de propriétés]:

Aparecerá el siguiente asistente:

Seleccionamos la opción [Pagination]:

Arriba podemos ver los valores de los atributos de paginación del [DataGrid] del código de presentación.
Además, permitimos la ordenación de los datos en una de las columnas del [DataGrid]. Hay diferentes formas de hacerlo. Una de ellas es definir la propiedad [AllowSorting=true] en la ventana de propiedades del [DataGrid]. También se puede utilizar el generador de propiedades. Sea cual sea el método utilizado, esto se traduce en la presencia del atributo [AllowSorting=true] en la etiqueta <asp:DataGrid> del componente:
<asp:DataGrid id="DataGrid1" runat="server" ... AllowPaging="True" PageSize="4" AllowSorting="True">
8.6.4. Los controladores
El controlador [global.asax, global.asax.vb] es el siguiente:
[global.asax]
[global.asax.vb]
Imports st.istia.univangers.fr
Imports System.Configuration
Public Class Global
Inherits System.Web.HttpApplication
Sub Application_Start(ByVal sender As Object, ByVal e As EventArgs)
' se crea un objeto de productos
Dim objProduits As produits
Try
objProduits = New produits(ConfigurationSettings.AppSettings("OLEDBStringConnection"))
' se inserta el objeto en la aplicación
Application("objProduits") = objProduits
' sin errores
Application("erreur") = False
Catch ex As Exception
'se ha producido un error; se anota en la aplicación
Application("erreur") = True
Application("message") = ex.Message
End Try
End Sub
End Class
Cuando se inicia la aplicación (Application_Start), creamos un objeto [produits] y lo incorporamos a la aplicación para que esté disponible para todas las solicitudes de todos los clientes. Si se produce una excepción durante esta creación, se registra en la aplicación. El procedimiento [Application_Start] solo se ejecutará una vez. A partir de entonces, el controlador [global.asax] ya no intervendrá. Será el controlador [main.aspx.vb] el que se encargue del trabajo:
Imports System.Collections
Imports Microsoft.VisualBasic
Imports System.Data
Imports st.istia.univangers.fr
Imports System
Imports System.Xml
Public Class main
Inherits System.Web.UI.Page
' componentes de la página
Protected WithEvents txtSelect As System.Web.UI.WebControls.TextBox
Protected WithEvents RequiredFieldValidator1 As System.Web.UI.WebControls.RequiredFieldValidator
Protected WithEvents txtPages As System.Web.UI.WebControls.TextBox
Protected WithEvents RequiredFieldValidator2 As System.Web.UI.WebControls.RequiredFieldValidator
Protected WithEvents RangeValidator1 As System.Web.UI.WebControls.RangeValidator
Protected WithEvents btnExécuter As System.Web.UI.WebControls.Button
Protected WithEvents vueFormulaire As System.Web.UI.WebControls.Panel
Protected WithEvents lblSelect As System.Web.UI.WebControls.Label
Protected WithEvents DataGrid1 As System.Web.UI.WebControls.DataGrid
Protected WithEvents lnkRésultats As System.Web.UI.WebControls.LinkButton
Protected WithEvents vueRésultats As System.Web.UI.WebControls.Panel
Protected WithEvents lnkErreurs As System.Web.UI.WebControls.LinkButton
Protected WithEvents vueErreurs As System.Web.UI.WebControls.Panel
Protected WithEvents rdCroissant As System.Web.UI.WebControls.RadioButton
Protected WithEvents rdDécroissant As System.Web.UI.WebControls.RadioButton
' Datos de la página
Protected erreursHTML As String
Private Sub Page_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
' se comprueba si la aplicación presenta algún error
If CType(Application("erreur"), Boolean) Then
' La aplicación no se ha inicializado correctamente
Dim erreurs As New ArrayList
erreurs.Add("Application momentanément indisponible (" + CType(Application("message"), String) + ")")
afficheErreurs(erreurs, False)
Exit Sub
End If
'Primera consulta
If Not IsPostBack Then
' se muestra el formulario vacío
afficheFormulaire()
End If
End Sub
Private Sub afficheErreurs(ByVal erreurs As ArrayList, ByVal afficheLien As Boolean)
' muestra la vista de errores
erreursHTML = ""
For i As Integer = 0 To erreurs.Count - 1
erreursHTML += "<li>" + erreurs(i).ToString + "</li>" + ControlChars.CrLf
Next
lnkErreurs.Visible = afficheLien
' se muestra la vista [erreurs]
vueErreurs.Visible = True
vueFormulaire.Visible = False
vueRésultats.Visible = False
End Sub
Private Sub afficheFormulaire()
' se muestra la vista [formulaire]
vueFormulaire.Visible = True
vueErreurs.Visible = False
vueRésultats.Visible = False
End Sub
Private Sub afficheRésultats(ByVal sqlTexte As String, ByVal données As DataView)
' se inicializan los controles
lblSelect.Text = sqlTexte
With DataGrid1
.DataSource = données
.PageSize = CType(txtPages.Text, Integer)
.CurrentPageIndex = 0
.DataBind()
End With
' se muestra la vista [résultats]
vueRésultats.Visible = True
vueFormulaire.Visible = False
vueErreurs.Visible = False
End Sub
Private Sub btnExécuter_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnExécuter.Click
' ¿Página válida?
If Not Page.IsValid Then
afficheFormulaire()
Exit Sub
End If
' ejecución de la consulta del cliente SELECT
Dim données As DataView
Try
données = CType(Application("objProduits"), produits).getDataSet(txtSelect.Text.Trim).Tables(0).DefaultView
Catch ex As Exception
Dim erreurs As New ArrayList
erreurs.Add("erreur d'accès à la base de données (" + ex.Message + ")")
afficheErreurs(erreurs, True)
Exit Sub
End Try
' Todo va bien: se muestran los resultados
afficheRésultats(txtSelect.Text.Trim, données)
' se guardan los datos en la sesión
Session("données") = données
End Sub
Private Sub retourFormulaire(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles lnkErreurs.Click, lnkRésultats.Click
' conjunto de vistas
vueErreurs.Visible = False
vueFormulaire.Visible = True
vueRésultats.Visible = False
End Sub
Private Sub DataGrid1_PageIndexChanged(ByVal source As Object, ByVal e As System.Web.UI.WebControls.DataGridPageChangedEventArgs) Handles DataGrid1.PageIndexChanged
' cambio de página
With DataGrid1
.CurrentPageIndex = e.NewPageIndex
.DataSource = CType(Session("données"), DataView)
.DataBind()
End With
End Sub
Private Sub DataGrid1_SortCommand(ByVal source As Object, ByVal e As System.Web.UI.WebControls.DataGridSortCommandEventArgs) Handles DataGrid1.SortCommand
' se ordena la vista de datos
Dim données As DataView = CType(Session("données"), DataView)
données.Sort = e.SortExpression + " " + CType(IIf(rdCroissant.Checked, "asc", "desc"), String)
' se muestra
With DataGrid1
.CurrentPageIndex = 0
.DataSource = données
.DataBind()
End With
End Sub
End Class
Al cargar la página [Page_Load], lo primero que comprobamos es si la aplicación se ha inicializado correctamente. Si no es así, se muestra la vista [erreurs] sin enlace de retorno al formulario, ya que dicho enlace resulta innecesario en ese caso. De hecho, solo se puede mostrar la vista [erreurs] si la aplicación no se ha podido inicializar correctamente. En caso contrario, se muestra la vista [formulaire] si se trata de la primera solicitud del cliente. Por lo demás, dejamos que sea el lector quien interprete el código. Nos centraremos únicamente en tres procedimientos: el procedimiento [btnExécuter_Click], que se ejecuta cuando el usuario ha solicitado la ejecución de la consulta SQL introducida en la vista [formulaire], el procedimiento [DataGrid1_PageIndexChanged], que se ejecuta cuando elusuario utiliza los enlaces [Suivant] y [Précédent] de [DataGrid], y el procedimiento [DataGrid1_SortCommand], que se ejecuta cuando el usuario hace clic en el título de una columna para ordenar los datos según elorden de la misma. El sentido del orden, ascendente o descendente, se establece mediante los dos botones de radio de ordenación.
Por lo tanto, en el procedimiento [btnExécuter_Click] se empieza por comprobar si la página es válida o no. Cuando se ejecuta el procedimiento [btnExécuter_Click], ya se han realizado las comprobaciones relacionadas con los distintos controles de validación de la página. Para cada control de validación, se han establecido dos atributos:
se establece en «verdadero» si los datos comprobados son válidos, y en «falso» en caso contrario | |
el mensaje de error si los datos verificados resultan inválidos |
Para la propia página, se ha establecido un atributo [IsValid]. Este atributo solo tiene el valor «verdadero» si todos los controles de validación tienen su atributo [IsValid] en «verdadero». Si no es así, hay que mostrar la vista [formulaire]. Esta contiene los controles de validación que mostrarán su atributo [errorMessage]. Si la página es válida, se utiliza el objeto de tipo [produits] creado por [Application_Start] para obtener el [DataSet] correspondiente a la ejecución de la consulta SELECT SQL. Este se transforma en el objeto [DataView]:
Dim données As DataView
Try
données = CType(Application("objProduits"), produits).getDataSet(txtSelect.Text.Trim).Tables(0).DefaultView
...
Se podría haber trabajado simplemente con el [DataSet] y escribir:
Dim données As DataSet
Try
données = CType(Application("objProduits"), produits).getDataSet(txtSelect.Text.Trim)
...
Un objeto [DataSet] es, en esencia, un conjunto de tablas vinculadas entre sí mediante relaciones. En nuestra aplicación concreta, el [DataSet] obtenido a partir de la clase [produits] solo contiene una tabla, la resultante de la instrucción [select]. Una tabla se puede ordenar, mientras que un [DataSet] no, y lo que nos interesa es ordenar los datos obtenidos. Para trabajar con la tabla resultante de [select], podríamos haber escrito:
Dim données As DataTable
Try
données = CType(Application("objProduits"), produits).getDataSet(txtSelect.Text.Trim).Tables(0)
...
El objeto [DataTable], aunque representa una tabla de base de datos, no dispone de método de ordenación. Para ello, es necesario disponer de una vista de la tabla. Una vista es un objeto de tipo [DataView]. Se pueden obtener diferentes vistas de una misma tabla mediante filtros. Una tabla tiene una vista por defecto, que es aquella en la que no se ha definido ningún filtro. Por lo tanto, representa la totalidad de la tabla. Esta vista por defecto se obtiene mediante [DataTable.DefaultView]. Se puede ordenar una vista mediante su propiedad [sort], sobre la que volveremos más adelante.
Si la obtención del [DataSet] de la clase [produits] se realiza correctamente, se muestra la vista [résultats]; de lo contrario, se muestra la vista [erreurs]. La visualización de la vista [résultats] se realiza mediante el procedimiento [afficheRésultats], al que se le pasan dos parámetros:
- el texto que se va a colocar en la etiqueta [lblSelect]
- el [DataView] que se va a vincular a [DataGrid1]
Este ejemplo nos muestra la gran flexibilidad del componente [DataGrid]. Es capaz de reconocer la estructura del [DataView] al que se vincula y adaptarse a ella. Por último, el procedimiento [btnExécuter_Click] almacena el [DataView] que acaba de obtener en la sesión del usuario para poder disponer de él cuando este solicite otras páginas del mismo [DataView].
El procedimiento [DataGrid1_PageIndexChanged] se ejecuta cuando el usuario utiliza los enlaces [Suivant] y [Précédent] del [DataGrid]. Recibe dos parámetros:
Private Sub DataGrid1_PageIndexChanged(ByVal source As Object, ByVal e As System.Web.UI.WebControls.) Handles DataGrid1.PageIndexChanged
el objeto origen del evento —en este caso, uno de los enlaces [Suivant] o [Précédent] | |
información sobre el evento. El atributo e.NewPageIndex es el número de página que se debe mostrar para responder a la solicitud del cliente |
El código completo del gestor de eventos es el siguiente:
Private Sub DataGrid1_PageIndexChanged(ByVal source As Object, ByVal e As System.Web.UI.WebControls.DataGridPageChangedEventArgs) Handles DataGrid1.PageIndexChanged
' cambio de página
With DataGrid1
.CurrentPageIndex = e.NewPageIndex
.DataSource = CType(Session("données"), DataView)
.DataBind()
End With
End Sub
El componente [DataGrid] tiene un atributo [CurrentPageIndex] que indica el número de la página que muestra o va a mostrar. A este atributo se le asigna el valor [NewPageIndex] del parámetro [e]. A continuación, el [DataGrid] se asocia al [DataView] que se había almacenado en la sesión mediante el procedimiento [btnExécuter_Click].
Cabe preguntarse si el [DataGrid] necesita el atributo [EnableViewState=true], ya que su contenido lo calcula el código cada vez que se ejecuta la página. Se podría pensar que no. Sin embargo, si el [DataGrid] tiene el atributo [EnableViewState=false], se observa que el evento [DataGrid1.PageIndexChanged] nunca se activa. Por eso se ha dejado el [EnableViewState=true]. Sabemos que esto hace que el contenido de [DataGrid] se coloque en el campo oculto [__VIEWSTATE] de la página. Esto puede provocar una sobrecarga significativa de la página si el [DataGrid] es grande. Si esto resulta molesto, se puede gestionar la paginación manualmente sin utilizar la paginación automática del [DataGrid].
El procedimiento [DataGrid1_SortCommand] se ejecuta cuando el usuario hace clic en el título de una de las columnas mostradas por el [DataGrid] para solicitar que los datos se ordenen según el orden de dicha columna. Recibe dos parámetros:
Private Sub DataGrid1_SortCommand(ByVal source As Object, ByVal e As System.Web.UI.WebControls.DataGridSortCommandEventArgs) Handles DataGrid1.SortCommand
el objeto origen del evento —en este caso, uno de los enlaces [Suivant] o [Précédent] | |
información sobre el evento. El atributo [e.SortExpression] es el nombre de la columna en la que se ha hecho clic para ordenar |
El código completo del gestor de eventos es el siguiente:
Private Sub DataGrid1_SortCommand(ByVal source As Object, ByVal e As System.Web.UI.WebControls.DataGridSortCommandEventArgs) Handles DataGrid1.SortCommand
' se ordena la vista de datos
Dim données As DataView = CType(Session("données"), DataView)
données.Sort = e.SortExpression + " " + CType(IIf(rdCroissant.Checked, "asc", "desc"), String)
' se muestra
With DataGrid1
.CurrentPageIndex = 0
.DataSource = données
.DataBind()
End With
End Sub
Se recupera el [DataView] visualizado por el [DataGrid] en la sesión actual. Fue el procedimiento [btnExécuter_Click] el que lo colocó allí. El componente [DataView] tiene una propiedad [Sort] a la que se le asigna la expresión de ordenación. Esta obedece a la sintaxis [select ... order by expr1, expr2, ...], en la que cada [expri] puede ir seguido de la palabra clave [asc] para una ordenación ascendente o de [desc] para una ordenación descendente. La expresión [order by] utilizada aquí es [order by colonne asc/desc]. La propiedad [e.SortExpression] nos proporciona el nombre de la columna de [DataGrid] en la que se ha hecho clic para ordenar. La cadena [asc/desc] se establece en función de los valores de los botones de opción del grupo [rdTri]. Una vez establecida la expresión de ordenación del [DataView], el [DataGrid] se asocia a este. Se coloca el [DataGrid] en su primera página.
8.7. Componente DataList y enlace de datos
Ahora nos centramos en el componente [DataList]. Ofrece más posibilidades de formato que el [DataGrid], pero es menos flexible. Por lo tanto, no puede adaptarse automáticamente a la fuente de datos a la que está vinculado. Si se desea esta adaptación, hay que realizarla mediante código. Si se conoce de antemano la estructura de la fuente de datos, este componente ofrece opciones de formato que pueden hacer que sea preferible al [DataGrid].
8.7.1. Aplicación
Para ilustrar el uso del [DataList], creamos una aplicación MVC análoga a la anterior:
![]() |
Las tres vistas se incorporarán al código de presentación del controlador [main.aspx] en forma de contenedores. Por lo tanto, esta aplicación tiene una única página [main.aspx].
8.7.2. Las clases de negocio
La clase [produits] es la misma que la anterior.
8.7.3. Las vistas
Cuando el usuario realiza su primera solicitud a la aplicación, obtiene la siguiente vista [résultats1]:
![]() |
n.º | nombre | tipo | propiedades | función |
1 | RadioButton | EnableViewState=false | permite elegir uno de los dos estilos de [DataList] | |
2 | Botón | EnableViewState=false | botón [submit] | |
3 | DataList | EnableViewState=true | Campo de visualización de la lista de datos |
Si el usuario elige el estilo n.º 2, obtendrá la vista [résultats2] siguiente:
![]() |
n.º | nombre | tipo | propiedades | función |
1 | DataList | EnableViewState=true | Campo de visualización de la lista de datos |
La vista [erreurs] indica un problema de acceso a la fuente de datos:
![]() |
n.º | nombre | tipo | propiedades | función |
1 | variable | código HTML necesario para mostrar los errores |
Las tres vistas de la aplicación son tres contenedores (panel) diferentes dentro de la misma página [main.aspx]. Su código de presentación es el siguiente:
<%@ Page src="main.aspx.vb" inherits="main" autoeventwireup="false" Language="vb" %>
<HTML>
<HEAD>
</HEAD>
<body>
<P>Liaison de données avec un DataList</P>
<HR width="100%" SIZE="1">
<form runat="server" ID="Form1">
<asp:Panel Runat="server" ID="bandeau">
<P>Choisissez votre style :
<asp:RadioButton id="RadioButton1" runat="server" EnableViewState="False" Text="1" GroupName="rdstyle"
Checked="True"></asp:RadioButton>
<asp:RadioButton id="RadioButton2" runat="server" EnableViewState="False" Text="2" GroupName="rdstyle"></asp:RadioButton>
<asp:Button id="btnChanger" runat="server" EnableViewState="False" Text="Changer"></asp:Button></P>
<HR width="100%" SIZE="1">
</asp:Panel>
<asp:Panel id="vueRésultats1" runat="server">
<P>
<asp:DataList id="DataList1" runat="server" BorderColor="Tan" BorderWidth="1px" BackColor="LightGoldenrodYellow"
CellPadding="2" RepeatDirection="Horizontal" RepeatColumns="4" ForeColor="Black">
<SelectedItemStyle ForeColor="GhostWhite" BackColor="DarkSlateBlue"></SelectedItemStyle>
<HeaderTemplate>
Contenu de la table [liste] de la base [produits]
</HeaderTemplate>
<AlternatingItemStyle BackColor="PaleGoldenrod"></AlternatingItemStyle>
<ItemTemplate>
nom :
<%# Container.DataItem("nombre") %>
<br>
prix :
<%# databinder.eval(Container.DataItem,"precio","{0:C}") %>
<br>
</ItemTemplate>
<FooterStyle BackColor="Tan"></FooterStyle>
<HeaderStyle Font-Bold="True" BackColor="Tan"></HeaderStyle>
</asp:DataList></P>
</asp:Panel>
<asp:Panel id="vueRésultats2" runat="server">
<P>
<asp:DataList id="DataList2" runat="server">
<HeaderTemplate>
Contenu de la table [liste] de la base [produits]
<HR width="100%" SIZE="1">
</HeaderTemplate>
<AlternatingItemStyle BackColor="Teal"></AlternatingItemStyle>
<SeparatorStyle BackColor="LightSkyBlue"></SeparatorStyle>
<ItemStyle BackColor="#C0C000"></ItemStyle>
<ItemTemplate>
nom :
<%# Container.DataItem("nombre") %>
, prix :
<%# databinder.eval(Container.DataItem,"precio","{0:C}") %>
<BR>
</ItemTemplate>
<SeparatorTemplate>
<HR width="100%" SIZE="1">
</SeparatorTemplate>
<HeaderStyle BackColor="#C0C0FF"></HeaderStyle>
</asp:DataList></P>
</asp:Panel>
<asp:Panel id="vueErreurs" runat="server">
<P>Les erreurs suivantes se sont produites :</P>
<%= erreursHTML %>
</asp:Panel>
</form>
</body>
</HTML>
8.7.4. Configuración de los componentes [DataList]
Veamos los diferentes atributos de un componente [DataList]. Son muy numerosos y aquí solo presentamos una pequeña parte. Se pueden definir hasta siete plantillas de visualización dentro de un [DataList]:
Plantilla del encabezado de [DataList] | |
Plantilla de las líneas que muestran los elementos de la lista de datos asociada. Solo esta plantilla es obligatoria. | |
Para diferenciar visualmente los elementos sucesivos que se muestran, se pueden utilizar dos plantillas: ItemTemplate para el elemento n, y AlternatingItemTemplate para el elemento n+1 | |
Plantilla del elemento seleccionado en [DataList] | |
plantilla del separador entre dos elementos del [DataList] | |
Un [DataList] permite modificar los valores que muestra. [EditItemTemplate] es la plantilla de un elemento del [DataList] para el que se encuentra en modo «edición» | |
plantilla del pie de página del [DataList] |
El componente [DataList1] se ha creado a partir de [WebMatrix]. En su ventana de propiedades se ha seleccionado el enlace [Mise en forme automatique]:
![]() | 1234567 ![]() |
En el ejemplo anterior, el esquema [Couleur 5] generará un [DataList] con estilos para las siguientes plantillas: HeaderTemplate (1), ItemTemplate (2, 6), AlternatingTemplate (3, 5), SelectedItemTemplate (4), FooterTemplate (7). El código generado es el siguiente:
<asp:DataList id="DataList1" runat="server" BorderColor="Tan" BorderWidth="1px" BackColor="LightGoldenrodYellow"
CellPadding="2" ForeColor="Black">
<SelectedItemStyle ForeColor="GhostWhite" BackColor="DarkSlateBlue"></SelectedItemStyle>
<AlternatingItemStyle BackColor="PaleGoldenrod"></AlternatingItemStyle>
<FooterStyle BackColor="Tan"></FooterStyle>
<HeaderStyle Font-Bold="True" BackColor="Tan"></HeaderStyle>
</asp:DataList></P>
No hay ninguna plantilla definida. Nos toca a nosotros hacerlo. Definimos las siguientes plantillas:
| |
|
Recordemos que el modelo [ItemTemplate] es el que muestra los elementos de la fuente de datos vinculada a [DataList]. Esta fuente de datos es un conjunto de líneas de datos, cada una de las cuales contiene uno o varios valores. La línea actual de la fuente de datos está representada por el objeto [Container.DataItem]. Dicha línea tiene columnas. [Container.DataItem("col1")] es el valor de la columna «col1» de la línea actual. Para incluir este valor en el código de presentación, se escribe <%# Container.DataItem("col") %>. A veces, se desea presentar un elemento de la línea actual en un formato especial. En este caso, queremos mostrar la columna «precio» de la línea actual en euros. Para ello, utilizamos la función [DataBinder.Eval], que admite tres parámetros:
- la línea actual [Container.DataItem]
- el nombre de la columna que se va a formatear
- la cadena de formato en el formato {0:formato}, donde [format] es uno de los formatos aceptados por el método [string.format].
Así, el código <%# DataBinder.Eval(Container.DataItem,"precio",{0:C}) %> mostrará la columna [prix] de la línea actual en formato monetario (formato C = Currency).
Por lo tanto, tendremos un [DataList] que tendrá este aspecto:

En el ejemplo anterior, los datos se han distribuido a razón de cuatro datos por línea. Esto se consigue con los siguientes atributos de [DataList]:
Horizontal | |
número de columnas deseado |
Al final, el código de [DataList1] es el que se ha presentado en el código de presentación un poco más arriba. Dejamos al lector la tarea de estudiar el código de presentación de [DataList2]. Al igual que con el componente [DataGrid], la mayoría de las propiedades de [DataList] se pueden configurar mediante un asistente de [WebMatrix]. Para ello, se utiliza el enlace [Générateur de propriétés] de la ventana de propiedades de [DataList]:
![]() | ![]() |
8.7.5. Los controladores
El controlador [global.asax, global.asax.vb] es el siguiente:
[global.asax]
[global.asax.vb]
Imports System
Imports System.Web
Imports System.Web.SessionState
Imports st.istia.univangers.fr
Imports System.Configuration
Imports System.Data
Imports System.Collections
Public Class Global
Inherits System.Web.HttpApplication
Sub Application_Start(ByVal sender As Object, ByVal e As EventArgs)
' se crea un objeto «productos»
Try
Dim données As DataSet = New produits(ConfigurationSettings.AppSettings("OLEDBStringConnection")).getDataSet("select * from LISTE")
' se introduce el objeto en la aplicación
Application("données") = données
' sin errores
Application("erreur") = False
Catch ex As Exception
'se ha producido un error; se anota en la aplicación
Application("erreur") = True
Application("message") = ex.Message
End Try
End Sub
End Class
Cuando se inicia la aplicación (Application_Start), creamos un [dataset] a partir de la clase de negocio [produits] y lo incorporamos a la aplicación para que esté disponible para todas las consultas de todos los clientes. Si se produce una excepción durante esta creación, se registra en la aplicación. El procedimiento [Application_Start] solo se ejecutará una vez. A partir de ese momento, el controlador [global.asax] ya no intervendrá. Será el controlador [main.aspx.vb] el que se encargue del trabajo:
Imports System.Collections
Imports Microsoft.VisualBasic
Imports System.Data
Imports st.istia.univangers.fr
Imports System
Imports System.Xml
Public Class main
Inherits System.Web.UI.Page
' componentes de la página
Protected WithEvents vueErreurs As System.Web.UI.WebControls.Panel
Protected WithEvents DataList1 As System.Web.UI.WebControls.DataList
Protected WithEvents DataList2 As System.Web.UI.WebControls.DataList
Protected WithEvents vueRésultats1 As System.Web.UI.WebControls.Panel
Protected WithEvents vueRésultats2 As System.Web.UI.WebControls.Panel
Protected WithEvents RadioButton1 As System.Web.UI.WebControls.RadioButton
Protected WithEvents RadioButton2 As System.Web.UI.WebControls.RadioButton
Protected WithEvents btnChanger As System.Web.UI.WebControls.Button
Protected WithEvents bandeau As System.Web.UI.WebControls.Panel
' Datos de la página
Protected erreursHTML As String
Private Sub Page_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
' se comprueba si la aplicación presenta algún error
If CType(Application("erreur"), Boolean) Then
' La aplicación no se ha inicializado correctamente
Dim erreurs As New ArrayList
erreurs.Add("Application momentanément indisponible (" + CType(Application("message"), String) + ")")
afficheErreurs(erreurs, False)
Exit Sub
End If
'Primera consulta
If Not IsPostBack Then
' se inicializan los controles
With DataList1
.DataSource = CType(Application("données"), DataSet)
.DataBind()
End With
With DataList2
.DataSource = CType(Application("données"), DataSet)
.DataBind()
End With
' se muestra el formulario vacío
afficheRésultats(True, False)
End If
End Sub
Private Sub afficheErreurs(ByVal erreurs As ArrayList, ByVal afficheLien As Boolean)
' se muestra la vista de errores
erreursHTML = ""
For i As Integer = 0 To erreurs.Count - 1
erreursHTML += "<li>" + erreurs(i).ToString + "</li>" + ControlChars.CrLf
Next
' se muestra la vista [erreurs]
vueErreurs.Visible = True
vueRésultats1.Visible = False
vueRésultats2.Visible = False
bandeau.Visible = False
End Sub
Private Sub afficheRésultats(ByVal visible1 As Boolean, ByVal visible2 As Boolean)
' se muestra la vista [résultats]
vueRésultats1.Visible = visible1
vueRésultats2.Visible = visible2
vueErreurs.Visible = False
End Sub
Private Sub btnChanger_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnChanger.Click
' se cambia el estilo
afficheRésultats(RadioButton1.Checked, RadioButton2.Checked)
End Sub
End Class
8.8. Componente «Repeater» y enlace de datos
El componente [Repeater] permite repetir el código HTML de cada uno de los elementos de una lista de datos. Supongamos que queremos mostrar una lista de errores con el siguiente formato:

Ya nos hemos encontrado con este problema y lo hemos resuelto incluyendo en el código de presentación una variable con el formato <ul><% =erreursHTML %></ul>, donde el valor de erreursHTML lo calcula el controlador. Este valor contiene el código HTML, correspondiente a una lista. Esto presenta el inconveniente de que, si queremos modificar la presentación de esta lista HTML, nos vemos obligados a acudir a la parte del controlador, lo que va en contra de la separación entre controlador y presentación. El componente [Repeater] nos ofrece una solución. Al igual que con el [DataList], se pueden definir las plantillas <HeaderTemplate> para el encabezado, <ItemTemplate> para el elemento actual de la lista de datos y <FooterTemplate> para el final de los datos. En este caso, podríamos tener la siguiente definición para el componente [Repeater]:
<asp:Repeater id="Repeater1" runat="server" EnableViewState="False">
<HeaderTemplate>
Les erreurs suivantes se sont produites :
<ul>
</HeaderTemplate>
<ItemTemplate>
<li>
<%# Container.DataItem %>
</li>
</ItemTemplate>
<FooterTemplate>
</ul>
</FooterTemplate>
</asp:Repeater>
Cabe recordar que [Container.DataItem] representa una línea de datos si la fuente de datos tiene varias columnas. Representa un dato si la fuente solo tiene una columna. Este será el caso aquí. A modo de ejemplo, creamos la siguiente aplicación:

El código de presentación de la página es el siguiente:
<html>
<head>
</head>
<body>
<form runat="server">
<p>
Liaison de données avec un composant [Repeater]
</p>
<hr />
<p>
<asp:Repeater id="Repeater1" runat="server" EnableViewState="False">
<ItemTemplate>
<li>
<%# Container.DataItem %>
</li>
</ItemTemplate>
<HeaderTemplate>
Les erreurs suivantes se sont produites :
<ul>
</HeaderTemplate>
<FooterTemplate>
</ul>
</FooterTemplate>
</asp:Repeater>
</p>
</form>
</body>
</html>
El código de control es el siguiente:
<%@ Page Language="VB" %>
<script runat="server">
' procedimiento ejecutado al cargar la página
Sub page_Load(sender As Object, e As EventArgs)
if not IsPostBack then
' se crea una fuente de datos
with Repeater1
.DataSource=createDataSource
.DataBind
end with
end if
End Sub
function createDataSource as ArrayList
' se crea una lista de matrices
dim erreurs as new ArrayList
dim i as integer
for i=0 to 5
erreurs.add("erreur-"+i.ToString)
next
return erreurs
end function
</script>
<html>
...
</html>
A la primera solicitud del cliente, asociamos un objeto [ArrayList] al componente [Repeater], que se supone que representa una lista de errores.
8.9. Application
Retomamos aquí una aplicación ya tratada con componentes de servidor. La aplicación permite realizar simulaciones de cálculos de impuestos. Se basa en una clase [impot] que no volveremos a mencionar aquí. Esta clase necesita datos que encuentra en una fuente de datos OLEDB. A modo de ejemplo, utilizaremos una fuente ACCESS. En esta nueva versión introducimos las siguientes novedades:
- el uso de componentes de validación para comprobar la validez de los datos
- el uso de componentes de servidor vinculados a fuentes de datos para mostrar los resultados
8.9.1. La estructura MVC de la aplicación
La estructura MVC de la aplicación es la siguiente:
![]() |
Las tres vistas se incorporarán al código de presentación del controlador [main.aspx] en forma de contenedores. Por lo tanto, esta aplicación tiene una única página: [main.aspx].
8.9.2. Las vistas de la aplicación
La vista [formulaire] es el formulario de introducción de datos que permite calcular el impuesto de un usuario:

El usuario rellena el formulario:

Utiliza el botón [Envoyer] para solicitar el cálculo de su impuesto. Obtiene la siguiente vista [simulations]:

Vuelve al formulario mediante el enlace anterior. Lo encuentra tal y como lo había rellenado. Puede cometer errores al introducir los datos:

Estos se le señalan en la vista [formulaire]:

A continuación, el usuario corrige sus errores. Puede realizar nuevas simulaciones:

A continuación, obtiene la vista [simulations] con una simulación más:

Por último, si la fuente de datos no está disponible, se seña al usuario en la vista [erreurs]:

8.9.2.1. El código de presentación
Recordemos que la página [main.aspx] agrupa todas las vistas. Se trata de un único formulario con tres contenedores:
- [panelform] para la vista [formulaire]
- [panelerreurs] para la vista [erreurs]
- [panelsimulations] para la vista [simulations]
A continuación detallamos los componentes de estos tres contenedores. El contenedor [panelform] tiene la siguiente representación visual:
![]() |
n.º | nombre | tipo | propiedades | función |
Panel | Vista de formulario | |||
RadioButton | GroupName=rdmarie | botones de opción | ||
CustomValidator | ErrorMessage=No ha indicado su estado civil EnableClientScript=false | Comprueba que el programa cliente haya enviado correctamente el estado civil esperado | ||
TextBox | número de hijos | |||
RequiredFieldValidator | ErrorMessage=Indique el número de hijos ControlToValidate=txtEnfants | comprueba que el campo [txtEnfants] no esté vacío | ||
RangeValidator | ErrorMessage=Introduzca un número entre 1 y 30 ControlToValidate=txtEnfants | comprueba que el campo [txtEnfants] esté dentro del intervalo [1,30] | ||
TextBox | EnableViewState=true | salario anual | ||
RequiredFieldValidator | ControlToValidate=txtSalario ErrorMessage=Indica el importe de tu salario | comprueba que el campo [txtSalaire] no esté vacío | ||
RegularExpressionValidator | ControlToValidate=txtSalario ErrorMessage=Salario no válido Expresión regular=\s*\d+\s* | comprueba que el campo [txtSalaire] sea una secuencia de dígitos | ||
Botón | CausesValidation=true | botón [submit] del formulario: inicia el cálculo del impuesto | ||
Botón | CausesValidation=false | Botón [submit] del formulario: borra el contenido del formulario |
Puede sorprender que el control [cvMarié] se encargue de comprobar que el usuario haya marcado correctamente uno de los dos botones de opción. De hecho, no puede ser de otra manera si utiliza correctamente el formulario enviado por el servidor. Como no hay forma de asegurarlo, nos vemos obligados a comprobar todos los parámetros enviados. Cabe destacar también el atributo [CausesValidation=false] del botón [btnEffacer]. De hecho, cuando el usuario utiliza este botón, no hay que comprobar los datos enviados, ya que se van a ignorar.
Todos los componentes del contenedor tienen la propiedad [EnableViewState=false], excepto [panelForm, txtEnfants, txtSalaire, rdOui, rdNon]. El contenedor [panelerreurs] tiene la siguiente representación visual:
![]() |
n.º | nombre | tipo | propiedades | función |
Panel | EnableViewState=false | vista de errores | ||
Repetidor | EnableViewState=false | muestra una lista de errores |
El componente [rptErreurs] se define de la siguiente manera:
<asp:Repeater id="rptErreurs" runat="server" EnableViewState="False">
<ItemTemplate>
<li>
<%# Container.Dataitem%>
</li>
</ItemTemplate>
<HeaderTemplate>
Les erreurs suivantes se sont produites
<ul>
</HeaderTemplate>
<FooterTemplate>
</ul>
</FooterTemplate>
</asp:Repeater>
La lista de datos asociada al componente [rptErreurs] será un objeto [ArrayList] que contiene una lista de mensajes de error. Así, en el ejemplo anterior, <%# Container.Dataitem%> hace referencia al mensaje de error actual. El contenedor [panelsimulations] tiene la siguiente representación visual:
![]() |
n.º | nombre | tipo | propiedades | función |
Panel | EnableViewState=false | Vista de simulaciones | ||
LinkButton | EnableViewState=false | enlace al formulario | ||
DataGrid | EnableViewState=false | encargado de mostrar las simulaciones incluidas en un objeto [DataTable] |
El componente [dgSimulations] está configurado de la siguiente manera en [Webmatrix]. En la ventana de propiedades de [dgSimulations], seleccionamos el enlace [Mise en forme automatique] y seleccionamos el esquema [Couleur 3]:
![]() | ![]() |
A continuación, sin salir de la ventana de propiedades de [dlgSimulations], seleccionamos el enlace [Générateur de propriétés]. Solicitamos que se muestre el encabezado de las columnas:

Seleccionamos la opción [Colonnes] para definir las cuatro columnas de [DataGrid]:

Desmarcamos la frase [Créer des colonnes automatiquement ...]. Vamos a definir nosotros mismos las cuatro columnas del [DataGrid]. Este se asociará a un objeto [DataTable] con cuatro columnas denominadas, respectivamente, «casado», «hijos», «salario» e «impuestos». Para crear una columna en el [DataGrid], seleccionamos la opción [Colonnes connexe] y utilizamos el botón de creación tal y como se ha indicado anteriormente. Se crea una columna y podemos definir sus características. La primera columna de la tabla de simulaciones contendrá la columna «casado» del objeto [DataTable], que se asociará al [DataGrid]. Esta primera columna del [DataGrid] se define de la siguiente manera en el asistente:

título de la columna, en este caso «Casado» | |
nombre de la columna de la fuente de datos que se visualizará en esta columna del [DataGrid]. En este caso, es la columna «casado» del [DataTable]. |
La segunda columna se define de la siguiente manera:
Hijos | |
hijos |
La tercera columna se define de la siguiente manera:
Salario anual | |
Salario | |
{0:C} - formato monetario para mostrar el símbolo del euro |
La cuarta columna se define de la siguiente manera:
Importe del impuesto | |
impuesto | |
{0:C} - formato monetario para mostrar el símbolo del euro |
Colocamos el código de presentación y el código de control en dos archivos separados. El primero estará en [main.aspx] y el segundo en [main.aspx.vb]. El código de [main.aspx] es el siguiente:
<%@ page src="main.aspx.vb" inherits="main" AutoEventWireUp="false" %>
<HTML>
<HEAD>
<title>Calculer votre impôt</title>
</HEAD>
<body>
<P>Calculer votre impôt</P>
<HR width="100%" SIZE="1">
<FORM id="Form1" runat="server">
<asp:panel id="panelform" Runat="server">
<TABLE id="Table1" cellSpacing="1" cellPadding="1" border="0">
<TR>
<TD height="19">Etes-vous marié(e)</TD>
<TD height="19">
<asp:RadioButton id="rdOui" runat="server" GroupName="rdMarie"></asp:RadioButton>Oui
<asp:RadioButton id="rdNon" runat="server" GroupName="rdMarie" Checked="True"></asp:RadioButton>Non
<asp:CustomValidator id="cvMarie" runat="server" ErrorMessage="Vous n'avez pas indiqué votre état marital"
Display="Dynamic" EnableClientScript="False" Visible="False" EnableViewState="False"></asp:CustomValidator></TD>
</TR>
<TR>
<TD>Nombre d'enfants</TD>
<TD>
<asp:TextBox id="txtEnfants" runat="server" Columns="3" MaxLength="3"></asp:TextBox>
<asp:RequiredFieldValidator id="rfvEnfants" runat="server" ErrorMessage="Indiquez le nombre d'enfants" Display="Dynamic"
ControlToValidate="txtEnfants" EnableViewState="False"></asp:RequiredFieldValidator>
<asp:RangeValidator id="rvEnfants" runat="server" ErrorMessage="Tapez un nombre entre 1 et 30" Display="Dynamic"
ControlToValidate="txtEnfants" Type="Integer" MaximumValue="30" MinimumValue="0" EnableViewState="False"></asp:RangeValidator></TD>
</TR>
<TR>
<TD>Salaire annuel (euro)</TD>
<TD>
<asp:TextBox id="txtSalaire" runat="server" Columns="10" MaxLength="10"></asp:TextBox>
<asp:RequiredFieldValidator id="rfvSalaire" runat="server" ErrorMessage="Indiquez le montant de votre salaire"
Display="Dynamic" ControlToValidate="txtSalaire" EnableViewState="False"></asp:RequiredFieldValidator>
<asp:RegularExpressionValidator id="revSalaire" runat="server" ErrorMessage="Salaire invalide" Display="Dynamic"
ControlToValidate="txtSalaire" ValidationExpression="\s*\d+\s*" EnableViewState="False"></asp:RegularExpressionValidator></TD>
</TR>
</TABLE>
<P>
<asp:Button id="btnCalculer" runat="server" Text="Calculer"></asp:Button>
<asp:Button id="btnEffacer" runat="server" Text="Effacer" CausesValidation="False"></asp:Button></P>
</asp:panel>
<asp:panel id="panelerreurs" runat="server" EnableViewState="False">
<asp:Repeater id="rptErreurs" runat="server" EnableViewState="False">
<ItemTemplate>
<li>
<%# Container.Dataitem%>
</li>
</ItemTemplate>
<HeaderTemplate>
Les erreurs suivantes se sont produites
<ul>
</HeaderTemplate>
<FooterTemplate>
</ul>
</FooterTemplate>
</asp:Repeater>
</asp:panel>
<asp:panel id="panelsimulations" runat="server" EnableViewState="False">
<P>
<asp:DataGrid id="dgSimulations" runat="server" BorderColor="#DEBA84" BorderStyle="None" CellSpacing="2"
BorderWidth="1px" BackColor="#DEBA84" CellPadding="3" AutoGenerateColumns="False" EnableViewState="False">
<SelectedItemStyle Font-Bold="True" ForeColor="White" BackColor="#738A9C"></SelectedItemStyle>
<ItemStyle ForeColor="#8C4510" BackColor="#FFF7E7"></ItemStyle>
<HeaderStyle Font-Bold="True" ForeColor="White" BackColor="#A55129"></HeaderStyle>
<FooterStyle ForeColor="#8C4510" BackColor="#F7DFB5"></FooterStyle>
<Columns>
<asp:BoundColumn DataField="marié" HeaderText="Marié"></asp:BoundColumn>
<asp:BoundColumn DataField="enfants" HeaderText="Enfants"></asp:BoundColumn>
<asp:BoundColumn DataField="salaire" HeaderText="Salaire annuel" DataFormatString="{0:C}"></asp:BoundColumn>
<asp:BoundColumn DataField="impôt" HeaderText="Montant de l'impôt" DataFormatString="{0:C}"></asp:BoundColumn>
</Columns>
<PagerStyle HorizontalAlign="Center" ForeColor="#8C4510" Mode="NumericPages"></PagerStyle>
</asp:DataGrid></P>
<P>
<asp:LinkButton id="lnkForm2" runat="server" EnableViewState="False">Retour au formulaire</asp:LinkButton></P>
</asp:panel>
</FORM>
</body>
</HTML>
8.9.3. El código de control de la aplicación
El código de control de la aplicación se encuentra repartido en los archivos [global.asax.vb] y [main.aspx.vb]. El archivo [global.asax] se define de la siguiente manera:
El archivo [global.asax.vb] es el siguiente:
Imports System
Imports System.Web
Imports System.Web.SessionState
Imports st.istia.univangers.fr
Imports System.Configuration
Imports System.Collections
Imports System.Data
Public Class Global
Inherits System.Web.HttpApplication
Sub Application_Start(ByVal sender As Object, ByVal e As EventArgs)
' se crea un objeto «impot»
Dim objImpot As impot
Try
objImpot = New impot(New impotsOLEDB(ConfigurationSettings.AppSettings("chaineConnexion")))
' se añade el objeto a la aplicación
Application("objImpot") = objImpot
' sin errores
Application("erreur") = False
Catch ex As Exception
'se ha producido un error; se anota en la aplicación
Application("erreur") = True
Application("message") = ex.Message
End Try
End Sub
Sub Session_Start(ByVal sender As Object, ByVal e As EventArgs)
' Inicio de sesión: se crea una lista de simulaciones vacía
Dim simulations As New DataTable("simulations")
simulations.Columns.Add("marié", Type.GetType("System.String"))
simulations.Columns.Add("enfants", Type.GetType("System.String"))
simulations.Columns.Add("salaire", Type.GetType("System.Int32"))
simulations.Columns.Add("impôt", Type.GetType("System.Int32"))
Session.Item("simulations") = simulations
End Sub
End Class
Cuando se inicia la aplicación (primera solicitud realizada a la aplicación), se ejecuta el procedimiento [Application_Start]. Este procedimiento intenta crear un objeto de tipo [impot] tomando sus datos de una fuente OLEDB. Se recomienda al lector que consulte el capítulo 5, donde se definió esta clase, en caso de que lo haya olvidado. La creación del objeto [impot] puede fallar si la fuente de datos no está disponible. En ese caso, el error se almacena en la aplicación para que todas las consultas posteriores sepan que este objeto no se ha podido inicializar correctamente. Si la creación se realiza correctamente, el objeto [impot] creado también se almacena en la aplicación. Será utilizado por todas las consultas de cálculo de impuestos. Cuando un cliente realiza su primera consulta, el procedimiento [Application_Start] crea una sesión para él. Esta sesión está destinada a almacenar las diferentes simulaciones de cálculo de impuestos que vaya a realizar. Estas se almacenarán en un objeto [DataTable] asociado a la clave de sesión «simulations». Al iniciarse la sesión, esta clave se asocia a un objeto [DataTable] vacío, pero cuya estructura ya se ha definido. La información necesaria para la aplicación se almacena en su archivo de configuración [wenConfig]:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<appSettings>
<add key="chaineConnexion" value="Provider=Microsoft.Jet.OLEDB.4.0; Ole DB Services=-4; Data Source=D:\data\devel\aspnet\poly\webforms2\vs\impots6\impots.mdb" />
</appSettings>
</configuration>
La clave [chaineConnexion] hace referencia a la cadena de conexión a la fuente OLEDB. La otra parte del código de control se encuentra en [main.aspx.vb]:
Imports System.Collections
Imports Microsoft.VisualBasic
Imports st.istia.univangers.fr
Imports System
Imports System.Web.UI.Control
Imports System.Data
Public Class main
Inherits System.Web.UI.Page
Protected WithEvents rdOui As System.Web.UI.WebControls.RadioButton
Protected WithEvents rdNon As System.Web.UI.WebControls.RadioButton
Protected WithEvents txtEnfants As System.Web.UI.WebControls.TextBox
Protected WithEvents txtSalaire As System.Web.UI.WebControls.TextBox
Protected WithEvents btnCalculer As System.Web.UI.WebControls.Button
Protected WithEvents btnEffacer As System.Web.UI.WebControls.Button
Protected WithEvents panelform As System.Web.UI.WebControls.Panel
Protected WithEvents lnkForm2 As System.Web.UI.WebControls.LinkButton
Protected WithEvents panelerreurs As System.Web.UI.WebControls.Panel
Protected WithEvents rfvEnfants As System.Web.UI.WebControls.RequiredFieldValidator
Protected WithEvents rvEnfants As System.Web.UI.WebControls.RangeValidator
Protected WithEvents rfvSalaire As System.Web.UI.WebControls.RequiredFieldValidator
Protected WithEvents rptErreurs As System.Web.UI.WebControls.Repeater
Protected WithEvents dgSimulations As System.Web.UI.WebControls.DataGrid
Protected WithEvents revSalaire As System.Web.UI.WebControls.RegularExpressionValidator
Protected WithEvents cvMarie As System.Web.UI.WebControls.CustomValidator
Protected WithEvents panelsimulations As System.Web.UI.WebControls.Panel
' variables locales
Private Sub Page_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
...
End Sub
Private Sub afficheErreurs(ByRef erreurs As ArrayList)
...
End Sub
Private Sub afficheFormulaire()
...
End Sub
Private Sub afficheSimulations(ByRef simulations As DataTable, ByRef lien As String)
...
End Sub
Private Sub btnCalculer_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnCalculer.Click
....
End Sub
Private Sub lnkForm1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs)
...
End Sub
Private Sub lnkForm2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles lnkForm2.Click
...
End Sub
Private Sub btnEffacer_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnEffacer.Click
...
End Sub
Private Sub razForm()
...
End Sub
Private Sub cvMarie_ServerValidate(ByVal source As System.Object, ByVal args As System.Web.UI.WebControls.ServerValidateEventArgs)
...
End Sub
End Class
El primer evento procesado por el código es [Page_Load]:
Private Sub Page_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
' En primer lugar, se comprueba el estado de la aplicación
If CType(Application("erreur"), Boolean) Then
' La aplicación no ha podido inicializarse
' se muestra la vista de errores
Dim erreurs As New ArrayList
erreurs.Add("Application momentanément indisponible (" + CType(Application("message"), String) + ")")
afficheErreurs(erreurs)
Exit Sub
End If
' No hay errores: en la primera solicitud, se muestra el formulario
If Not IsPostBack Then afficheFormulaire()
End Sub
Antes de empezar a procesar la solicitud, nos aseguramos de que la aplicación se haya inicializado correctamente. Si no es así, se muestra la vista [erreurs] mediante el procedimiento [afficheErreurs]. Si se trata de la primera solicitud (IsPostBack=false), mostramos la vista [formulaire] mediante [afficheFormulaire].
El procedimiento que muestra la vista [erreurs] es el siguiente:
Private Sub afficheErreurs(ByRef erreurs As ArrayList)
' se genera la lista de errores
With rptErreurs
.DataSource = erreurs
.DataBind()
End With
' Visualización de los contenedores
panelerreurs.Visible = True
panelform.Visible = False
panelsimulations.Visible = False
Exit Sub
End Sub
El procedimiento recibe como parámetro una lista de mensajes de error en [erreurs] del tipo [ArrayList]. Nos limitamos a vincular esta fuente de datos al componente [rptErreurs], encargado de mostrarla.
El procedimiento que muestra la vista [formulaire] es el siguiente:
Private Sub afficheFormulaire()
' muestra el formulario
panelform.Visible = True
' los demás contenedores están ocultos
panelerreurs.Visible = False
panelsimulations.Visible = False
End Sub
Este procedimiento se limita a hacer visible el contenedor [panelform]. Los componentes se muestran con su valor actualizado o anterior (VIEWSTATE).
Cuando el usuario hace clic en el botón [Calculer] de la vista [formulaire], se realiza un POST hacia [main.aspx]. Se ejecuta el procedimiento [Page_Load] y, a continuación, el procedimiento [btnCalculer_Click]:
Private Sub btnCalculer_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnCalculer.Click
' se comprueba la validez de los datos introducidos; si hay errores, se avisa
If Not Page.IsValid Then
afficheFormulaire()
Exit Sub
End If
' sin errores: se calcula el impuesto
Dim impot As Long = CType(Application("objImpot"), impot).calculer( _
rdOui.Checked, CType(txtEnfants.Text, Integer), CType(txtSalaire.Text, Long))
' se añade el resultado a las simulaciones existentes
Dim simulations As DataTable = CType(Session.Item("simulations"), DataTable)
Dim simulation As DataRow = simulations.NewRow
simulation("marié") = CType(IIf(rdOui.Checked, "oui", "non"), String)
simulation("enfants") = txtEnfants.Text.Trim
simulation("salaire") = CType(txtSalaire.Text.Trim, Long)
simulation("impôt") = impot
simulations.Rows.Add(simulation)
' se guardan las simulaciones en la sesión
Session.Item("simulations") = simulations
' se muestra la página de simulaciones
afficheSimulations(simulations, "Retour au formulaire")
End Sub
El procedimiento comienza comprobando la validez de la página. Cabe recordar que, cuando se ejecuta el procedimiento [btnCalculer_Click], los controles de validación ya han realizado su trabajo y se ha activado el atributo [IsValid] de la página. Si la página no es válida, se vuelve a mostrar la vista [formulaire] y el procedimiento finaliza. Los componentes de validación de la vista [formulaire] cuyo atributo [IsValid] sea comprendido entre [false] y [ErrorMessage] mostrarán su atributo [ErrorMessage]. Si la página es válida, el importe del impuesto se calcula mediante el objeto de tipo [impot] que se había almacenado en la aplicación al iniciarla. Esta nueva simulación se añade a la lista de simulaciones ya realizadas y se almacena en la sesión.
Por último, la vista [simulations] se muestra mediante el siguiente procedimiento [afficheSimulations]:
Private Sub afficheSimulations(ByRef simulations As DataTable, ByRef lien As String)
' se vincula la tabla de datos a la fuente de simulaciones
With dgSimulations
.DataSource = simulations
.DataBind()
End With
' vínculo
lnkForm2.Text = lien
' las vistas
panelsimulations.Visible = True
panelerreurs.Visible = False
panelform.Visible = False
End Sub
El procedimiento tiene dos parámetros:
- una lista de simulaciones en [simulations] del tipo [DataTable]
- un texto de enlace en [lien]
La fuente de datos [simulations] está vinculada al componente encargado de mostrarla. El texto del enlace se coloca en la propiedad [Text] del objeto [LinkButton] de la vista.
Cuando el usuario hace clic en el botón [Effacer] de la vista [formulaire], se ejecuta el procedimiento [btnEffacer_click] (siempre después de [Page_Load]):
Private Sub btnEffacer_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnEffacer.Click
' muestra el formulario vacío
razForm()
afficheFormulaire()
End Sub
Private Sub razForm()
' vacía el formulario
rdOui.Checked = False
rdNon.Checked = True
txtEnfants.Text = ""
txtSalaire.Text = ""
End Sub
El código anterior es lo suficientemente sencillo como para no necesitar comentarios. Ahora solo nos queda gestionar el clic en los enlaces de las vistas [erreurs] y [simulations]:
Private Sub lnkForm1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles lnkForm1.Click
' muestra el formulario
afficheFormulaire()
End Sub
Private Sub lnkForm2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles lnkForm2.Click
' muestra el formulario
afficheFormulaire()
End Sub
Ambos procedimientos se limitan a mostrar la vista [formulaire]. Sabemos que los campos de esta vista obtendrán un valor que será, o bien el valor registrado para ellos, o bien su valor anterior. Como en este caso, el POST del cliente no envía ningún valor para los campos del formulario, estos recuperarán su valor anterior. Por lo tanto, el formulario se muestra con los valores introducidos por el usuario.
8.9.4. Pruebas
Todos los archivos necesarios para la aplicación se encuentran en una carpeta <application-path>: ![]() | La carpeta [bin] contiene el archivo DLL con las clases [impot], [impotsData] y [impotsOLEDB] necesarias para la aplicación: |
El lector podrá, si lo desea, volver a consultar el capítulo 5, donde se explica cómo crear el archivo [impot.dll] mencionado anteriormente. Una vez hecho esto, se inicia el servidor Cassini con los parámetros (<application-path>,/impots6). Se solicita la URL [http://impots6/main.aspx] con un navegador:

Si cambiamos el nombre del archivo ACCESS [impots.mdb] por [impots1.mdb], obtendremos la siguiente página:

8.9.5. Conclusión
Tenemos una aplicación MVC que utiliza únicamente componentes de servidor. El uso de estos componentes ha permitido separar por completo la parte de presentación de la aplicación de su parte de control. Esto no había sido posible hasta ahora, ya que el código de presentación contenía variables calculadas por el código de control y cuyo valor incluía código HTML. Ahora ya no es así.






















