Skip to content

8. Componentes do Servidor ASP - 2

8.1. Introdução

Continuamos o nosso trabalho na interface do utilizador explorando:

  • componentes de validação de dados
  • como ligar dados a componentes do servidor
  • componentes de servidor HTML

8.2. componentes de validação de dados

8.2.1. Introdução

Em aplicações baseadas em formulários, é essencial verificar a validade dos dados introduzidos nos mesmos. No exemplo de cálculo de impostos, tínhamos o seguinte formulário:

Image

Ao receber os valores deste formulário, devemos verificar a validade dos dados introduzidos: o número de filhos e o salário devem ser números inteiros positivos ou zero. Se não for esse o caso, o formulário é devolvido ao cliente tal como foi introduzido, juntamente com mensagens de erro. Tratámos este cenário utilizando duas vistas, uma para o formulário acima e outra para os erros:

Image

O ASP.NET oferece componentes chamados componentes de validação que permitem verificar o seguinte:

Componente
Função
RequiredFieldValidator
verifica se um campo não está vazio
CompareValidator
compara dois valores entre si
RangeValidator
verifica se um valor se encontra entre dois limites
RegularExpressionValidator
verifica se um campo corresponde a uma expressão regular
CustomValidator
permite ao programador definir as suas próprias regras de validação — este componente poderia substituir todos os outros
ValidationSummary
permite reunir as mensagens de erro geradas pelos controlos anteriores num único local da página

Vamos agora apresentar cada um destes componentes.

8.2.2. RequiredFieldValidator

Estamos a criar a seguinte página [requiredfieldvalidator1.aspx]:

N.º
nome
tipo
propriedades
função
1
txtName
Caixa de Texto
EnableViewState=true
campo de entrada
2
Validador de campo obrigatório 1
Validador de campo obrigatório
EnableViewState=false
AtivarScriptCliente=true
ErrorMessage=O campo [nome] é obrigatório
componente de validação
3
btnSubmit
Botão
EnableViewState=false
Motivos de validação=true
Botão [submit]

O campo [RequiredFieldValidator1] é utilizado para apresentar uma mensagem de erro se o campo [txtName] estiver vazio ou contiver uma sequência de espaços. As suas propriedades são as seguintes:

ControlToValidate
O campo cujo valor deve ser validado pelo componente. O que significa o valor de um componente? É o valor do atributo [value] da tag HTML correspondente. Para um [TextBox], este é o conteúdo do campo de entrada; para um [DropDownList], é o valor do item selecionado.
EnableClientScript
Booleano - quando verdadeiro, indica que o conteúdo do campo anterior também deve ser validado no lado do cliente. Neste caso, o formulário só será enviado pelo navegador se não contiver erros. No entanto, as verificações de validação também são realizadas no servidor, caso o cliente não seja um navegador, por exemplo.
ErrorMessage
A mensagem de erro que o componente deve exibir se for detetado um erro

A validação de dados na página é realizada apenas se o botão ou link que acionou o [POST] da página tiver a propriedade [CausesValidation=true]. [true] é o valor padrão para esta propriedade.

Vamos executar esta aplicação. Primeiro, vamos usar um navegador [Mozilla]:

Image

O código HTML recebido pelo [Mozilla] é o seguinte:

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

Podemos ver que o botão [btnEnvoyer] está ligado a uma função JavaScript através do seu atributo [onclick]. Também podemos ver que a página não contém código [JavaScript]. O teste [typeof(Page_ClientValidate) == 'function'] irá falhar, e a função JavaScript não será chamada. O formulário será então enviado para o servidor, que realizará as verificações de validação. Vamos utilizar o botão [Submit] sem introduzir um nome. A resposta do servidor é a seguinte:

Image

O que aconteceu? O formulário foi enviado para o servidor. O servidor executou o código de todos os componentes de validação na página. Aqui, existe apenas um. Se pelo menos um componente de validação detetar um erro, o servidor devolve o formulário tal como foi preenchido (na verdade, para componentes com [EnableViewState=true], inclui também uma mensagem de erro para cada componente de validação que detetou um erro). Esta mensagem é o atributo [ErrorMessage] do componente de validação. Este é o mecanismo que vemos em ação acima. Se introduzirmos algo no campo [name], a mensagem de erro deixa de aparecer quando o formulário é enviado:

Image

