Skip to content

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:

Image

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:

Image

ASP.NET ofrece componentes denominados «componentes de validación» que permiten verificar los siguientes casos:

Componente
Función
RequiredFieldValidator
comprueba que un campo no esté vacío
CompareValidator
comprueba si dos valores coinciden
RangeValidator
comprueba que un valor esté comprendido entre dos límites
RegularExpressionValidator
comprueba que un campo cumpla una expresión regular
CustomValidator
permite al desarrollador establecer sus propias reglas de validación; este componente podría sustituir a todos los demás
ValidationSummary
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
txtNom
TextBox
EnableViewState=true
campo de entrada
2
RequiredFieldValidator1
RequiredFieldValidator
EnableViewState=false
EnableClientScript=true
ErrorMessage=El campo [nombre] es obligatorio
componente de validación
3
btnEnvoyer
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:

ControlToValidate
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.
EnableClientScript
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.
ErrorMessage
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]:

Image

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" />
                            &nbsp;
                        </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:

Image

¿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:

Image

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

Image

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:

Image

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:

Image

¿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]:

Image

n.º
nombre
tipo
propiedades
función
1
cmbChoix1
DropDownList
EnableViewState=true
lista desplegable
2
cmbChoix2
DropDownList
EnableViewState=true
lista desplegable
3
CompareValidator1
CompareValidator
EnableViewState=false
EnableClientScript=true
ErrorMessage=Opción 1 no válida
ValorParaComparar=?
Operador=No igual
Tipo=cadena
componente de validación
4
CompareValidator2
CompareValidator
EnableViewState=false
EnableClientScript=true
ErrorMessage=Opción 2 no válida
ControlToCompare=?
Operator=NotEqual
Tipo=cadena
componente de validación
5
btnEnvoyer
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:

ControlToValidate
campo cuyo contenido debe ser comprobado por el componente
EnableClientScript
booleano: si es verdadero, indica que el contenido del campo anterior también debe comprobarse en el lado del cliente
ErrorMessage
el mensaje de error que el componente debe mostrar en caso de detectarse un error
ValeurToCompare
valor con el que se debe comparar el valor del campo [ControlToValidate]
ControlToCompare
componente con cuyo valor debe compararse el valor del campo [ControlToValidate]
Operator
operador de comparación entre ambos valores
Type
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:

Image

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]:

Image

n.º
nombre
tipo
propiedades
función
1
cmbDiplomes
DropDownList
EnableViewState=true
lista desplegable
2
CompareValidator2
CompareValidator
EnableViewState=false
EnableClientScript=true
ErrorMessage=Título no válido
Operator=NotEqual
ValueToCompare=?
Tipo=Cadena
Componente de validación del control [1]
3
txtAutreDiplome
TextBox
EnableViewState=false
campo de entrada
4
CustomValidator1
CustomValidator
EnableViewState=false
EnableClientScript=true
ErrorMessage=Precisión del título no válida
ClientValidationFunction=chkAutreDiplome
Componente de validación del campo [3]
5
txtAnDiplome
TextBox
EnableViewState=false
campo de entrada
6
RangeValidator1
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]
7
RequiredFieldValidator2
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]
8
btnEnvoyer
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:

ControlToValidate
campo cuyo valor debe ser comprobado por el componente
EnableClientScript
booleano: si es verdadero, indica que el contenido del campo anterior también debe validarse en el lado del cliente
ErrorMessage
el mensaje de error que el componente debe mostrar en caso de que se detecte un error
MinValue
valor mínimo del campo que se va a comprobar
MaxValue
valor máximo del campo que se va a comprobar
Type
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:

ControlToValidate
campo cuyo valor debe ser validado por el componente
EnableClientScript
booleano: si es verdadero, indica que el contenido del campo anterior también debe validarse en el lado del cliente
ErrorMessage
el mensaje de error que el componente debe mostrar en caso de que se detecte un error
ClientValidationFunction
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&#238;trise">Ma&#238;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]:

Image

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]:

Image

n.º
nombre
tipo
propiedades
función
1
txtMel
TextBox
EnableViewState=true
campo de entrada
2
RequiredFieldValidator3
RequiredFieldValidator
ControlToValidate=txtMel
Display=Dynamic
Componente de validación del control [1]
3
RegularExpressionValidator1
RegularExpressionValidator
ControlToValidate=txtMel
Display=Dynamic
Componente de validación del control [1]
4
btnEnvoyer
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:

ControlToValidate
campo cuyo valor debe ser comprobado por el componente
EnableClientScript
booleano: si es verdadero, indica que el contenido del campo anterior también debe comprobarse en el lado del cliente
ErrorMessage
el mensaje de error que el componente debe mostrar en caso de que se detecte un error
RegularExpression
la expresión regular con la que se comparará el contenido de [ControlToValidate]
Display
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:

Image

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]:

Image

n.º
nombre
tipo
propiedades
función
1
ValidationSummary1
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
lnkErreursToFormulaire
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]:

Image

1

n.º
nombre
tipo
propiedades
función
1
lblInfo
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:

Image

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

Image

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

Image

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]:

Image

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:

DropDownList, ListBox
<option value="V">T</option>
CheckButtonList
<input type="checkbox" value="V">T
RadioButtonList
<input type="radio" value="V">T

La asociación de un componente [ListControl] a una fuente de datos se realiza mediante las siguientes propiedades:

DataSource
una fuente de datos [Array], [ArrayList], [DataTable], [DataSet], [HashTable], ...
DataMember
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.
DataTextField
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]
DataValueField
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:

Array A
[ListControl].DataSource=A
los campos [Text] y [Value] de los elementos de [ListControl] tendrán como valores los elementos de A
ArrayList AL
[ListControl].DataSource=AL
los campos [Text] y [Value] de los elementos de [ListControl] tendrán como valores los elementos de AL
DataTable DT
[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
DataSet DS
[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»
HashTable HT
[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
1
Array
2
ArrayList
3
DataTable
4
DataSet
5
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:

            .DataSource= myDataTable
            .DataValueField="id"
            .DataTextField="texte"

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:

            .DataSource= myDataSet
            .DataMember="table1"
            .DataValueField="id"
            .DataTextField="texte"

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:

            .DataSource=myHashTable
            .DataValueField="key"
            .DataTextField="value"

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]:

DataSource
una fuente de datos [Array], [ArrayList], [DataTable], [DataSet], ...
DataMember
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:

Image

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]:

Image

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:

Image

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

Image

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á:

Image

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:

          with [DataGrid]
            .DataSource=[source de données]
            .DataBind
          end with

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
DataGrid1
DataGrid
EnableViewState=true
muestra una fuente de datos S
2
DataGrid2
DataGrid
EnableViewState=true
muestra la misma fuente S que [DataGrid1]
3
Button1
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:

Image

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:

Image

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
DataGrid1
DataGrid
EnableViewState=true
muestra una fuente de datos S
2
DataGrid2
DataGrid
EnableViewState=false
muestra la misma fuente S que [DataGrid1]
3
Button1
Botón
EnableViewState=false
botón [submit]
4
lblInfo1
lblInfo2
lblInfo3
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:

Image

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&nbsp;:
                <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:

            Session("source") = S

¿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:

            Session("source") = dtThèmes

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:

        Session("numRequête3") = numRequête3

para guardar el nuevo valor de [numRequête3] en la sesión. En cambio, habría que escribir:

        Session("numRequête1") = numRequête1
        Session("numRequête2") = numRequête2

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:

Image

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
txtSelect
TextBox
EnableViewState=true
campo de introducción de la consulta SELECT
2
RequiredFieldValidator1
RequiredFieldValidator
EnableViewState=false
comprueba si hay un 1
3
txtPages
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
RequiredFieldValidator2
RequiredFieldValidator
EnableViewState=false
comprueba si hay 3
5
RangeValidator1
RangeValidator
EnableViewState=false
comprueba que (3) esté dentro del intervalo [1,30]
6
btnExécuter
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
lblSelect
Etiqueta
EnableViewState=false
campo de información
2
rdCroissant
rdDécroissant
RadioButton
EnableViewState=false
GroupName=rdTri
permite elegir un orden de clasificación
3
DataGrid1
DataGrid
EnableViewState=true
AllowPaging=true
AllowSorting=true
tabla de visualización del resultado de la selección
4
lnkRésultats
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.

Image

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

Image

Se muestra la vista [erreurs]:

n.º
nombre
tipo
propiedades
función
1
erreursHTML
variable
 
código HTML necesario para mostrar los errores
3
lnkForm2
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>&nbsp;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&#233;c&#233;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&#233;c&#233;dent" ...></PagerStyle>
                    </asp:DataGrid></P>
AllowPaging="true"
permite la paginación
PageSize="4"
cuatro líneas de datos por página
NextPageText="Suivant"
Texto del enlace para ir a la página siguiente de la fuente de datos
PrevPageText="Précédent"
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]:

Image

Aparecerá el siguiente asistente:

Image

Seleccionamos la opción [Pagination]:

Image

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]

<%@ Application src="global.asax.vb" inherits="Global" %>

[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:

IsValid
se establece en «verdadero» si los datos comprobados son válidos, y en «falso» en caso contrario
ErrorMessage
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
source
el objeto origen del evento —en este caso, uno de los enlaces [Suivant] o [Précédent]
e
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
source
el objeto origen del evento —en este caso, uno de los enlaces [Suivant] o [Précédent]
e
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
RadioButton1
RadioButton2
RadioButton
EnableViewState=false
permite elegir uno de los dos estilos de [DataList]
2
btnChanger
Botón
EnableViewState=false
botón [submit]
3
DataList1
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
DataList2
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
erreursHTML
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]:

HeaderTemplate
Plantilla del encabezado de [DataList]
ItemTemplate
Plantilla de las líneas que muestran los elementos de la lista de datos asociada. Solo esta plantilla es obligatoria.
AlternatingItemTemplate
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
SelectedItemTemplate
Plantilla del elemento seleccionado en [DataList]
SeparatorTemplate
plantilla del separador entre dos elementos del [DataList]
EditItemTemplate
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»
FooterTemplate
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:

HeaderTemplate

                        <HeaderTemplate>
                            Contenu de la table [liste] de la base [produits]
                        </HeaderTemplate>
ItemTemplate

                        <ItemTemplate>
                            nom :
                            <%# Container.DataItem("nombre") %>
                            <br>
                            prix :
                            <%# DataBinder.Eval(Container.DataItem,"precio","{0:C}") %>
                            <br>
                        </ItemTemplate>

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:

Image

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]:

RepeatDirection
Horizontal
RepeatColumns
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]

<%@ Application src="global.asax.vb" inherits="Global" %>

[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:

Image

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:

Image

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:

Image

El usuario rellena el formulario:

Image

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

Image

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

Image

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

Image

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

Image

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

Image

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

Image

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
0
panelform
Panel
 
Vista de formulario
1
rdOui
rdNon
RadioButton
GroupName=rdmarie
botones de opción
2
cvMarie
CustomValidator
ErrorMessage=No ha indicado su estado civil
EnableClientScript=false
Comprueba que el programa cliente haya enviado correctamente el estado civil esperado
3
txtEnfants
TextBox
 
número de hijos
4
rfvEnfants
RequiredFieldValidator
ErrorMessage=Indique el número de hijos
ControlToValidate=txtEnfants
comprueba que el campo [txtEnfants] no esté vacío
5
rvEnfants
RangeValidator
ErrorMessage=Introduzca un número entre 1 y 30
ControlToValidate=txtEnfants
comprueba que el campo [txtEnfants] esté dentro del intervalo [1,30]
6
txtSalaire
TextBox
EnableViewState=true
salario anual
7
rfvSalaire
RequiredFieldValidator
ControlToValidate=txtSalario
ErrorMessage=Indica el importe de tu salario
comprueba que el campo [txtSalaire] no esté vacío
8
revSalaire
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
9
btnCalculer
Botón
CausesValidation=true
botón [submit] del formulario: inicia el cálculo del impuesto
10
btnEffacer
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
0
panelerreurs
Panel
EnableViewState=false
vista de errores
1
rptErreurs
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
0
panelsimulations
Panel
EnableViewState=false
Vista de simulaciones
2
lnkForm2
LinkButton
EnableViewState=false
enlace al formulario
1
dgSimulations
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:

Image

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

Image

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:

Image

Texte de l'en-tête
título de la columna, en este caso «Casado»
Champ de données
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:

Texte de l'en-tête
Hijos
Champ de données
hijos

La tercera columna se define de la siguiente manera:

Texte de l'en-tête
Salario anual
Champ de données
Salario
Expression de mise en forme
{0:C} - formato monetario para mostrar el símbolo del euro

La cuarta columna se define de la siguiente manera:

Texte de l'en-tête
Importe del impuesto
Champ de données
impuesto
Expression de mise en forme
{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&#233;" HeaderText="Mari&#233;"></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&#244;t" HeaderText="Montant de l'imp&#244;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:

<%@ Application src="Global.asax.vb" Inherits="Global" %>

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:

Image

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

Image

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í.