Agora, vamos usar o Internet Explorer e solicitar a URL [http://localhost/requiredfieldvalidator1.aspx]. Obtemos a seguinte página:

Image

O código HTML recebido pelo IE é o seguinte:

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

Podemos ver que este código é muito mais longo do que o recebido pelo [Mozilla]. Não entraremos em detalhes. A diferença decorre do facto de o servidor ter incluído funções JavaScript no documento HTML enviado. Por que razão existem dois códigos HTML diferentes quando o URL solicitado por ambos os navegadores é o mesmo? Já referimos anteriormente que a tecnologia ASP.NET faz com que o servidor adapte o documento HTML enviado ao cliente ao ambiente específico desse cliente. Existem vários navegadores no mercado e nem todos têm as mesmas capacidades. Os navegadores da Microsoft e da Netscape, por exemplo, não utilizam o mesmo modelo de objeto para o documento que recebem. Consequentemente, o código JavaScript utilizado para manipular este documento no lado do cliente difere entre os dois navegadores. Da mesma forma, os fornecedores de navegadores têm lançado versões sucessivas dos seus navegadores, melhorando continuamente as suas capacidades. Assim, um documento HTML que o IE5 consegue analisar pode não ser compreendido pelo IE3. Acima está um exemplo desta adaptação do lado do servidor para o cliente. Por uma razão que não é explorada em profundidade aqui, o servidor web determinou que o cliente [Mozilla] não tinha a capacidade de lidar com o código JavaScript de validação. Portanto, este código não foi incluído no documento HTML que lhe foi enviado, e a validação foi realizada no lado do servidor. Para o [IE6], este código JavaScript foi incluído no documento HTML enviado, como podemos ver. Para ver isto em ação, vamos tentar a seguinte experiência:

  • parar o servidor web
  • clique no botão [Submit] sem preencher o campo [Name]

Recebemos a seguinte nova página:

Image

É, de facto, o navegador que exibiu a mensagem de erro. Isto acontece porque o servidor está parado. Podemos verificar isto introduzindo algo no campo [nome] e enviando. Desta vez, a resposta é a seguinte:

Image

O que aconteceu? O navegador executou o código de validação JavaScript. Este código determinou que a página era válida. O navegador enviou então o formulário para o servidor web, que estava parado. O navegador percebeu isso e exibiu a página acima. Se reiniciarmos o servidor, tudo volta ao normal.

O que podemos aprender com este exemplo?

  • o valor do conceito de componente de validação, que permite que qualquer formulário incorreto seja devolvido ao cliente
  • o valor do [VIEWSTATE], que permite que o formulário seja devolvido exatamente como foi preenchido
  • a capacidade do servidor de se adaptar ao seu cliente. O cliente é identificado pelo cabeçalho HTTP [User-Agent:] que envia ao servidor. Portanto, não é o servidor que «adivinha» com quem está a lidar. Esta adaptabilidade é de grande interesse para o programador, que não precisa de se preocupar com o tipo de cliente para a sua aplicação.

8.2.3. CompareValidator

Estamos a construir a seguinte página [comparevalidator1.aspx]:

Image

N.º
nome
tipo
propriedades
função
1
cmbChoix1
Lista suspensa
EnableViewState=true
lista suspensa
2
cmbChoix2
Lista suspensa
EnableViewState=true
lista suspensa
3
CompareValidator1
CompareValidator
EnableViewState=false
AtivarScriptCliente=true
Mensagem de erro=Escolha inválida 1
Valor a comparar=?
Operador=Não igual
Tipo=string
componente de validação
4
CompareValidator2
CompareValidator
AtivarViewState=false
AtivarScriptCliente=true
Mensagem de erro=Escolha inválida 2
Controlo a comparar=?
Operador=Não igual
Tipo=string
componente de validação
5
btnSend
Botão
EnableViewState=false
Motivos de validação=true
Botão [submit]

O componente [CompareValidator] é utilizado para comparar dois valores. Os operadores disponíveis são [Equal, NotEqual, LessThan, LessThanEqual, GreaterThan, GreaterThanEqual]. O primeiro valor é definido pela propriedade [ControlToValidate], o segundo por [ValueToCompare] se o primeiro valor for comparado a uma constante, ou por [ControlToCompare] se for comparado ao valor de outro componente. As propriedades importantes do componente [CompareValidator] são as seguintes:

ControlToValidate
campo cujo conteúdo deve ser validado pelo componente
EnableClientScript
Booleano - quando verdadeiro, indica que o conteúdo do campo anterior também deve ser validado no lado do cliente
Mensagem de erro
a mensagem de erro que o componente deve exibir se for detetado um erro
ValueToCompare
o valor com o qual o valor do campo [ControlToValidate] deve ser comparado
ControlToCompare
o componente cujo valor deve ser comparado com o valor do campo [ControlToValidate]
Operador
operador de comparação entre os dois valores
Tipo
Tipo dos valores a comparar

O código HTML desta página é o seguinte:

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

As restrições de validação para a página são as seguintes:

  • O valor selecionado em [cmbChoix1] deve ser diferente da cadeia de caracteres "?". Isto implica as seguintes propriedades para o componente [CompareValidator1]: Operator=NotEqual, ValueToCompare=?, Type=string
  • O valor selecionado em [cmbChoix2] deve ser diferente do valor selecionado em [cmbChoix1]. Isto implica as seguintes propriedades para o componente [CompareValidator2]: Operador=NãoIgual, ControloParaComparar=cmbChoix1, Tipo=string

Executamos esta aplicação. Aqui está um exemplo de uma página recebida durante as trocas cliente-servidor:

Image

Se a mesma página for solicitada pelo Internet Explorer 6, é incluído nela código JavaScript que resulta num comportamento ligeiramente diferente. Quaisquer erros são reportados assim que o utilizador altera um valor numa das listas suspensas. Isto melhora a experiência do utilizador.

8.2.4. CustomValidator , RangeValidator

Estamos a criar a seguinte página [customvalidator1.aspx]:

Image

N.º
nome
tipo
propriedades
função
1
cmbDiplomas
Lista suspensa
EnableViewState=true
lista suspensa
2
CompareValidator2
CompareValidator
EnableViewState=false
AtivarScriptCliente=true
Mensagem de erro=Diploma inválido
Operador=Não igual
Valor a comparar=?
Tipo=String
componente de validação do controlo [1]
3
txtOutroGrau
Caixa de Texto
EnableViewState=false
campo de entrada
4
ValidadorPersonalizado1
Validador personalizado
EnableViewState=false
AtivarScriptCliente=true
ErrorMessage=Especificação de grau inválida
Função de validação do cliente=chkOtherDegree
componente de validação para o campo [3]
5
txtAnDiploma
Caixa de Texto
EnableViewState=false
campo de entrada
6
Validador de intervalo 1
Validador de intervalo
EnableViewState=false
EnableClientScript=true
ErrorMessage=O ano de conclusão do curso deve estar no intervalo [1990,2004]
ValorMínimo=1990
MaxValue=2004
Tipo=Inteiro
ControlToValidate=txtAnDiplome
componente de validação de campo [5]
7
Validador de campo obrigatório 2
Validador de campo obrigatório
EnableViewState=false
EnableClientScript=true
Mensagem de erro=É necessário indicar o ano de conclusão
ControlToValidate=txtAnDiplome
componente de validação de campo [5]
8
btnSubmit
Botão
EnableViewState=false
Motivos de validação=true
Botão [submit]

O campo [RangeValidator] é utilizado para verificar se o valor de um controlo se situa entre dois limites [MinValue] e [MaxValue]. As suas propriedades são as seguintes:

ControlToValidate
campo cujo valor deve ser validado pelo componente
EnableClientScript
Booleano - quando verdadeiro, indica que o conteúdo do campo anterior também deve ser validado no lado do cliente
ErrorMessage
a mensagem de erro que o componente deve exibir se for detetado um erro
MinValue
valor mínimo do campo a ser validado
MaxValue
valor máximo do campo a ser verificado
Tipo
Tipo do valor do campo a ser validado

O campo [CustomValidator] permite realizar validações que não podem ser realizadas pelos componentes de validação fornecidos pelo ASP.NET. Esta validação é realizada por uma função escrita pelo programador. Esta função é executada no lado do servidor. Uma vez que a validação também pode ser realizada no lado do cliente, o programador poderá ter de desenvolver uma função JavaScript para incluir no documento HTML. As suas propriedades são as seguintes:

ControlToValidate
O campo cujo valor deve ser validado pelo componente
EnableClientScript
Booleano - quando verdadeiro, indica que o conteúdo do campo anterior também deve ser validado no lado do cliente
ErrorMessage
A mensagem de erro que o componente deve exibir se for detetado um erro
ClientValidationFunction
A função a ser executada no lado do cliente

O código do modelo de página é o seguinte:

<%@ 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>

O atributo [OnServerValidate] do componente [CustomValidator] permite especificar a função responsável pela validação do lado do servidor. No exemplo acima, o componente [CustomValidator1] chama o seguinte procedimento [CustomValidator1_ServerValidate_1]:

<%@ Page Language="VB" %>
<script runat="server">

    Sub CustomValidator1_ServerValidate_1(sender As Object, e As ServerValidateEventArgs)
        ' field [txtAutreDiplome] must be non-empty if [cmbDiplomes]=[other]
        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>
....

Uma função de validação associada a um componente [CustomValidator] recebe dois parâmetros:

  • sender: o objeto que desencadeou o evento
  • e: o evento. O procedimento deve definir a propriedade [e.IsValid] como true se os dados validados estiverem corretos e como false caso contrário.

Aqui, são realizadas as seguintes verificações:

  • a lista suspensa [cmbDiplomes] não pode ter [?] como valor. Isto é verificado pelo componente [CompareValidator2]
  • o utilizador seleciona um grau na lista suspensa [cmbDiplomes]. Se o seu grau não existir na lista, pode selecionar a opção [Outro] na lista. Deve então introduzir o seu grau no campo de entrada [txtAutreDiplome]. Se [Outro] for selecionado em [cmbDiplomes], o campo [txtAutreDiplome] não pode estar vazio. Se nem [Outro] nem [?] forem selecionados em [cmbDiplomes], o campo [txtAutreDiplome] deve estar vazio. Isto é verificado pela função associada ao componente [CustomValidator1].
  • O ano em que o diploma foi obtido deve estar dentro do intervalo [1900-2004]. Isto é verificado pelo [RangeValidator1]. No entanto, se o utilizador deixar o campo em branco, a função de validação do [RangeValidator1] não é utilizada. Por isso, adicionamos o componente [RequiredFieldValidator2] ao [ ] para verificar a presença de conteúdo. Esta é uma regra geral. O conteúdo de um campo não é verificado se estiver vazio. O único caso em que é verificado é quando está associado a um componente [RequiredFieldValidator].

Aqui está um exemplo de execução no navegador [Mozilla]:

Image

Se utilizarmos o navegador [IE6], podemos adicionar uma função de validação do lado do cliente para o componente [CustomValidator1]. Para tal, este componente possui as seguintes propriedades:

  • EnableClientScript=true
  • ClientValidationFunction=chkAutreDiplome

A função [chkAutreDiplome] é a seguinte:

<head>
    <meta http-equiv="pragma" content="no-cache" />
    <script language="javascript">
        function chkAutreDiplome(source,args){
            // vérifie la validité du champ txtAutreDiplome
            with(document.frmCandidature){
                diplome=cmbDiplomes.options[cmbDiplomes.selectedIndex].text;
                    args.IsValid= !(diplome=="[Autre]" && txtAutreDiplome.value=="")
                        && ! (diplome!="[Autre]" && diplome!="?" && txtAutreDiplome.value!="");
            }
        }
        </script>
</head>

A função [chkAutreDiplome] recebe os mesmos dois parâmetros que a função do servidor [CustomValidator1_ServerValidate_1]:

  • source: o objeto que desencadeou o evento
  • args: o evento. Este possui um atributo [IsValid] que deve ser definido como true pela função se os dados validados estiverem corretos. Um valor false exibirá a mensagem de erro associada no local do componente de validação, e o formulário não será enviado para o servidor.

8.2.5. RegularExpressionValidator

Estamos a criar a seguinte página [regularexpressionvalidator1.aspx]:

Image

N.º
nome
tipo
propriedades
função
1
txtMel
Caixa de Texto
EnableViewState=true
campo de entrada
2
Validador de campo obrigatório 3
Validador de campo obrigatório
ControlToValidate=txtMel
Exibir=Dinâmico
componente de validação de controlo [1]
3
Validador de Expressão Regular 1
Validador de Expressão Regular
ControlToValidate=txtMel
Exibição=Dinâmica
componente de validação de controlo [1]
4
btnSend
Botão
EnableViewState=false
CausesValidation=true
Botão [submit]

Aqui, queremos validar o formato de um endereço de e-mail. Fazemos isso utilizando um componente [RegularExpressionValidator], que nos permite validar um campo utilizando uma expressão regular. Lembre-se de que uma expressão regular é um padrão de caracteres. Por isso, verificamos o conteúdo de um campo em relação a um padrão. Uma vez que o conteúdo de um campo não é verificado se estiver vazio, também precisamos de um componente [RequiredFieldValidator] para verificar se o endereço de e-mail não está vazio. A classe [RegularExpressionValidator] tem propriedades semelhantes às das classes de componentes já abordadas:

ControlToValidate
campo cujo valor deve ser validado pelo componente
EnableClientScript
booleano - quando verdadeiro, indica que o conteúdo do campo anterior também deve ser validado no lado do cliente
ErrorMessage
a mensagem de erro que o componente deve exibir se for detetado um erro
RegularExpression
a expressão regular com a qual o conteúdo de [ControlToValidate] será comparado
Exibição
modo de exibição: Estático: o campo está sempre presente, mesmo que não exiba uma mensagem de erro; Dinâmico: o campo está presente apenas se houver uma mensagem de erro; Nenhum: a mensagem de erro não é exibida. Este campo também existe para outros componentes, mas não tinha sido utilizado até agora.

O padrão de expressão regular para um endereço de e-mail poderia ser o seguinte: \s*[\w-]+(\.[\w-]+)*@[\w-]+(\.[\w-]+)+\s*

Um endereço de e-mail tem o formato [champ1.champ2....@champA.champB...]. Deve haver pelo menos um campo antes do símbolo @ e pelo menos dois campos depois dele. Estes campos consistem em caracteres alfanuméricos e no símbolo -. Um caractere alfanumérico é representado pelo símbolo \w. A sequência [\w-] significa o caractere \w ou o caractere -. O sinal + após uma sequência S significa que esta pode ser repetida 1 ou mais vezes; o sinal * significa que pode ser repetida 0 ou mais vezes; o sinal ? significa que pode ser repetida 0 ou 1 vez.

Um campo no endereço de e-mail corresponde ao padrão [\w-]+. O facto de ter de haver pelo menos um campo antes do sinal @ e pelo menos dois depois dele, separados pelo sinal ., corresponde ao padrão: [\w-]+(\.[\w-]+)*@[\w-]+(\.[\w-]+)+. Podemos permitir que o utilizador inclua espaços antes e depois do endereço. Estes serão removidos durante o processamento. Uma sequência de espaços, que pode estar vazia, é representada pelo padrão \s*. Daí a expressão regular para o endereço de e-mail: \s*[\w-]+(\.[\w-]+)*@[\w-]+(\.[\w-]+)+\s*.

O código de layout da página é, então, o seguinte:

<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. Resumo da validação

Por razões estéticas, poderá querer agrupar as mensagens de erro num único local, como no exemplo seguinte [summaryvalidator1.aspx], onde combinámos todos os exemplos anteriores numa única página:

Image

Todos os controlos de validação têm as seguintes propriedades:

  • [Display=Dynamic], que garante que os controlos não ocupem espaço na página se a sua mensagem de erro estiver vazia
  • [EnableClientScript=false] para desativar toda a validação do lado do cliente
  • [Text=*]. Esta será a mensagem exibida em caso de erro, enquanto o conteúdo do atributo [ErrorMessage] é exibido pelo controlo [ValidationSummary] apresentado abaixo.

Este conjunto de controlos é colocado num componente [Panel] chamado [vueFormulaire]. Num outro componente [Panel] chamado [vueErreurs], colocamos um controlo [ValidationSummary]:

Image

N.º
nome
tipo
propriedades
função
1
Resumo da validação 1
Resumo da validação
Texto do cabeçalho=Ocorreram os seguintes erros, AtivarScriptCliente=false, MostrarResumo=true
exibe os erros de todos os controlos de validação na página
2
lnkErrorsToForm
Botão de ligação
ValidationCauses=false
ligação de volta ao formulário

Para o link [lnkErrorsToForm], não é necessário ativar a validação, uma vez que este link não envia quaisquer valores para verificação.

Quando todos os dados são válidos, a seguinte vista [info] é apresentada ao utilizador:

Image

1

N.º
Nome
tipo
propriedades
função
1
lblInfo
Rótulo
 
Mensagem informativa para o utilizador

O código HTML desta página é o seguinte:

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

Aqui estão alguns exemplos dos resultados obtidos. Enviamos a vista [form] sem introduzir quaisquer valores:

Image

O botão [Submit] dá-nos a seguinte resposta:

Image

A vista [errors] foi apresentada. Se utilizarmos o link para regressar ao formulário, encontramo-lo no seguinte estado:

Image

Esta é a vista [form]. Repare no caractere [*] ao lado dos dados incorretos. Este é o campo [Text] dos controlos de validação que foi apresentado. Se preenchermos os campos corretamente, obteremos a vista [info]:

Image

O código de controlo da página é o seguinte:

<%@ Page Language="VB" %>
<script runat="server">

    ' procedure executed when the page is loaded
    Sub page_Load(sender As Object, e As EventArgs)
        ' on the 1st request, we present the [form] view
        if not ispostback then
            afficheVues(true,false,false)
        end if
    end sub

    sub afficheVues(byval formulaireVisible as boolean, _
        erreursVisible as boolean, infosVisible as boolean)
        ' set of views
        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)
        ' displays the form view
        afficheVues(true,false,false)
        ' redo validity checks
        Page.validate
    End Sub

    Sub btnEnvoyer_Click(sender As Object, e As EventArgs)
        ' is the page valid?
        if not Page.IsValid then
            ' the [errors] view is displayed
            afficheVues(false,true,false)
        else
            ' otherwise the view [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>
  • No procedimento [Page_Load], que é executado em cada pedido do cliente, apresentamos a vista [form], enquanto as outras ficam ocultas. Isto é feito apenas no primeiro pedido. O procedimento chama um procedimento utilitário [displayViews], ao qual passamos três valores booleanos para determinar se as três vistas devem ou não ser apresentadas.
  • Quando o procedimento [btnEnvoyer_Click] é chamado, a validação de dados já foi realizada. O botão [btnEnvoyer] tem a propriedade [CausesValidation=true], que aciona esta validação de dados. Todos os controlos de validação foram executados e a sua propriedade [IsValid] foi definida. Esta propriedade indica se os dados validados pelo controlo eram válidos ou não. Além disso, a propriedade [IsValid] da própria página também foi definida. Ela é [true] apenas se a propriedade [IsValid] de todos os controlos de validação na página tiver o valor [true]. Assim, o procedimento [btnEnvoyer_Click] começa por verificar se a página é válida ou não. Se for inválida, é apresentada a vista [errors]. Esta vista contém o controlo [ValidationSummary], que lista os atributos [ErrorMessage] de todos os controlos (ver captura de ecrã acima). Se a página for válida, é apresentada a vista [info] juntamente com uma mensagem informativa.
  • O procedimento [lnkErrorsToForm_Click] é responsável por exibir a vista [form] no estado em que foi validada. Uma vez que todos os campos de entrada na vista [form] têm a propriedade [EnableViewState=true], o seu estado é automaticamente regenerado. Curiosamente, o estado dos componentes de validação não é restaurado. Seria de esperar que, ao regressar à vista [errors], os controlos inválidos exibissem o seu campo [Text]. Não é esse o caso. Por isso, forçámos a validação de dados utilizando o método [Page.Validate] da página. Isto deve ser feito assim que o painel [formView] for tornado visível. Existem, assim, um total de duas validações. Na prática, isto deve ser evitado. Aqui, o exemplo permitiu-nos introduzir novos conceitos relativos à validação de páginas.

8.3. Componentes ListControl e ligação de dados

Vários dos componentes de servidor abordados permitem-lhe apresentar uma lista de valores (DropDownList, ListBox). Outros que ainda não abordámos permitem-lhe apresentar várias listas de valores em tabelas HTML. Para todos eles, é possível associar programaticamente os valores nas listas ao componente correspondente, um por um. Também é possível associar objetos mais complexos a estes componentes, tais como objetos do tipo [Array], [ArrayList], [DataSet], [HashTable], etc., o que simplifica o código que associa os dados ao componente. Esta associação é chamada de ligação de dados.

Todos os componentes derivados da classe [ListControl] podem ser associados a uma lista de dados. Estes incluem os componentes [DropDownList], [ListBox], [CheckButtonList] e [RadioButtonList]. Cada um destes componentes pode ser vinculado a uma fonte de dados. Isto pode assumir várias formas: [Array], [ArrayList], [DataTable], [DataSet], [HashTable], ... geralmente um objeto que implementa uma das interfaces IEnumerable, ICollection ou IListSource. Apresentaremos aqui apenas alguns deles. Um objeto [DataSet] é uma representação de uma base de dados relacional. É, portanto, um conjunto de tabelas ligadas por relações. O objeto [DataTable] representa uma tabela desse tipo. A fonte de dados define as propriedades [Text] e [Value] de cada [Item] no objeto [ListControl]. Se T for o valor de [Text] e V for o valor de [Value], a tag HTML gerada para cada elemento de [ListControl] é a seguinte:

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

Um componente [ListControl] é associado a uma fonte de dados utilizando as seguintes propriedades:

DataSource
uma fonte de dados [Array], [ArrayList], [DataTable], [DataSet], [HashTable], ...
DataMember
se a fonte de dados for um [DataSet], representa o nome da tabela a ser utilizada como fonte de dados. A fonte de dados real é, então, uma tabela.
DataTextField
se a fonte de dados for uma tabela ([DataTable], [DataSet]), representa o nome da coluna da tabela que fornecerá valores ao campo [Text] dos itens do [ListControl]
DataValueField
se a fonte de dados for uma tabela ([DataTable], [DataSet]), representa o nome da coluna da tabela que fornecerá valores ao campo [Value] dos elementos [ListControl]

A ligação de um componente [ListControl] a uma fonte de dados não inicializa o componente com os valores da fonte de dados. A operação [ListControl].DataBind é que o faz.

Dependendo da natureza da fonte de dados, a ligação a um componente [ListControl] será feita de forma diferente:

Matriz A
[ListControl].DataSource=A
Os campos [Text] e [Value] dos elementos [ListControl] terão os valores dos elementos em A
ArrayList AL
[ListControl].DataSource=AL
Os campos [Text] e [Value] dos elementos [ListControl] terão os valores dos elementos em AL
DataTable DT
[ListControl].DataSource = DT, [ListControl].DataTextField = "col1", [ListControl].DataValueField = "col2"
onde col1 e col2 são duas colunas da tabela DT. Os campos [Text] e [Value] dos itens [ListControl] terão os valores das colunas col1 e col2 da tabela DT
DataSet DS
[ListControl].DataSource=DS, [ListControl].DataSource="table", em que "table" é o nome de uma das tabelas em DS.
[ListControl].DataTextField="col1", [ListControl].DataValueField ="col2" onde col1 e col2 são duas colunas da tabela "table". Os campos [Text] e [Value] dos elementos [ListControl] terão os valores das colunas col1 e col2 da tabela "table"
HashTable HT
[ListControl].DataSource = HT, [ListControl].DataTextField = "key", [ListControl].DataValueField = "value", onde [key] e [value] são as chaves e os valores de HT, respetivamente.

Aplicamos esta informação ao seguinte exemplo [databind1.aspx]:

Aqui temos cinco ligações de dados, cada uma com quatro controlos dos tipos [DropDownList], [ListBox], [CheckBoxList] e [RadioButtonList]. As cinco ligações analisadas diferem nas suas fontes de dados:

ligação
fonte de dados
1
Matriz
2
Lista de matrizes
3
DataTable
4
Conjunto de dados
5
Tabela de hash

8.3.1. Código de apresentação do componente

O código de apresentação para os controlos na ligação 1 é o seguinte:

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

A ligação para as ligações 2 a 5 é idêntica, exceto pelo número da ligação.

8.3.2. Ligação a uma fonte de dados de matriz

A ligação dos quatro controlos [ListBox] acima é feita da seguinte forma no procedimento [Page_Load] do código do controlo:

' global data
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

' procedure executed when the page is loaded
Sub page_Load(sender As Object, e As EventArgs)
    if not IsPostBack then
      ' create the data sources to be linked to the components
      createDataSources
      ' link to an array [Array]
      bindToArray
      ' link to a list [ArrayList]
      bindToArrayList
      ' link to a table [DataTable]
      bindToDataTable
      ' link to a data group [DataSet]
      bindToDataSet
      ' link to a dictionary [HashTable]
      bindToHashTable
    end if
End Sub

sub createDataSources
  ' creates data sources to be linked to components
  ' arraylist
  dim i as integer
  for i=0 to textes.length-1
    myDataListe.add(textes(i))
  next
  ' datatable
  ' we define its two columns
  myDataTable.Columns.Add("id",Type.GetType("System.Int32"))
  myDataTable.Columns.Add("texte",Type.GetType("System.String"))
  ' fill the table
  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
  ' dataset - a single table
  myDataSet.Tables.Add(myDataTable)
  ' hashtable
  for i=0 to textes.length-1
    myHashTable.add(valeurs(i),textes(i))
  next
end sub

' panel connection
sub bindToArray
        ' association with components
        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
        ' item selection
        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

Os controlos são ligados aos dados aqui apenas na primeira solicitação. Depois disso, os controlos manterão os seus elementos através do mecanismo [VIEWSTATE]. A ligação é realizada no procedimento [bindToArray]. Uma vez que a fonte de dados é do tipo [Array], apenas o campo [DataSource] dos componentes [ListControl] é inicializado. O controlo é preenchido com valores da fonte de dados associada utilizando o método [ListControl].DataBind. Só então é que os objetos [ListControl] têm elementos. Pode então selecionar alguns deles.

8.3.3. Ligação a uma fonte de dados ArrayList

A fonte de dados [myDataList] é inicializada no procedimento [createDataSources]:

' global data
dim textes() as string={"un","deux","trois","quatre"}
dim myDataListe as new ArrayList
..

' procedure executed when the page is loaded
Sub page_Load(sender As Object, e As EventArgs)
    if not IsPostBack then
      ' create the data sources to be linked to the components
      createDataSources
...
    end if
End Sub

sub createDataSources
  ' creates data sources to be linked to components
  ' arraylist
  dim i as integer
  for i=0 to textes.length-1
    myDataListe.add(textes(i))
  next
...
end sub

A ligação dos quatro controlos [ListBox] na ligação 2 é feita da seguinte forma no procedimento [bindToArrayList] do código do controlo:

' liaison arraylist
sub bindToArrayList
        ' l'association aux composants
        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 sélection des éléments
        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

Uma vez que a fonte de dados é do tipo [ArrayList], apenas o campo [DataSource] dos componentes [ListControl] é inicializado.

8.3.4. Fonte de dados do tipo DataTable

A fonte de dados [myDataTable] é inicializada no procedimento [createDataSources]:

' global data
dim textes() as string={"un","deux","trois","quatre"}
dim myDataTable as new DataTable("table1")
...

' procedure executed when the page is loaded
Sub page_Load(sender As Object, e As EventArgs)
    if not IsPostBack then
      ' create the data sources to be linked to the components
      createDataSources
...
    end if
End Sub

sub createDataSources
  ' creates data sources to be linked to components
...
  ' datatable
  ' we define its two columns
  myDataTable.Columns.Add("id",Type.GetType("System.Int32"))
  myDataTable.Columns.Add("texte",Type.GetType("System.String"))
  ' fill the table
  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

Começamos por criar um objeto [DataTable] com duas colunas: [id] e [text]. A coluna [id] irá preencher o campo [Value] dos itens [ListControl], e a coluna [text] irá preencher os seus campos [Text]. O [DataTable] com duas colunas é criado da seguinte forma:

  myDataTable.Columns.Add("id",Type.GetType("System.Int32"))
  myDataTable.Columns.Add("texte",Type.GetType("System.String"))

Assim, criamos uma tabela com duas colunas:

  • a primeira, chamada "id", é do tipo inteiro
  • a segunda, chamada "text", é do tipo string

Agora que a estrutura da tabela foi criada, podemos preenchê-la com o seguinte 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

A coluna [id] conterá números inteiros [0,1,..,n], enquanto a coluna [text] conterá os valores da matriz [data]. Uma vez feito isto, a tabela [dataList] é preenchida. A ligação dos quatro controlos [ListBox] na ligação 3 é realizada da seguinte forma no procedimento [bindToDataTable] do código de controlo:

sub bindToDataTable
        ' l'association aux composants
        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
        ' la sélection des éléments
        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] é vinculado à fonte de dados [myDataTable] definindo o seguinte para cada um:

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

A tabela [myDataTable] é a fonte de dados. A coluna [id] desta tabela irá preencher os campos [Value] dos elementos do componente, enquanto a coluna [text] irá preencher os seus campos [Text].

8.3.5. Fonte de dados do tipo DataSet

A fonte de dados [myDataSet] é inicializada no procedimento [createDataSources]:

' global data
dim myDataTable as new DataTable("table1")
dim myDataSet as new DataSet
...


' procedure executed when the page is loaded
Sub page_Load(sender As Object, e As EventArgs)
    if not IsPostBack then
      ' create the data sources to be linked to the components
      createDataSources
...
    end if
End Sub

sub createDataSources
  ' creates data sources to be linked to components
  ' dataset - a single table
  myDataSet.Tables.Add(myDataTable)
...
end sub

Um objeto [DataSet] representa uma coleção de tabelas [DataTable]. Adicionamos a [myDataTable] criada anteriormente ao [DataSet]. A ligação dos quatro controlos [ListBox] na ligação 4 é feita da seguinte forma no procedimento [bindToDataSet] do código do controlo:

sub bindToDataSet
        ' l'association aux composants
        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
        ' la sélection des éléments
        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á ligado à fonte de dados da seguinte forma:

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

O conjunto de dados [myDataSet] é a fonte de dados. Uma vez que pode conter várias tabelas, especificamos o nome da tabela a utilizar em [DataMember]. A coluna [id] desta tabela irá preencher os campos [Value] dos elementos do componente, enquanto a coluna [text] irá preencher os seus campos [Text].

8.3.6. Fonte de dados HashTable

A fonte de dados [myHashTable] é inicializada no procedimento [createDataSources]:

' global data
dim textes() as string={"un","deux","trois","quatre"}
dim valeurs() as string={"1","2","3","4"}
dim myHashTable as new HashTable
...

' procedure executed when the page is loaded
Sub page_Load(sender As Object, e As EventArgs)
    if not IsPostBack then
      ' create the data sources to be linked to the components
      createDataSources
...
    end if
End Sub

sub createDataSources
  ' creates data sources to be linked to components
...
  ' hashtable
  for i=0 to textes.length-1
    myHashTable.add(valeurs(i),textes(i))
  next
end sub

O dicionário [myHashTable] pode ser visto como uma tabela com duas colunas chamadas «key» e «value». A coluna [key] representa as chaves do dicionário e a coluna [value] representa os valores associados a elas. Aqui, a coluna [key] consiste no conteúdo da matriz [values] e a coluna [value] consiste no conteúdo da matriz [texts]. A ligação desta fonte aos controlos é realizada no procedimento [bindToHashTable]:

sub bindToHashTable
        ' l'association aux composants
        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 sélection des éléments
        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 componente, a ligação é estabelecida utilizando as seguintes instruções:

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

A fonte de dados é o dicionário [myHashTable]. Os valores dos controlos são fornecidos pela coluna [key] do dicionário e o texto pela coluna [value]. Os elementos do dicionário são inseridos nos controlos pela ordem das chaves, que inicialmente é aleatória.

8.3.7. Diretivas de importação de namespaces

Vários namespaces são importados automaticamente para uma página ASP.NET. Este não é o caso de "System.Data", onde se encontram as classes [DataTable] e [DataSet]. Por conseguinte, esta classe deve ser importada. Isto é feito da seguinte forma:

<%@ Page Language="VB" %>
<%@ import Namespace="System.Data" %>
<script runat="server">
...
</script>
<html>
...
</html>

8.4. Componente DataGrid e ligação de dados

O componente [DataGrid] permite-lhe apresentar dados em formato de tabela, mas vai muito além da simples apresentação:

  • oferece a capacidade de configurar com precisão a «apresentação visual» da tabela
  • permite-lhe atualizar a fonte de dados

O componente [DataGrid] é simultaneamente poderoso e complexo. Iremos apresentá-lo passo a passo.

8.4.1. Exibição de uma fonte de dados do tipo Array, ArrayList, DataTable ou DataSet

O componente [DataGrid] permite-lhe apresentar fontes de dados do tipo [Array], [ArrayList], [DataTable] e [DataSet] numa tabela HTML. Para estes quatro tipos de dados, basta associar a fonte à propriedade [DataSource] do componente [DataGrid]:

DataSource
uma fonte de dados [Array], [ArrayList], [DataTable], [DataSet], ...
DataMember
se a fonte de dados for um [DataSet], representa o nome da tabela a ser utilizada como fonte de dados. A fonte de dados real é, então, uma tabela. Se este campo for deixado em branco, todas as tabelas no [DataSet] são apresentadas.

Apresentamos agora a página [datagrid1.aspx], que mostra um [DataGrid] associado a quatro fontes de dados diferentes:

Image

A página contém quatro componentes [DataGrid] criados com o [WebMatrix] da seguinte forma. Colocamos o componente na sua localização no separador [Design]:

Image

É então desenhada uma tabela HTML genérica. As propriedades de um [DataGrid] podem ser definidas em tempo de design. É isso que estamos a fazer aqui para as suas propriedades de formatação. Para tal, selecionamos o [DataGrid] a configurar. As suas propriedades aparecem numa janela no canto inferior direito:

Image

Vamos utilizar os dois links acima. O link [Property Generator] dá acesso às principais propriedades do [DataGrid]:

Image

Desmarcamos a opção [Show Header] para os quatro componentes [DataGrid] e guardamos a página. O outro link, [Auto Format], permite escolher entre vários estilos para a tabela HTML que será apresentada:

Image

Selecionamos [color i] para o [DataGrid] #i. Estas escolhas de design refletem-se no código de apresentação da 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>

No modo de design, definimos apenas propriedades de formatação. É no código de controlo que associamos dados aos quatro componentes:

<%@ Page Language="VB" %>
<%@ import Namespace="system.data" %>
<script runat="server">

    ' global data
        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

        ' procedure executed when the page is loaded
        Sub page_Load(sender As Object, e As EventArgs)
            if not IsPostBack then
              ' create the data sources to be linked to the components
              createDataSources
              ' link to an array [Array]
              bindToArray
              ' link to a list [ArrayList]
              bindToArrayList
              ' link to a table [DataTable]
              bindToDataTable
              ' link to a data group [DataSet]
              bindToDataSet
            end if
        End Sub

        sub createDataSources
          ' creates data sources to be linked to components
          ' arraylist
          dim i as integer
          for i=0 to textes1.length-1
            myDataListe.add(textes1(i))
          next
          ' datatable
          ' we define its two columns
          myDataTable.Columns.Add("id",Type.GetType("System.Int32"))
          myDataTable.Columns.Add("texte1",Type.GetType("System.String"))
          myDataTable.Columns.Add("texte2",Type.GetType("System.String"))
          ' fill the table
          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
          ' dataset - a single table
          myDataSet.Tables.Add(myDataTable)
        end sub

        ' panel connection
        sub bindToArray
          with DataGrid1
            .DataSource=textes1
            .DataBind
          end with
        end sub

        ' arraylist link
        sub bindToArrayList
          with DataGrid2
            .DataSource=myDataListe
            .DataBind
          end with
        end sub

        ' datatable link
        sub bindToDataTable
          with DataGrid3
            .DataSource=myDataTable
            .DataBind
          end with
        end sub

        ' dataset link
        sub bindToDataSet
          with DataGrid4
            .DataSource=myDataSet
            .DataBind
          end with
        end sub

</script>
<html>
...
</html>

O código é bastante semelhante ao do exemplo anterior, pelo que não o comentaremos especificamente. Note, no entanto, como é feita a ligação de dados. Para cada um dos quatro controlos, a seguinte sequência é suficiente:

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

onde [data source] é do tipo [Array], [ArrayList], [DataTable] ou [DataSet]. Podemos ver, portanto, que esta é uma ferramenta poderosa para apresentar dados em tabelas:

  • O layout é criado utilizando o [WebMatrix] ou qualquer outro IDE durante a fase de design
  • A ligação de dados é feita no código. Com o [WebMatrix], pode ser feita em tempo de design se a fonte de dados for uma base de dados SQL Server ou uma base de dados Access. Neste caso, um componente [SqlDataSource] ou [AccessDataSource] é colocado no formulário. Este componente pode ser ligado em tempo de design à fonte de dados física, seja uma base de dados SQL Server ou uma base de dados Access, conforme apropriado. Se atribuir um objeto [SqlDataSource] ou [AccessDataSource] ligado a uma fonte física à propriedade [DataSource] de um componente [DataGrid], os dados reais aparecerão no componente [DataGrid] no modo de design.

8.5. ViewState dos componentes de lista de dados

Aqui, pretendemos destacar o mecanismo [VIEWSTATE] para componentes de lista de dados. Pode hesitar entre dois métodos para manter o estado de um componente de lista de dados entre duas solicitações do cliente:

  • definir o seu atributo [VIEWSTATE] como true
  • definir o seu atributo [VIEWSTATE] como false e armazenar a sua fonte de dados na sessão, para que o componente possa ser ligado a essa fonte durante a próxima solicitação.

O exemplo seguinte ilustra certos aspetos do mecanismo [VIEWSTATE] para contentores de dados. Na primeira solicitação do cliente, a aplicação apresenta a seguinte vista:

N.º
nome
tipo
propriedades
função
1
DataGrid1
DataGrid
EnableViewState=true
exibe uma fonte de dados S
2
DataGrid2
DataGrid
EnableViewState=true
exibe a mesma fonte de dados S que [DataGrid1]
3
Botão1
Botão
EnableViewState=false
Botão [submit]

O componente [DataGrid1] é mantido pelo mecanismo [VIEWSTATE]. Queremos determinar se este mecanismo, que regenera a apresentação do [DataGrid1] a cada pedido, também regenera a sua fonte de dados. Para tal, a fonte de dados é ligada ao componente [DataGrid2]. A sua geração a cada pedido é realizada através de uma ligação explícita à fonte de dados do [DataGrid1]. O seu atributo [EnableViewState] também é definido como [true].

O código de apresentação [main.aspx] da aplicação é o seguinte:


<%@ 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>

O controlador [main.aspx.vb] é o seguinte:


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
        ' on the 1st query, the data source is defined and linked to the 1st datagrid
        If Not IsPostBack Then
            'define data source
            With DataGrid1
                .DataSource = createDataSource()
                .DataBind()
            End With
        End If
        ' for each query, datagrid2 is linked to the source of the 1st datagrid
        With Datagrid2
            .DataSource = DataGrid1.DataSource
            .DataBind()
        End With
    End Sub
 
    Private Function createDataSource() As DataTable
        ' initialize the data source
        Dim thèmes As New DataTable
        ' columns
        With thèmes.Columns
            .Add("id", GetType(System.Int32))
            .Add("thème", GetType(System.String))
            .Add("description", GetType(System.String))
        End With
        ' column id will be primary key
        thèmes.Constraints.Add("cléprimaire", thèmes.Columns("id"), True)
        ' lines
        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

O método [createDataSource] cria uma fonte de dados S do tipo [DataTable]. Não nos deteremos no seu código, uma vez que não é o foco deste exemplo. Este é o método utilizado para construir os dois componentes [DataGrid] que nos interessam:

  • o componente [DataGrid1] é vinculado à tabela S uma vez, durante a primeira consulta. A partir daí, deixa de estar vinculado.
  • O componente [DataGrid2] é vinculado à fonte [DataGrid1.DataSource] a cada nova consulta.

Durante a primeira consulta, obtemos a seguinte visualização:

Image

Logicamente, ambos os componentes exibem a fonte de dados à qual estavam ligados. Utilizamos o botão [Submit] para acionar um [PostBack] para o servidor. A visualização resultante é então a seguinte:

Image

Observamos que o componente [DataGrid1] manteve o seu valor, mas o componente [DataGrid2] não. Explicação:

  • Mesmo antes do procedimento [Page_Load] iniciar, os objetos [DataGrid1] e [DataGrid2] já recuperaram os valores que tinham durante o pedido anterior, devido ao mecanismo [viewstate]. Na verdade, ambos têm a sua propriedade [EnableViewState] definida como [true].
  • O procedimento [Page_Load] é executado. Uma vez que se trata de uma operação [PostBack], o componente [DataGrid1] não é modificado pelo [Page_Load] (ver código). Por conseguinte, mantém o valor recuperado através do [viewstate]. É isto que o ecrã acima mostra.
  • O componente [DataGrid2], por outro lado, está vinculado via [DataBind] à fonte de dados [DataGrid1.DataSource]. Por isso, é reconstruído, e o valor que acabara de recuperar através do [viewstate] é perdido. Teria sido, portanto, vantajoso definir a sua propriedade [EnableViewState] como [false] para evitar uma gestão desnecessária do estado. O ecrã acima mostra que o [DataGrid2] foi vinculado a uma fonte vazia. Uma vez que esta fonte é [DataGrid1.DataSource], podemos concluir que, embora o mecanismo [viewstate] restaure com sucesso a exibição do componente [DataGrid1], não restaura as suas propriedades, tais como [DataSource].

O que podemos concluir deste exemplo? Deve evitar definir a propriedade [EnableViewState] de um contentor de dados como [true] se este precisar de ser vinculado (DataBind) a uma fonte de dados em cada pedido. No entanto, existem certos casos em que, mesmo neste cenário, a propriedade [EnableViewState] do contentor deve permanecer definida como [true]; caso contrário, os eventos que pretende tratar não serão acionados. Encontraremos um exemplo disso mais adiante.

Frequentemente, a fonte de dados de um contentor de dados muda ao longo das solicitações. Portanto, o contentor deve ser vinculado à fonte de dados em cada solicitação. É comum que a fonte de dados tenha o escopo da sessão para que as solicitações possam aceder a ela. O nosso segundo exemplo demonstra este mecanismo. A aplicação fornece apenas uma única vista:

N.º
nome
tipo
propriedades
função
1
DataGrid1
DataGrid
EnableViewState=true
exibe uma fonte de dados S
2
DataGrid2
DataGrid
EnableViewState=false
exibe a mesma fonte de dados S que [DataGrid1]
3
Botão1
Botão
EnableViewState=false
Botão [submit]
4
lblInfo1
lblInfo2
lblInfo3
Rótulo
EnableViewState=false
texto informativo

O componente [DataGrid1] é vinculado aos dados apenas durante o primeiro pedido. Ele manterá o seu valor entre pedidos graças ao mecanismo [viewstate]. O componente [DataGrid2] é vinculado a uma fonte de dados a cada pedido, à qual é adicionado um item a cada novo pedido. Portanto, o componente [DataGrid2] deve ser vinculado (DataBind) a cada pedido. Por isso, definimos o seu atributo [EnableViewState] como [false], tal como recomendado anteriormente. Assim, durante a segunda solicitação (usando o botão [Submit]), obtemos a seguinte resposta:

Image

O componente [DataGrid1] manteve o seu valor inicial. O componente [DataGrid2] tem mais um item. Os três valores [1,2,2] representam o número da consulta. Podemos ver que um dos valores está incorreto. Vamos tentar perceber porquê.

O código de apresentação da aplicação [main.aspx] é o seguinte:


<%@ 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>

O código do controlador [main.aspx.vb] é o seguinte:


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
        ' on the 1st query, the data source is defined and linked to the 1st datagrid
        If Not IsPostBack Then
            'define data source
            dtThèmes = createDataSource()
            With DataGrid1
                .DataSource = dtThèmes
                .DataBind()
            End With
            ' store information in the session
            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
        ' a new theme is added to each query
        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)
        'links datagrid2 with the data source
        With Datagrid2
            .DataSource = dtThèmes
            .DataBind()
        End With
        ' info no. of requests
        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
        ' store some information in the session
        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

Não reproduzimos o código do método [createDataSource]. É o mesmo da aplicação anterior, exceto que apenas três linhas estão incluídas no código-fonte. Vejamos primeiro como a fonte de dados e os dois contentores são geridos:


    Private Sub Page_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
        ' on the 1st query, the data source is defined and linked to the 1st datagrid
        If Not IsPostBack Then
            'define data source
            dtThèmes = createDataSource()
            With DataGrid1
                .DataSource = dtThèmes
                .DataBind()
            End With
            ' store information in the session
            Session("source") = dtThèmes
...
        End If
        ' a new theme is added to each query
        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)
        'links datagrid2 with the data source
        With Datagrid2
            .DataSource = dtThèmes
            .DataBind()
        End With
...
    End Sub

O componente [DataGrid1] é vinculado à fonte de dados S apenas durante a primeira consulta (não IsPostBack). Em seguida, é colocado na sessão. Não será colocado lá novamente a partir daí. A fonte de dados S colocada na sessão é recuperada a cada solicitação, e uma nova linha é adicionada a ela. O componente [DataGrid2] é explicitamente vinculado à fonte S a cada solicitação. É por isso que o seu conteúdo aumenta em uma linha a cada solicitação. Observe que, após modificar o conteúdo da fonte S, ela não é explicitamente colocada de volta na sessão por meio de uma operação:

            Session("source") = S

Porquê? Quando uma consulta é iniciada, a sessão contém um objeto Session("source") do tipo [DataTable], que é a fonte de dados tal como estava durante a última consulta. Vamos chamar ao objeto Session("source") de S. Quando escrevemos:


        dtThèmes = CType(Session("source"), DataTable)

[dtThèmes] e [S] são duas referências ao mesmo objeto [DataTable]. Assim, quando o código em [Page_Load] adiciona um elemento à tabela referenciada por [dtThèmes], adiciona-o simultaneamente à tabela referenciada por [S]. No final da execução da página, todos os objetos da sessão serão guardados, incluindo o objeto Session("source"), ou seja, S, ou seja, [dtThemes]. Assim, é de facto o novo conteúdo da fonte de dados que é guardado. Não havia necessidade de escrever:

            Session("source") = dtThèmes

para realizar este salvamento, porque Session("source") já é igual a [dtThèmes]. Isto deixa de ser verdade quando os dados colocados na sessão não são objetos, tais como as estruturas [Integer, Float, ...]. Isto é demonstrado pela gestão do contador de consultas:


    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
        ' on the 1st query, the data source is defined and linked to the 1st datagrid
....
            ' store information in the session
            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
        ' a new theme is added to each query
....
        ' info no. of requests
        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
        ' store some information in the session
        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

Armazenamos os contadores de pedidos em três elementos:

  • numRequest1 e numRequest2 do tipo [Integer] — [Integer] não é uma classe, mas uma estrutura
  • numRequest3 do tipo [integer] - [integer] é uma classe definida para este fim

Quando escrevemos:


        numRequête1 = CType(Session("numRequête1"), Integer)
..
        numRequête2 = CType(Session("numRequête2"), Integer)
..
        numRequête3 = CType(Session("numRequête3"), entier)
..
  • A estrutura [Session("numRequest1")] é copiada para [numRequest1]. Assim, quando o elemento [numRequest1] é modificado, o próprio elemento [Session("numRequest1")] não é
  • O mesmo se aplica a [Session("numRequest2")] e [numRequest2]
  • Os elementos [Session("numRequête3")] e [numRequête3] são ambos referências ao mesmo objeto do tipo [integer]. O objeto referenciado pode ser modificado por qualquer uma das referências.

A partir disto, podemos concluir que é desnecessário escrever:

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

para armazenar o novo valor de [numRequest3] na sessão. Em vez disso, deve escrever:

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

para armazenar os novos valores das estruturas [numRequête1] e [numRequête2]. Fazemos isto apenas para [numRequête2], o que explica por que razão, na captura de ecrã obtida após a segunda consulta, o contador [numRequête1] está incorreto.

Devemos, portanto, notar que, uma vez que os dados tenham sido adicionados a uma sessão, não precisam de ser adicionados repetidamente se forem representados por um objeto. Nos outros casos, devem ser adicionados se tiverem sido modificados.

8.6. Exibir uma lista de dados utilizando um DataGrid paginado e ordenado

O componente [DataGrid] permite-lhe apresentar o conteúdo de um [DataSet]. Até agora, sempre construímos os nossos [DataSets] «manualmente». Desta vez, estamos a utilizar um [DataSet] proveniente de uma base de dados. Estamos a construir a seguinte aplicação MVC:

As três vistas serão incorporadas no código de apresentação do controlador [main.aspx] como contentores. Por conseguinte, esta aplicação tem uma única página, [main.aspx].

8.6.1. As classes de negócio

A classe [products] fornece acesso à seguinte base de dados ACCESS:

Image

O código para a classe [products] é o seguinte:


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)
            ' save the connection string
            Me.chaineConnexionOLEDB = chaineConnexionOLEDB
        End Sub
 
        Public Function getDataSet(ByVal commande As String) As DataSet
            ' create a DataAdapter object to read data from source OLEDB
            Dim adaptateur As New OleDbDataAdapter(commande, chaineConnexionOLEDB)
            ' create a memory image of the select result
            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
            ' we return the result
            Return contenu
        End Function
    End Class
End Namespace

A base de dados ACCESS será gerida através de um controlador OLEDB. Fornecemos ao construtor da classe [products] a cadeia de ligação, o controlador OLEDB e a base de dados a ser gerida. A classe [products] possui um método [getDataSet] que retorna um [DataSet] obtido através da execução de uma consulta SQL [select], cujo texto é passado como parâmetro. Durante o método, ocorrem várias operações: estabelecer a ligação à base de dados, executar a consulta [select] e encerrar a ligação. Tudo isto pode gerar uma exceção, que é tratada aqui. Um programa de teste poderia ter o seguinte aspeto:

Option Explicit On 
Option Strict On

' namespaces
Imports System
Imports System.Data
Imports Microsoft.VisualBasic

Namespace st.istia.univangers.fr

    ' test pg
    Module testproduits
        Sub Main(ByVal arguments() As String)
            ' displays the contents of a product table
            ' the table is in a ACCESS database whose pg receives the file name
            Const syntaxe1 As String = "pg bdACCESS"

            ' checking program parameters
            If arguments.Length <> 1 Then
                ' error msg
                Console.Error.WriteLine(syntaxe1)
                ' end
                Environment.Exit(1)
            End If

            ' prepare the connection chain
            Dim chaineConnexion As String = "Provider=Microsoft.Jet.OLEDB.4.0; Ole DB Services=-4; Data Source=" + arguments(0)

            ' creation of a product object
            Dim objProduits As produits = New produits(chaineConnexion)

            ' retrieve the product table from a dataset
            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

            ' display its contents
            Dim lignes As DataRowCollection = contenu.Tables(0).Rows
            For i As Integer = 0 To lignes.Count - 1
                ' table line i
                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

Os vários componentes desta aplicação são compilados da seguinte forma:

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. As Visualizações

Agora que temos a classe de acesso aos dados, vamos escrever os controladores e as vistas para esta aplicação web. Vamos primeiro ver como funciona. A primeira vista é a seguinte:

N.º
nome
tipo
propriedades
função
1
txtSelect
Caixa de Texto
EnableViewState=true
campo de entrada de consulta de seleção
2
Validador de campo obrigatório 1
Validador de campo obrigatório
EnableViewState=false
verifica a presença de 1
3
txtPages
TextBox
EnableViewState=true
campo de entrada - especifica o número de linhas de dados a apresentar por página de resultados
4
Validador de campo obrigatório 2
Validador de campo obrigatório
EnableViewState=false
verifica a presença de 3
5
RangeValidator1
RangeValidator
EnableViewState=false
verifica se (3) está no intervalo [1,30]
6
btnExecute
Botão
EnableViewState=false
Botão [submit]

Chamaremos a esta vista a vista [form]. Ela permite ao utilizador executar uma consulta SQL SELECT na base de dados [products.mdb]. A execução da consulta cria uma nova vista:

N.º
nome
tipo
propriedades
função
1
lblSelect
Rótulo
EnableViewState=false
campo de informação
2
rdAscending
rdDescending
Botão de opção
EnableViewState=false
GroupName=rdSort
permite-lhe escolher uma ordem de classificação
3
DataGrid1
DataGrid
EnableViewState=true
Permitir paginação=true
PermitirOrdenação=true
tabela que apresenta os resultados da seleção
4
lnkResults
LinkButton
EnableViewState=false
[submit]

Chamaremos a esta vista a vista [results]. Ela contém o [DataGrid] que exibirá os resultados da instrução SQL SELECT. O utilizador pode cometer um erro na sua consulta. Alguns erros serão-lhe comunicados na vista [errors] graças aos controlos de validação.

Image

Outros erros serão comunicados através da vista [errors]:

Image

A vista [errors] é apresentada:

N.º
nome
tipo
propriedades
função
1
HTMLErrors
variável
 
Código HTML necessário para exibir erros
3
lnkForm2
LinkButton
EnableViewState=false
Botão [submit]

As três vistas da aplicação são três contentores (painéis) diferentes dentro da mesma página [main.aspx]. O seu código de apresentação é o seguinte:


<%@ 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. Configurar o DataGrid

Vamos analisar mais detalhadamente a paginação do componente [DataGrid], uma funcionalidade com a qual nos deparamos pela primeira vez. No código acima, esta paginação é controlada pelos seguintes 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"
ativa a paginação
PageSize="4"
quatro linhas de dados por página
NextPageText="Seguinte"
O texto do link para ir para a página seguinte da fonte de dados
PrevPageText="Anterior"
O texto do link para ir para a página anterior da fonte de dados

Esta informação pode ser introduzida diretamente nos atributos da tag <asp:datagrid>. Também pode utilizar o [WebMatrix]. Na janela de propriedades [DataGrid], clique na ligação [Gerador de Propriedades]:

Image

Aparece o seguinte assistente:

Image

Selecione a opção [Pagination]:

Image

Acima, vemos os valores dos atributos de paginação para o [DataGrid] no código de apresentação.

Além disso, ativamos a ordenação dos dados numa das colunas do [DataGrid]. Existem várias formas de o fazer. Uma delas consiste em definir a propriedade [AllowSorting=true] na janela de propriedades do [DataGrid]. Também é possível utilizar o gerador de propriedades. Independentemente do método utilizado, isto resulta na presença do atributo [AllowSorting=true] na tag <asp:DataGrid> do componente:


                    <asp:DataGrid id="DataGrid1" runat="server" ... AllowPaging="True" PageSize="4" AllowSorting="True">

8.6.4. Os controladores

O controlador [global.asax, global.asax.vb] é o seguinte:

[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)
        ' create a product object
        Dim objProduits As produits
        Try
            objProduits = New produits(ConfigurationSettings.AppSettings("OLEDBStringConnection"))
            ' put the object in the application
            Application("objProduits") = objProduits
            ' no error
            Application("erreur") = False
        Catch ex As Exception
            'there has been an error, we note it in the application
            Application("erreur") = True
            Application("message") = ex.Message
        End Try
    End Sub
End Class

Quando a aplicação é iniciada (Application_Start), criamos um objeto [products] e armazenamo-lo na aplicação para que esteja disponível para todos os pedidos de todos os clientes. Se ocorrer uma exceção durante esta criação, ela é registada na aplicação. O procedimento [Application_Start] será executado apenas uma vez. Depois disso, o controlador [global.asax] deixará de estar envolvido. O controlador [main.aspx.vb] tratará então do resto:


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
 
    ' components page
    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
    ' data page
    Protected erreursHTML As String
 
    Private Sub Page_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
        ' check for application errors
        If CType(Application("erreur"), Boolean) Then
            ' the application has not initialized correctly
            Dim erreurs As New ArrayList
            erreurs.Add("Application momentanément indisponible (" + CType(Application("message"), String) + ")")
            afficheErreurs(erreurs, False)
            Exit Sub
        End If
        '1st request
        If Not IsPostBack Then
            ' the empty form is displayed
            afficheFormulaire()
        End If
    End Sub
 
    Private Sub afficheErreurs(ByVal erreurs As ArrayList, ByVal afficheLien As Boolean)
        ' displays the error view
        erreursHTML = ""
        For i As Integer = 0 To erreurs.Count - 1
            erreursHTML += "<li>" + erreurs(i).ToString + "</li>" + ControlChars.CrLf
        Next
        lnkErreurs.Visible = afficheLien
        ' the [errors] view is displayed
        vueErreurs.Visible = True
        vueFormulaire.Visible = False
        vueRésultats.Visible = False
    End Sub
 
    Private Sub afficheFormulaire()
        ' the [form] view is displayed
        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)
        ' initialize controls
        lblSelect.Text = sqlTexte
        With DataGrid1
            .DataSource = données
            .PageSize = CType(txtPages.Text, Integer)
            .CurrentPageIndex = 0
            .DataBind()
        End With
        ' the [results] view is displayed
        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
        ' valid page?
        If Not Page.IsValid Then
            afficheFormulaire()
            Exit Sub
        End If
 
        ' execute query SELECT customer
        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
        ' all's well - the results are in
        afficheRésultats(txtSelect.Text.Trim, données)
        ' put the data in the session
        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
        ' set of views
        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
        ' change page
        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
        ' sort the dataview
        Dim données As DataView = CType(Session("données"), DataView)
        données.Sort = e.SortExpression + " " + CType(IIf(rdCroissant.Checked, "asc", "desc"), String)
        ' we display it
        With DataGrid1
            .CurrentPageIndex = 0
            .DataSource = données
            .DataBind()
        End With
    End Sub
End Class

Quando a página carrega [Page_Load], verificamos primeiro se a aplicação conseguiu inicializar corretamente. Caso contrário, exibimos a vista [errors] sem um link de retorno para o formulário, uma vez que esse link se torna desnecessário. Na verdade, apenas a vista [errors] pode ser exibida se a aplicação não conseguiu inicializar corretamente. Caso contrário, exibimos a vista [form] se esta for a primeira solicitação do cliente. Quanto ao resto, deixamos ao leitor a tarefa de compreender o código. Vamos concentrar-nos apenas em três procedimentos: o procedimento [btnExecute_Click], que é executado quando o utilizador solicita a execução da consulta SQL introduzida na vista [form], o procedimento [DataGrid1_PageIndexChanged], que é executado quando o utilizador utiliza os links [Next] e [Previous] no [DataGrid], e o procedimento [DataGrid1_SortCommand], que é executado quando o utilizador clica num cabeçalho de coluna para ordenar os dados nessa ordem. A ordem de ordenação — ascendente ou descendente — é determinada pelos dois botões de opção de ordenação.

No procedimento [btnExécuter_Click], começamos, portanto, por verificar se a página é válida ou não. Quando o procedimento [btnExécuter_Click] é executado, as verificações relacionadas com os vários controlos de validação da página já foram realizadas. Para cada controlo de validação, foram definidos dois atributos:

IsValid
definido como true se os dados verificados forem válidos, false caso contrário
ErrorMessage
a mensagem de erro se os dados verificados forem inválidos

Para a própria página, foi definido um atributo [IsValid]. Este é verdadeiro apenas se todos os controlos de validação tiverem o seu atributo [IsValid] definido como verdadeiro. Se não for esse o caso, a vista [form] deve ser apresentada. Esta vista contém os controlos de validação que irão apresentar o seu atributo [errorMessage]. Se a página for válida, utilizamos o objeto [products] criado por [Application_Start] para obter o [DataSet] correspondente à execução da consulta SQL SELECT. Convertemos isto num objeto [DataView]:


        Dim données As DataView
        Try
            données = CType(Application("objProduits"), produits).getDataSet(txtSelect.Text.Trim).Tables(0).DefaultView
...

Poderíamos ter simplesmente trabalhado com o [DataSet] e escrito:


        Dim données As DataSet
        Try
            données = CType(Application("objProduits"), produits).getDataSet(txtSelect.Text.Trim)
...

Um objeto [DataSet] é, essencialmente, uma coleção de tabelas ligadas por relações. Na nossa aplicação específica, o [DataSet] obtido a partir da classe [products] contém apenas uma tabela, a que resulta da instrução [select]. Uma tabela pode ser ordenada, ao passo que um [DataSet] não; no entanto, estamos interessados em ordenar os dados recuperados. Para trabalhar com a tabela de resultados da instrução [select], poderíamos ter escrito:


        Dim données As DataTable
        Try
            données = CType(Application("objProduits"), produits).getDataSet(txtSelect.Text.Trim).Tables(0)
...

O objeto [DataTable], embora represente uma tabela de base de dados, não possui um método de ordenação. Para o fazer, é necessária uma vista da tabela. Uma vista é um objeto do tipo [DataView]. Pode ter diferentes vistas da mesma tabela utilizando filtros. Uma tabela tem uma vista padrão, que é aquela em que não estão definidos filtros. Representa, portanto, a tabela na sua totalidade. Esta vista padrão é obtida através de [DataTable.DefaultView]. Pode ordenar uma vista utilizando a sua propriedade [sort], que discutiremos mais adiante.

Se a recuperação do [DataSet] da classe [products] for bem-sucedida, a vista [results] é exibida; caso contrário, a vista [errors] é exibida. A vista [results] é exibida através do procedimento [displayResults], ao qual são passados dois parâmetros:

  • o texto a colocar no rótulo [lblSelect]
  • o [DataView] a ligar ao [DataGrid1]

Este exemplo demonstra a grande flexibilidade do componente [DataGrid]. Ele consegue reconhecer a estrutura do [DataView] ao qual está vinculado e adaptar-se a ela. Por fim, o procedimento [btnExécuter_Click] armazena o [DataView] que acabou de obter na sessão do utilizador, para que este esteja disponível quando o utilizador solicitar outras páginas do mesmo [DataView].

O procedimento [DataGrid1_PageIndexChanged] é executado quando o utilizador clica nos links [Next] e [Previous] no [DataGrid]. Recebe dois parâmetros:


    Private Sub DataGrid1_PageIndexChanged(ByVal source As Object, ByVal e As System.Web.UI.WebControls.) Handles DataGrid1.PageIndexChanged
source
o objeto que desencadeou o evento — neste caso, um dos links [Next] ou [Previous]
e
informações sobre o evento. A propriedade e.NewPageIndex é o número da página a ser exibida em resposta ao pedido do cliente

O código completo para o manipulador de eventos é o seguinte:


    Private Sub DataGrid1_PageIndexChanged(ByVal source As Object, ByVal e As System.Web.UI.WebControls.DataGridPageChangedEventArgs) Handles DataGrid1.PageIndexChanged
        ' change page
        With DataGrid1
            .CurrentPageIndex = e.NewPageIndex
            .DataSource = CType(Session("données"), DataView)
            .DataBind()
        End With
    End Sub

O componente [DataGrid] possui um atributo [CurrentPageIndex] que indica o número da página que está a ser exibida ou que será exibida. Atribuímos o valor [NewPageIndex] do parâmetro [e] a este atributo. O [DataGrid] é então vinculado ao [DataView] que foi guardado na sessão pelo procedimento [btnExécuter_Click].

Poder-se-á questionar se o [DataGrid] necessita do atributo [EnableViewState=true], uma vez que o seu conteúdo é calculado pelo código sempre que a página é recarregada. Poder-se-á pensar que não. No entanto, se o [DataGrid] tiver o atributo [EnableViewState=false], observamos que o evento [DataGrid1.PageIndexChanged] nunca é acionado. É por isso que mantivemos [EnableViewState=true]. Sabemos que isto faz com que o conteúdo do [DataGrid] seja armazenado no campo oculto da página [__VIEWSTATE]. Isto pode tornar a página significativamente mais lenta se o [DataGrid] for grande. Se isto for um problema, pode gerir a paginação por si próprio sem utilizar a paginação automática do [DataGrid].

O procedimento [DataGrid1_SortCommand] é executado quando o utilizador clica no cabeçalho de uma das colunas apresentadas pelo [DataGrid] para solicitar a ordenação dos dados na ordem dessa coluna. Recebe dois parâmetros:


    Private Sub DataGrid1_SortCommand(ByVal source As Object, ByVal e As System.Web.UI.WebControls.DataGridSortCommandEventArgs) Handles DataGrid1.SortCommand
source
o objeto que desencadeou o evento — neste caso, um dos links [Next] ou [Previous]
e
informações sobre o evento. A propriedade [e.SortExpression] é o nome da coluna clicada para ordenação

O código completo para o manipulador de eventos é o seguinte:


    Private Sub DataGrid1_SortCommand(ByVal source As Object, ByVal e As System.Web.UI.WebControls.DataGridSortCommandEventArgs) Handles DataGrid1.SortCommand
        ' sort the dataview
        Dim données As DataView = CType(Session("données"), DataView)
        données.Sort = e.SortExpression + " " + CType(IIf(rdCroissant.Checked, "asc", "desc"), String)
        ' we display it
        With DataGrid1
            .CurrentPageIndex = 0
            .DataSource = données
            .DataBind()
        End With
    End Sub

Recuperamos o [DataView] exibido pelo [DataGrid] na sessão atual. Foi colocado lá pelo procedimento [btnExécuter_Click]. O componente [DataView] possui uma propriedade [Sort] à qual atribuímos a expressão de ordenação. Esta segue a sintaxe [select ... order by expr1, expr2, ...], em que cada [expr] pode ser seguida pela palavra-chave [asc] para ordenação ascendente ou [desc] para ordenação descendente. A expressão [order by] aqui utilizada é [order by column asc/desc]. A propriedade [e.SortExpression] fornece-nos o nome da coluna do [DataGrid] em que se clicou para ordenação. A cadeia [asc/desc] é definida com base nos valores dos botões de opção no grupo [rdTri]. Assim que a expressão de ordenação do [DataView] é definida, o [DataGrid] é vinculado a ela. Posicionamos o [DataGrid] na sua primeira página.

8.7. Componente DataList e ligação de dados

Vamos agora concentrar-nos no componente [DataList]. Este oferece mais opções de formatação do que o [DataGrid], mas é menos flexível. Assim, não se pode adaptar automaticamente à fonte de dados à qual está ligado. Esta adaptação deve ser feita através de código, se desejado. Se a estrutura da fonte de dados for conhecida antecipadamente, então este componente oferece opções de formatação que podem torná-lo preferível ao [DataGrid].

8.7.1. Aplicação

Para ilustrar a utilização do [DataList], iremos construir uma aplicação MVC semelhante à anterior:

As três vistas serão incorporadas no código de apresentação do controlador [main.aspx] como contentores. Por conseguinte, esta aplicação tem uma única página [main.aspx].

8.7.2. Classes de Negócio

A classe [products] é a mesma de antes.

8.7.3. As vistas

Quando o utilizador faz o seu primeiro pedido à aplicação, vê a seguinte vista [results1]:

N.º
nome
tipo
propriedades
função
1
Botão de opção 1
Botão de opção 2
Botão de opção
EnableViewState=false
permite-lhe escolher um dos dois estilos [DataList]
2
btnChanger
Botão
EnableViewState=false
Botão [submit]
3
DataList1
DataList
EnableViewState=true
Campo de exibição da lista de dados

Se o utilizador selecionar o estilo n.º 2, verá a seguinte vista [results2]:

N.º
nome
tipo
propriedades
função
1
DataList2
DataList
EnableViewState=true
Campo de exibição da lista de dados

A vista [errors] indica um problema ao aceder à fonte de dados:

N.º
nome
tipo
propriedades
função
1
HTMLErrors
variável
 
Código HTML necessário para apresentar erros

As três vistas da aplicação são três contentores (painéis) diferentes dentro da mesma página [main.aspx]. O seu código de apresentação é o seguinte:


<%@ 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("nom") %>
                            <br>
                            prix :
                            <%# databinder.eval(Container.DataItem,"prix","{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("nom") %>
                            , prix :
                            <%# databinder.eval(Container.DataItem,"prix","{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. Configurar componentes [DataList]

Vamos dar uma olhada nos vários atributos de um componente [DataList]. Existem muitos deles, e abordaremos apenas uma pequena seleção aqui. Pode definir até sete modelos de exibição dentro de um [DataList]:

HeaderTemplate
Modelo de cabeçalho [DataList]
ItemTemplate
modelo para as linhas que exibem os itens na lista de dados associada. Apenas este modelo é obrigatório.
AlternatingItemTemplate
Para distinguir visualmente entre itens exibidos sucessivamente, podem ser utilizados dois modelos: ItemTemplate para o item n, AlternatingItemTemplate para o item n+1
SelectedItemTemplate
Modelo para o item selecionado na [DataList]
SeparatorTemplate
Modelo para o separador entre dois elementos na [DataList]
EditItemTemplate
Uma [DataList] permite-lhe editar os valores que apresenta. [EditItemTemplate] é o modelo para um item na [DataList] que se encontra atualmente no modo «editar»
FooterTemplate
Modelo para o rodapé da [DataList]

O componente [DataList1] foi criado com o [WebMatrix]. Na janela de propriedades, foi selecionado o link [AutoFormat]:

1234567

Acima, o esquema [Color 5] irá gerar uma [DataList] com estilos para os seguintes modelos: HeaderTemplate (1), ItemTemplate (2, 6), AlternatingTemplate (3, 5), SelectedItemTemplate (4), FooterTemplate (7). O código gerado é o seguinte:


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

Não foram definidos modelos. Cabe-nos a nós fazê-lo. Definimos os seguintes modelos:

HeaderTemplate

                        <HeaderTemplate>
                            Conteúdo da tabela [list] na base de dados [products]
                        </HeaderTemplate>
ItemTemplate

                        <ItemTemplate>
                            nome:
                            <%# Container.DataItem("name") %>
                            
                            preço:
                            <%# DataBinder.Eval(Container.DataItem,"preço","{0:C}") %>
                            
                        </ItemTemplate>

Lembre-se de que o modelo [ItemTemplate] é utilizado para apresentar os itens da fonte de dados associada à [DataList]. Esta fonte de dados é uma coleção de linhas de dados, cada uma contendo um ou mais valores. A linha atual na fonte de dados é representada pelo objeto [Container.DataItem]. Essa linha possui colunas. [Container.DataItem("col1")] é o valor da coluna "col1" na linha atual. Para incluir este valor no código de apresentação, escrevemos <%# Container.DataItem("col") %>. Por vezes, queremos apresentar um elemento da linha atual num formato especial. Aqui, queremos apresentar a coluna "price" da linha atual em euros. Utilizamos a função [DataBinder.Eval], que recebe três parâmetros:

  • a linha atual [Container.DataItem]
  • o nome da coluna a formatar
  • a string de formatação no formato {0:format}, em que [format] é um dos formatos aceites pelo método [string.format].

Assim, o código <%# DataBinder.Eval(Container.DataItem,"price",{0:C}) %> exibirá a coluna [price] da linha atual no formato monetário (formato C=Currency).

Teremos, portanto, uma [DataList] com o seguinte aspeto:

Image

Acima, os dados foram organizados com quatro itens de dados por linha. Isto é conseguido utilizando os seguintes atributos [DataList]:

RepeatDirection
Horizontal
RepeatColumns
número desejado de colunas

Em última análise, o código para [DataList1] é o apresentado no trecho de código acima. Deixamos ao leitor a tarefa de estudar o código de apresentação para [DataList2]. Tal como acontece com o componente [DataGrid], a maioria das propriedades [DataList] pode ser definida utilizando um assistente [WebMatrix]. Para o fazer, utilize a ligação [Property Generator] na janela de propriedades [DataList]:

8.7.5. Os controladores

O controlador [global.asax, global.asax.vb] é o seguinte:

[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)
        ' create a product object
        Try
            Dim données As DataSet = New produits(ConfigurationSettings.AppSettings("OLEDBStringConnection")).getDataSet("select * from LISTE")
            ' put the object in the application
            Application("données") = données
            ' no error
            Application("erreur") = False
        Catch ex As Exception
            'there has been an error, we note it in the application
            Application("erreur") = True
            Application("message") = ex.Message
        End Try
    End Sub
End Class

Quando a aplicação é iniciada (Application_Start), criamos um [dataset] a partir da classe de negócios [products] e adicionamo-lo à aplicação para que esteja disponível para todos os pedidos de todos os clientes. Se ocorrer uma exceção durante esta criação, esta é registada na aplicação. O procedimento [Application_Start] será executado apenas uma vez. Depois disso, o controlador [global.asax] deixará de estar envolvido. O controlador [main.aspx.vb] irá então tratar do trabalho:


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
 
    ' components page
    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
    ' data page
    Protected erreursHTML As String
 
    Private Sub Page_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
        ' check for application errors
        If CType(Application("erreur"), Boolean) Then
            ' the application has not initialized correctly
            Dim erreurs As New ArrayList
            erreurs.Add("Application momentanément indisponible (" + CType(Application("message"), String) + ")")
            afficheErreurs(erreurs, False)
            Exit Sub
        End If
        '1st request
        If Not IsPostBack Then
            ' initialize controls
            With DataList1
                .DataSource = CType(Application("données"), DataSet)
                .DataBind()
            End With
            With DataList2
                .DataSource = CType(Application("données"), DataSet)
                .DataBind()
            End With
            ' the empty form is displayed
            afficheRésultats(True, False)
        End If
    End Sub
 
    Private Sub afficheErreurs(ByVal erreurs As ArrayList, ByVal afficheLien As Boolean)
        ' displays the error view
        erreursHTML = ""
        For i As Integer = 0 To erreurs.Count - 1
            erreursHTML += "<li>" + erreurs(i).ToString + "</li>" + ControlChars.CrLf
        Next
        ' the [errors] view is displayed
        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)
        ' the [results] view is displayed
        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
        ' change the style
        afficheRésultats(RadioButton1.Checked, RadioButton2.Checked)
    End Sub
End Class

8.8. Componente Repeater e ligação de dados

O componente [Repeater] permite repetir o código HTML para cada item de uma lista de dados. Suponhamos que queremos apresentar uma lista de erros no seguinte formato:

Image

Já nos deparámos com este problema e resolvemo-lo incluindo uma variável no código de apresentação na forma <% =erreursHTML %>, onde o valor de erreursHTML é calculado pelo controlador. Este valor contém código HTML, especificamente o de uma lista. A desvantagem é que, se quiser modificar a apresentação desta lista HTML, tem de ir à secção do controlador, o que vai contra a separação entre controlador e apresentação. O componente [Repeater] oferece uma solução. Tal como no [DataList], podemos definir o <HeaderTemplate> para o cabeçalho, o <ItemTemplate> para o item atual na lista de dados e o <FooterTemplate> para o final dos dados. Aqui, poderíamos ter a seguinte definição para o 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>

Note que [Container.DataItem] representa uma linha de dados se a fonte de dados tiver várias colunas. Representa um único ponto de dados se a fonte tiver apenas uma coluna. Este será o caso aqui. Por exemplo, estamos a construir a seguinte aplicação:

Image

O código de layout da página é o seguinte:

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

O código de controlo é o seguinte:

<%@ Page Language="VB" %>
<script runat="server">

    ' procedure executed when the page is loaded
    Sub page_Load(sender As Object, e As EventArgs)
        if not IsPostBack then
          ' create a data source
          with Repeater1
              .DataSource=createDataSource
              .DataBind
          end with
        end if
    End Sub

    function createDataSource as ArrayList
      ' create an arraylist
      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>

Na primeira solicitação do cliente, associamos um objeto [ArrayList] ao componente [Repeater], que deve representar uma lista de erros.

8.9. Aplicação

Aqui, revisamos uma aplicação anteriormente implementada com componentes do lado do servidor. A aplicação permite aos utilizadores simular cálculos de impostos. Ela depende de uma classe [impot], que não revisaremos aqui. Esta classe requer dados que recupera de uma fonte de dados OLEDB. Para este exemplo, utilizaremos uma fonte de dados ACCESS. Introduzimos as seguintes novas funcionalidades nesta versão:

  • a utilização de componentes de validação para verificar a validade dos dados
  • a utilização de componentes do servidor ligados a fontes de dados para apresentar resultados

8.9.1. A estrutura MVC da aplicação

A estrutura MVC da aplicação é a seguinte:

As três vistas serão incorporadas no código de apresentação do controlador [main.aspx] como contentores. Portanto, esta aplicação tem uma única página [main.aspx].

8.9.2. As vistas da aplicação

A vista [form] é o formulário para introduzir informações utilizadas para calcular o imposto de um utilizador:

Image

O utilizador preenche o formulário:

Image

Utiliza o botão [Submit] para solicitar o cálculo do imposto. Vê a seguinte vista [simulations]:

Image

Volta ao formulário através do link acima. Encontra-o no estado em que o preencheu. Pode cometer erros na introdução de dados:

Image

Estes são assinalados na vista [formulário]:

Image

O utilizador corrige então os seus erros. Pode executar novas simulações:

Image

Isto abre a vista [simulações] com uma simulação adicional:

Image

Por fim, se a fonte de dados não estiver disponível, o utilizador é notificado na vista [erros]:

Image

8.9.2.1. O código de apresentação

Lembre-se de que a página [main.aspx] reúne todas as vistas. Trata-se de um único formulário com três contentores:

  • [panelform] para a vista [form]
  • [panelerrors] para a vista [errors]
  • [panelsimulations] para a vista [simulations]

Vamos agora detalhar os componentes destes três contentores. O contentor [panelform] tem a seguinte representação visual:

N.º
nome
tipo
propriedades
função
0
formulário de painel
Painel
 
visualização do formulário
1
rdSim
rdNão
Botão de opção
Nome do grupo=rdmarie
botões de opção
2
cvMarie
Validador personalizado
Mensagem de erro=Não especificou o seu estado civil
EnableClientScript=false
verifica se o programa cliente enviou o estado civil esperado
3
txtChildren
TextBox
 
número de filhos
4
rfvChildren
Validador de campo obrigatório
Mensagem de erro=Introduza o número de filhos
ControlToValidate=txtChildren
verifica se o campo [txtChildren] não está vazio
5
rvChildren
Validador de intervalo
ErrorMessage=Introduza um número entre 1 e 30
ControlToValidate=txtChildren
verifica se o campo [txtChildren] está dentro do intervalo [1,30]
6
txtSalary
Caixa de Texto
EnableViewState=true
salário anual
7
rfvSalário
Validador de campo obrigatório
ControlToValidate=txtSalary
Mensagem de erro=Introduza o valor do seu salário
verifica se o campo [txtSalary] não está vazio
8
revSalary
Validador de Expressão Regular
ControlToValidate=txtSalary
Mensagem de erro=Salário inválido
Expressão regular=\s*\d+\s*
verifica se o campo [txtSalary] é uma sequência de dígitos
9
btnCalculate
Botão
ValidationTriggers=true
Botão [submit] no formulário - inicia o cálculo do imposto
10
btnClear
Botão
Validação=false
Botão [submit] do formulário - limpa o formulário

Poderá ficar surpreendido com a verificação [cvMarié], que verifica se o utilizador selecionou um dos dois botões de opção. Isto é necessário porque não há forma de ter a certeza de que o utilizador está a utilizar o formulário enviado pelo servidor. Uma vez que nada pode garantir isso, temos de verificar todos os parâmetros enviados. Repare também no atributo [CausesValidation=false] do botão [btnEffacer]. Quando o utilizador clica neste botão, os dados enviados não devem ser verificados, uma vez que serão ignorados.

Todos os componentes no contentor têm a propriedade [EnableViewState=false], exceto [panelForm, txtEnfants, txtSalaire, rdOui, rdNon]. O contentor [panelerreurs] tem a seguinte representação visual:

N.º
nome
tipo
propriedades
função
0
painelDeErros
Painel
EnableViewState=false
visualização de erro
1
rptErrors
Repetidor
EnableViewState=false
exibe uma lista de erros

O componente [rptErrors] é definido da seguinte forma:


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

A lista de dados associada ao componente [rptErrors] será um objeto [ArrayList] contendo uma lista de mensagens de erro. Assim, como mostrado acima, <%# Container.Dataitem%> refere-se à mensagem de erro atual. O contentor [panelsimulations] tem a seguinte representação visual:

N.º
nome
tipo
propriedades
função
0
simulações de painéis
Painel
EnableViewState=false
visualização da simulação
2
lnkForm2
LinkButton
EnableViewState=false
ligação para o formulário
1
dgSimulations
DataGrid
EnableViewState=false
responsável pela exibição das simulações colocadas num objeto [DataTable]

O componente [dgSimulations] é configurado da seguinte forma no [Webmatrix]. Na janela de propriedades do [dgSimulations], selecionamos o link [AutoFormat] e escolhemos o esquema [Color 3]:

Em seguida, ainda na janela de propriedades [dlgSimulations], selecionamos o link [Property Generator]. Solicitamos que os cabeçalhos das colunas sejam exibidos:

Image

Selecionamos a opção [Columns] para definir as quatro colunas do [DataGrid]:

Image

Desmarcamos a opção [Create columns automatically...]. Iremos definir nós próprios as quatro colunas do [DataGrid]. Estas serão associadas a um objeto [DataTable] contendo quatro colunas denominadas «married», «children», «salary» e «tax», respetivamente. Para criar uma coluna no [DataGrid], selecionamos a opção [Related Columns] e utilizamos o botão de criação, conforme mostrado acima. É criada uma coluna e podemos definir as suas propriedades. A primeira coluna da tabela de simulação conterá a coluna "married" do objeto [DataTable] que será associado ao [DataGrid]. Esta primeira coluna do [DataGrid] é definida da seguinte forma no assistente:

Image

Texto do cabeçalho
título da coluna, aqui «Casado»
Campo de dados
Nome da coluna na fonte de dados que será exibida por esta coluna do [DataGrid]. Aqui, é a coluna "married" do [DataTable].

A segunda coluna é definida da seguinte forma:

Texto do cabeçalho
Filhos
Campo de dados
filhos

A terceira coluna é definida da seguinte forma:

Texto do cabeçalho
Salário anual
Campo de dados
salário
Expressão de formatação
{0:C} - exibição da moeda para obter o símbolo do euro

A quarta coluna é definida da seguinte forma:

Texto do cabeçalho
Montante do imposto
Campo de dados
imposto
Expressão de formatação
{0:C} - exibição da moeda para obter o símbolo do euro

Colocamos o código de apresentação e o código de controlo em dois ficheiros separados. O primeiro estará em [main.aspx] e o segundo em [main.aspx.vb]. O código para [main.aspx] é o seguinte:


<%@ 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. O código de controlo da aplicação

O código de controlo da aplicação está distribuído pelos ficheiros [global.asax.vb] e [main.aspx.vb]. O ficheiro [global.asax] é definido da seguinte forma:

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

O ficheiro [global.asax.vb] é o seguinte:


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)
        ' create an impot object
        Dim objImpot As impot
        Try
            objImpot = New impot(New impotsOLEDB(ConfigurationSettings.AppSettings("chaineConnexion")))
            ' put the object in the application
            Application("objImpot") = objImpot
            ' no error
            Application("erreur") = False
        Catch ex As Exception
            'there has been an error, we note it in the application
            Application("erreur") = True
            Application("message") = ex.Message
        End Try
    End Sub
 
    Sub Session_Start(ByVal sender As Object, ByVal e As EventArgs)
        ' start of session - create a list of empty simulations
        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

Quando a aplicação é iniciada (primeira solicitação feita à aplicação), o procedimento [Application_Start] é executado. Ele tenta criar um objeto do tipo [tax] recuperando os seus dados de uma fonte OLEDB. Recomenda-se ao leitor que reveja o Capítulo 5, onde esta classe foi definida, caso a tenha esquecido. A criação do objeto [impot] pode falhar se a fonte de dados não estiver disponível. Neste caso, o erro é armazenado na aplicação para que todas as solicitações subsequentes saibam que não foi possível inicializá-lo corretamente. Se a criação for bem-sucedida, o objeto [impot] criado também é armazenado na aplicação. Ele será utilizado por todas as solicitações de cálculo de impostos. Quando um cliente faz a sua primeira solicitação, é criada uma sessão para ele pelo procedimento [Application_Start]. Esta sessão destina-se a armazenar as várias simulações de cálculo de impostos que irá realizar. Estas serão armazenadas num objeto [DataTable] associado à chave de sessão "simulations". Quando a sessão é iniciada, esta chave é associada a um objeto [DataTable] vazio cuja estrutura foi definida. A informação necessária à aplicação é colocada no seu ficheiro de configuração [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>

A chave [connectionString] especifica a cadeia de ligação à fonte OLEDB. O resto do código de controlo encontra-se em [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
 
    ' local variables
 
    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

O primeiro evento tratado pelo código é [Page_Load]:


    Private Sub Page_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
        ' first, we look at the state of the application
        If CType(Application("erreur"), Boolean) Then
            ' the application failed to initialize
            ' the error view is displayed
            Dim erreurs As New ArrayList
            erreurs.Add("Application momentanément indisponible (" + CType(Application("message"), String) + ")")
            afficheErreurs(erreurs)
            Exit Sub
        End If
        ' no errors - on the 1st request, the form is presented
        If Not IsPostBack Then afficheFormulaire()
    End Sub

Antes de processar o pedido, verificamos se a aplicação foi inicializada corretamente. Caso contrário, apresentamos a vista [errors] utilizando o procedimento [displayErrors]. Se este for o primeiro pedido (IsPostBack=false), apresentamos a vista [form] utilizando [displayForm].

O procedimento que apresenta a vista [errors] é o seguinte:


    Private Sub afficheErreurs(ByRef erreurs As ArrayList)
        ' builds a list of errors
        With rptErreurs
            .DataSource = erreurs
            .DataBind()
        End With
        ' container display 
        panelerreurs.Visible = True
        panelform.Visible = False
        panelsimulations.Visible = False
        Exit Sub
    End Sub

O procedimento recebe como parâmetro uma lista de mensagens de erro em [errors] do tipo [ArrayList]. Basta ligar esta fonte de dados ao componente [rptErrors] responsável pela sua apresentação.

O procedimento que exibe a vista [form] é o seguinte:


    Private Sub afficheFormulaire()
        ' displays the form
        panelform.Visible = True
        ' the other containers are hidden
        panelerreurs.Visible = False
        panelsimulations.Visible = False
    End Sub

Este procedimento simplesmente torna o contentor [panelform] visível. Os componentes são apresentados com o seu valor enviado ou anterior (VIEWSTATE).

Quando o utilizador clica no botão [Calculate] na vista [form], é enviada uma solicitação POST para [main.aspx]. O procedimento [Page_Load] é executado, seguido pelo procedimento [btnCalculate_Click]:


    Private Sub btnCalculer_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnCalculer.Click
        ' we check the validity of the data entered; if there are any errors, we report them
        If Not Page.IsValid Then
            afficheFormulaire()
            Exit Sub
        End If
        ' no errors - tax is calculated
        Dim impot As Long = CType(Application("objImpot"), impot).calculer( _
        rdOui.Checked, CType(txtEnfants.Text, Integer), CType(txtSalaire.Text, Long))
        ' the result is added to existing simulations
        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)
        ' put the simulations in the session
        Session.Item("simulations") = simulations
        ' the simulations page is displayed
        afficheSimulations(simulations, "Retour au formulaire")
    End Sub

O procedimento começa por verificar a validade da página. Recorde-se que, quando o procedimento [btnCalculate_Click] é executado, os controlos de validação já tiveram de fazer o seu trabalho e o atributo [IsValid] da página já foi definido. Se a página for inválida, a vista [form] é apresentada novamente e o procedimento termina. Os componentes de validação da vista [form] cujo atributo [IsValid] esteja definido como [false] exibirão o seu atributo [ErrorMessage]. Se a página for válida, o montante do imposto é calculado utilizando o objeto [tax] que foi armazenado na aplicação no momento do arranque. Esta nova simulação é adicionada à lista de simulações já realizadas e armazenadas na sessão.

Por fim, a vista [simulations] é exibida pelo seguinte procedimento [displaySimulations]:


    Private Sub afficheSimulations(ByRef simulations As DataTable, ByRef lien As String)
        ' we link the datagrid to the simulations source
        With dgSimulations
            .DataSource = simulations
            .DataBind()
        End With
        ' link
        lnkForm2.Text = lien
        ' the views
        panelsimulations.Visible = True
        panelerreurs.Visible = False
        panelform.Visible = False
    End Sub

O procedimento tem dois parâmetros:

  • uma lista de simulações em [simulations] do tipo [DataTable]
  • um texto de ligação em [link]

A fonte de dados [simulations] está associada ao componente responsável pela sua exibição. O texto do link é colocado na propriedade [Text] do objeto [LinkButton] na vista.

Quando o utilizador clica no botão [Delete] na vista [form], o procedimento [btnDelete_click] é executado (sempre após [Page_Load]):


    Private Sub btnEffacer_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnEffacer.Click
        ' displays the empty form
        razForm()
        afficheFormulaire()
    End Sub
 
    Private Sub razForm()
        ' empty the form
        rdOui.Checked = False
        rdNon.Checked = True
        txtEnfants.Text = ""
        txtSalaire.Text = ""
    End Sub

O código acima é suficientemente simples para não necessitar de comentários. Ainda precisamos de tratar os cliques nos links de visualização [errors] e [simulations]:


    Private Sub lnkForm1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles lnkForm1.Click
        ' displays the form
        afficheFormulaire()
    End Sub
 
    Private Sub lnkForm2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles lnkForm2.Click
        ' displays the form
        afficheFormulaire()
    End Sub

Ambos os procedimentos limitam-se a apresentar a vista [form]. Sabemos que os campos nesta vista receberão um valor que é ou o valor enviado para eles ou o seu valor anterior. Uma vez que, neste caso, o pedido POST do cliente não envia quaisquer valores para os campos do formulário, estes reverterão para os seus valores anteriores. O formulário é, portanto, apresentado com os valores introduzidos pelo utilizador.

8.9.4. Testes

Todos os ficheiros necessários à aplicação são colocados numa pasta denominada <application-path>:
A pasta [bin] contém a DLL com as classes [impot], [impotsData] e [impotsOLEDB] necessárias para a aplicação:

Se desejar, o leitor pode consultar o Capítulo 5, que explica como criar o ficheiro [impot.dll] mencionado acima. Feito isto, o servidor Cassini é iniciado com os parâmetros (<application-path>,/impots6). Acedemos então ao URL [http://impots6/main.aspx] utilizando um navegador:

Image

Se renomear o ficheiro ACCESS [impots.mdb] para [impots1.mdb], verá a seguinte página:

Image

8.9.5. Conclusão

Temos uma aplicação MVC que utiliza apenas componentes do lado do servidor. A utilização destes componentes permitiu-nos separar completamente a camada de apresentação da aplicação da sua camada de controlo. Isto não tinha sido possível até agora, uma vez que o código de apresentação continha anteriormente variáveis calculadas pelo código de controlo, cujos valores incluíam código HTML. Agora, isso já não acontece.