Skip to content

8. Componentes do servidor ASP - 2

8.1. Introduction

Continuamos o nosso trabalho na interface do utilizador, explorando:

  • componentes de validação de dados
  • como associar dados a componentes de servidor
  • os componentes de servidor HTML

8.2. Os componentes de validação de dados

8.2.1. Introdução

Nas aplicações com formulários, é fundamental verificar a validade dos dados introduzidos nos mesmos. No exemplo sobre o cálculo de impostos, tínhamos o seguinte formulário:

Image

Após a receção dos valores deste formulário, é necessário verificar a validade dos dados introduzidos: o número de filhos e o salário devem ser números inteiros positivos ou nulos. Se não for esse o caso, o formulário é reenviado ao cliente tal como foi preenchido, acompanhado de mensagens que indicam os erros. Tratámos este caso com duas vistas, uma para o formulário acima referido e outra para os erros:

Image

O ASP.NET disponibiliza componentes denominados «componentes de validação» que permitem verificar os seguintes casos:

Componente
Função
RequiredFieldValidator
verifica se um campo não está vazio
CompareValidator
verifica se dois valores são iguais
RangeValidator
verifica se um valor está entre dois limites
RegularExpressionValidator
verifica se um campo cumpre 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 emitidas pelos controlos anteriores num único local da página

Vamos agora apresentar cada um destes componentes.

8.2.2. RequiredFieldValidator

Criamos a seguinte página [requiredfieldvalidator1.aspx]:

n.º
nome
tipo
propriedades
função
1
txtNom
TextBox
EnableViewState=true
campo de introdução
2
RequiredFieldValidator1
RequiredFieldValidator
EnableViewState=false
EnableClientScript=true
ErrorMessage=O campo [nome] é obrigatório
componente de validação
3
btnEnvoyer
Botão
EnableViewState=false
CausesValidation=true
botão [submit]

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

ControlToValidate
campo cujo valor deve ser verificado pelo componente. O que significa o valor de um componente? É o valor do atributo [value] da baliza HTML correspondente. Para um [TextBox], será o conteúdo do campo de introdução de dados; para um [DropDownList], será o valor do elemento selecionado.
EnableClientScript
booleano — se for verdadeiro, indica que o conteúdo do campo anterior deve ser verificado também do lado do cliente. Neste caso, o formulário só será enviado pelo navegador se não contiver erros. No entanto, os testes de validação são realizados também no servidor, para o caso de o cliente não ser um navegador, por exemplo.
ErrorMessage
a mensagem de erro que o componente deve apresentar caso seja detetado um erro

A verificação dos dados da página através dos controlos de validação só é efetuada se o botão ou a ligação que provocou o [POST] da página tiver a propriedade [CausesValidation=true]. [true] é o valor por predefinição desta propriedade.

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

Image

O código HTML recebido por [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>

Vê-se que o botão [btnEnvoyer] está associado a uma função JavaScript através do seu atributo [onclick]. É também possível observar que a página não contém qualquer 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 se encarregará de verificar a validade dos dados. Utilizemos o botão [Envoyer] sem atribuir qualquer nome. A resposta do servidor é a seguinte:

Image

O que aconteceu? O formulário foi enviado para o servidor. Este executou o código de todos os componentes de validação presentes na página. Neste caso, 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 os componentes com [EnableViewState=true], juntamente com uma mensagem de erro para cada componente de validação que tenha detetado um erro). Esta mensagem corresponde ao atributo [ErrorMessage] do componente de validação. É este mecanismo que vemos em ação acima. Se inserirmos algo no campo [nom], a mensagem de erro deixa de aparecer quando validamos o formulário:

Image

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

Image

O código HTML recebido por 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>

Vê-se que este código é muito mais extenso do que o recebido por [Mozilla]. Não entraremos em pormenores. A diferença deve-se ao facto de o servidor ter incluído funções JavaScript no documento HTML enviado. Por que razão existem dois códigos HTML diferentes, se o URL solicitado pelos dois navegadores é o mesmo? Já tivemos oportunidade de referir que a tecnologia ASP.NET fazia com que o servidor adaptasse o documento HTML enviado ao cliente ao nível desse cliente. Existem no mercado vários navegadores, nem todos com as mesmas capacidades. Os navegadores da Microsoft e da Netscape, por exemplo, não utilizam a mesma modelagem de objetos para o documento que recebem. Por isso, o código JavaScript para manipular esse documento no lado do navegador apresenta diferenças entre os dois navegadores. Da mesma forma, os editores têm criado versões sucessivas dos seus navegadores, melhorando constantemente as suas capacidades. Assim, um documento HTML compreendido pelo IE5 pode não ser compreendido pelo IE3. Temos acima um exemplo desta adaptação do servidor ao nível do seu cliente. Por uma razão que não foi aqui aprofundada, o servidor web considerou que o cliente [Mozilla] não tinha capacidade para gerir o código JavaScript de validação. Por isso, esse código não foi incluído no documento HTML que lhe foi enviado e a validação foi efetuada do lado do servidor. No caso de [IE6], este código JavaScript foi incluído no documento HTML enviado, como podemos ver. Para ver como funciona, vamos fazer a seguinte experiência:

  • paremos o servidor web
  • utilizemos o botão [Envoyer] sem preencher o campo [nom]

Obtemos a seguinte nova página:

Image

Foi mesmo o navegador que apresentou a mensagem de erro. De facto, o servidor está parado. Podemos confirmar isso introduzindo algo no campo [nom] e clicando em «Validar». Desta vez, a resposta é a seguinte:

Image

O que aconteceu? O navegador executou o código JavaScript de validação. Esse código indicou que a página estava válida. Por isso, o navegador enviou o formulário para o servidor web, que estava desligado. O navegador detetou isso e apresentou a página acima. Se reiniciarmos o servidor, tudo volta ao normal.

O que podemos retirar deste exemplo?

  • a importância do conceito de componente de validação, que permite reenviar ao cliente qualquer formulário incorreto
  • a importância do [VIEWSTATE], que permite reenviar esse formulário tal como foi preenchido
  • a capacidade de adaptação do servidor ao seu cliente. Este é identificado pelo cabeçalho HTTP [User-Agent:] que envia ao servidor. Portanto, não é o servidor que «adivinha» com quem está a lidar. Esta capacidade de adaptação reveste-se de grande interesse para o programador, que não precisa de se preocupar com o tipo de cliente da sua aplicação.

8.2.3. CompareValidator

Criamos a seguinte página [comparevalidator1.aspx]:

Image

n.º
nome
tipo
propriedades
função
1
cmbChoix1
DropDownList
EnableViewState=true
lista suspensa
2
cmbChoix2
DropDownList
EnableViewState=true
lista suspensa
3
CompareValidator1
CompareValidator
EnableViewState=false
EnableClientScript=true
ErrorMessage=Opção 1 inválida
ValorParaComparar=?
Operador=NãoIgual
Tipo=string
componente de validação
4
CompareValidator2
CompareValidator
EnableViewState=false
EnableClientScript=true
ErrorMessage=Opção 2 inválida
ControlToCompare=?
Operator=NotEqual
Tipo=string
componente de validação
5
btnEnvoyer
Botão
EnableViewState=false
CausesValidation=true
botão [submit]

O componente [CompareValidator] serve para comparar dois valores entre si. Os operadores que podem ser utilizados são [Equal, NotEqual, LessThan, LessThanEqual, GreaterThan, GreaterThanEqual]. O primeiro valor é definido pela propriedade [ControlToValidate], o segundo por [ValueToCompare], se o valor anterior tiver de ser comparado com uma constante, ou por [ControlToCompare], se tiver de ser comparado com o valor de outro componente. As propriedades importantes do componente [CompareValidator] são as seguintes:

ControlToValidate
campo cujo conteúdo deve ser verificado pelo componente
EnableClientScript
booleano — se for verdadeiro, indica que o conteúdo do campo anterior também deve ser verificado do lado do cliente
ErrorMessage
a mensagem de erro que o componente deve apresentar caso seja detetado um erro
ValeurToCompare
valor com o qual o valor do campo deve ser comparado [ControlToValidate]
ControlToCompare
componente cujo valor deve ser comparado com o valor do campo [ControlToValidate]
Operator
operador de comparação entre os dois valores
Type
tipo dos valores a comparar

O código de apresentação 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 da página são as seguintes:

  • o valor selecionado em [cmbChoix1] deve ser diferente da cadeia «?». 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=NotEqual, ControlToCompare=cmbChoix1, Tipo=string

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

Image

Se a mesma página for acedida através do Internet Explorer 6, é incluído nela código JavaScript que resulta num funcionamento ligeiramente diferente. Eventuais erros são assinalados assim que o utilizador alterar o valor numa das listas suspensas. Trata-se de uma melhoria do conforto do utilizador.

8.2.4. CustomValidator, RangeValidator

Criamos a seguinte página [customvalidator1.aspx]:

Image

n.º
nome
tipo
propriedades
função
1
cmbDiplomes
DropDownList
EnableViewState=true
lista suspensa
2
CompareValidator2
CompareValidator
EnableViewState=false
EnableClientScript=true
ErrorMessage=Diploma inválido
Operator=NotEqual
ValueToCompare=?
Type=String
componente de validação do controlo [1]
3
txtAutreDiplome
TextBox
EnableViewState=false
campo de introdução
4
CustomValidator1
CustomValidator
EnableViewState=false
EnableClientScript=true
ErrorMessage=Precisão do diploma inválida
ClientValidationFunction=chkOutroDiploma
componente de validação do campo [3]
5
txtAnDiplome
TextBox
EnableViewState=false
campo de introdução
6
RangeValidator1
RangeValidator
EnableViewState=false
EnableClientScript=true
ErrorMessage=O ano de conclusão do curso deve estar no intervalo [1990,2004]
MinValue=1990
MaxValue=2004
Tipo=Inteiro
ControlToValidate=txtAnDiplome
componente de validação do campo [5]
7
RequiredFieldValidator2
RequiredFieldValidator
EnableViewState=false
EnableClientScript=true
ErrorMessage=É necessário indicar o ano de conclusão do curso
ControlToValidate=txtAnDiplome
componente de validação do campo [5]
8
btnEnvoyer
Botão
EnableViewState=false
CausesValidation=true
botão [submit]

O campo [RangeValidator] serve para verificar se o valor de um controlo se encontra entre os limites [MinValue] e [MaxValue]. As suas propriedades são as seguintes:

ControlToValidate
campo cujo valor deve ser verificado pelo componente
EnableClientScript
booleano — se for verdadeiro, indica que o conteúdo do campo anterior também deve ser verificado do lado do cliente
ErrorMessage
a mensagem de erro que o componente deve apresentar caso seja detetado um erro
MinValue
valor mínimo do campo a verificar
MaxValue
valor máximo do campo a verificar
Type
tipo do valor do campo a verificar

O campo [CustomValidator] permite efetuar validações que os componentes de validação propostos pelo ASP.NET não conseguem realizar. Esta validação é efetuada por uma função escrita pelo programador. Esta é executada no lado do servidor. Como a verificação também pode ser feita no lado do cliente, o programador pode ter de desenvolver uma função JavaScript que incluirá no documento HTML. As suas propriedades são as seguintes:

ControlToValidate
campo cujo valor deve ser verificado pelo componente
EnableClientScript
booleano — se for verdadeiro, indica que o conteúdo do campo anterior também deve ser verificado do lado do cliente
ErrorMessage
a mensagem de erro que o componente deve apresentar caso seja detetado um erro
ClientValidationFunction
a função a executar no lado do cliente

O código de apresentação da 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. Acima, o componente [CustomValidator1] recorre ao seguinte procedimento [CustomValidator1_ServerValidate_1]:

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

    Sub CustomValidator1_ServerValidate_1(sender As Object, e As ServerValidateEventArgs)
        ' o campo [txtAutreDiplome] deve ser preenchido se [cmbDiplomes]=[autre]
        e.isvalid=not (cmbDiplomes.selecteditem.text="[Autre]" and txtAutreDiplome.text.trim="") _
            and not (cmbDiplomes.selecteditem.text<>"[Autre]" and cmbDiplomes.selecteditem.text<>"?" and txtAutreDiplome.text.trim<>"")
    End Sub

</script>
....

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

  • sender: objeto que provocou o evento
  • e: o evento. O procedimento deve definir o atributo [e.IsValid] como verdadeiro se os dados verificados estiverem corretos e como falso caso contrário.

Neste caso, as verificações efetuadas são as seguintes:

  • a lista suspensa [cmbDiplomes] não pode ter o valor [?]. Isto é verificado pelo componente [CompareValidator2]
  • o utilizador seleciona um diploma na lista suspensa [cmbDiplomes]. Se o seu diploma não existir na lista, tem a possibilidade de selecionar a opção [Autre] da lista. Deve, então, indicar o seu diploma no campo de introdução de dados [txtAutreDiplome]. Se [Autre] for selecionado em [cmbDiplomes], então o campo [txtAutreDiplome] não pode estar vazio. Se nem [Autre] nem [?] forem selecionados em [cmbDiplomes], então o campo [txtAutreDiplome] deve estar vazio. Isto é verificado pela função associada ao componente [CustomValidator1].
  • O ano de obtenção do diploma deve estar no intervalo [1900-2004]. Isto é verificado por [RangeValidator1]. No entanto, se o utilizador não introduzir nada no campo, a função de validação de [RangeValidator1] não é utilizada. Por isso, adiciona-se o componente [RequiredFieldValidator2] para verificar a presença de conteúdo. Esta é uma regra geral. O conteúdo de um campo não é verificado se este estiver vazio. O único caso em que é verificado é aquele em que está associado a um componente [RequiredFieldValidator].

Eis 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=chkOutroDiploma

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

<head>
    <meta http-equiv="pragma" content="no-cache" />
    <script language="javascript">
        function chkAutreDiplome(source,args){
             // verifica a validade do campo 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 de servidor [CustomValidator1_ServerValidate_1]:

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

8.2.5. RegularExpressionValidator

Criamos a seguinte página [regularexpressionvalidator1.aspx]:

Image

n.º
nome
tipo
propriedades
função
1
txtMel
TextBox
EnableViewState=true
campo de introdução
2
RequiredFieldValidator3
RequiredFieldValidator
ControlToValidate=txtMel
Display=Dynamic
componente de validação do controlo [1]
3
RegularExpressionValidator1
RegularExpressionValidator
ControlToValidate=txtMel
Display=Dynamic
componente de validação do controlo [1]
4
btnEnvoyer
Botão
EnableViewState=false
CausesValidation=true
botão [submit]

Aqui, pretendemos verificar o formato de um endereço de e-mail. Fazemo-lo com um componente [RegularExpressionValidator], que permite verificar a validade de um campo através de uma expressão regular. Recorde-se que uma expressão regular é um padrão de cadeia de caracteres. Verificamos, portanto, o conteúdo de um campo em relação a um padrão. Como o conteúdo de um campo não é verificado se estiver vazio, precisamos também 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 dos componentes já analisados:

ControlToValidate
campo cujo valor deve ser verificado pelo componente
EnableClientScript
booleano — se for verdadeiro, indica que o conteúdo do campo anterior também deve ser verificado do lado do cliente
ErrorMessage
a mensagem de erro que o componente deve apresentar caso seja detetado um erro
RegularExpression
a expressão regular com a qual o conteúdo de [ControlToValidate] será comparado
Display
modo de exibição: Static: o campo está sempre presente, mesmo que não exiba qualquer mensagem de erro; Dynamic: o campo só está presente se houver uma mensagem de erro; None: a mensagem de erro não é exibida. Este campo também existe para os outros componentes, mas não tinha sido utilizado até agora.

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

Um endereço de e-mail tem o formato [champ1.champ2....@champA.champB...]. Deve haver, obrigatoriamente, pelo menos um campo antes do símbolo @ e pelo menos dois campos a seguir. Estes campos são constituídos por caracteres alfanuméricos e pelo símbolo -. O caractere alfanumérico é representado pelo símbolo \w. A sequência [\w-] significa o carácter \w ou o carácter -. O sinal + após uma sequência S significa que esta pode repetir-se uma ou mais vezes; o sinal * significa que pode repetir-se zero ou mais vezes; o sinal ? significa que pode repetir-se zero ou uma vez.

Um campo do endereço de e-mail corresponde ao modelo [\w-]+. O facto de ser obrigatório haver pelo menos um campo antes do símbolo @ e pelo menos dois depois, separados pelo símbolo ., corresponde ao modelo: [\w-]+(\.[\w-]+)*@[\w-]+(\.[\w-]+)+. É possível permitir que o utilizador insira espaços antes e depois do endereço. Estes serão removidos durante o processamento. Uma sequência de espaços, eventualmente vazia, é representada pelo padrão \s*. Daí a expressão regular do endereço de e-mail: \s*[\w-]+(\.[\w-]+)*@[\w-]+(\.[\w-]+)+\s*.

O código de apresentação 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. ValidationSummary

Por razões estéticas, pode ser desejável reunir as mensagens de erro num único local, como no exemplo seguinte [summaryvalidator1.aspx], onde reunimos numa única página todos os exemplos anteriores:

Image

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

  • [Display=Dynamic], o que faz com que os controlos não ocupem espaço na página se a sua mensagem de erro estiver vazia
  • [EnableClientScript=false] para impedir qualquer validação do lado do cliente
  • [Text=*]. Esta será a mensagem apresentada em caso de erro, sendo que o conteúdo do atributo [ErrorMessage] é, por sua vez, apresentado pelo controlo [ValidationSummary] que apresentamos abaixo.

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

Image

n.º
nome
tipo
propriedades
função
1
ValidationSummary1
ValidationSummary
HeaderText=Ocorreram os seguintes erros, EnableClientScript=false, ShowSummary=true
exibe os erros de todos os controlos de validação da página
2
lnkErreursToFormulaire
LinkButton
CausesValidation=false
ligação de retorno ao formulário

No caso do link [lnkErreursToFormulaire], não é necessário ativar a validação, uma vez que este link não envia nenhum valor para verificação.

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

Image

1

n.º
nome
tipo
propriedades
função
1
lblInfo
Rótulo
 
mensagem informativa destinada ao utilizador

O código de apresentação 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>

Eis alguns exemplos dos resultados obtidos. Validamos a vista [formulaire] sem introduzir quaisquer valores:

Image

O botão [Envoyer] apresenta-nos a seguinte resposta:

Image

Foi apresentada a vista [erreurs]. Se utilizarmos o link de regresso ao formulário, este apresenta-se no seguinte estado:

Image

Trata-se da vista [formulaire]. Repare-se no caractere [*] junto aos dados errados. Foi apresentado o campo [Text] dos controlos de validação. Se preenchermos corretamente os campos, obteremos a vista [infos]:

Image

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

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

     ' procedimento executado ao carregar a página
    Sub page_Load(sender As Object, e As EventArgs)
        ' na primeira solicitação, é apresentada a vista [formulaire]
        if not ispostback then
            afficheVues(true,false,false)
        end if
    end sub

    sub afficheVues(byval formulaireVisible as boolean, _
        erreursVisible as boolean, infosVisible as boolean)
         ' conjunto de vistas
        vueFormulaire.visible=formulaireVisible
        vueErreurs.visible=erreursVisible
        vueInfos.visible=infosVisible
    end sub

    Sub CustomValidator1_ServerValidate(sender As Object, e As ServerValidateEventArgs)
...
    End Sub

    Sub CustomValidator2_ServerValidate(sender As Object, e As ServerValidateEventArgs)
...
    End Sub

    Sub CustomValidator1_ServerValidate_1(sender As Object, e As ServerValidateEventArgs)
...
    End Sub

    Sub lnkErreursToFormulaire_Click(sender As Object, e As EventArgs)
        ' exibe a vista do formulário
        afficheVues(true,false,false)
         ' repete as verificações de validade
        Page.validate
    End Sub

    Sub btnEnvoyer_Click(sender As Object, e As EventArgs)
        ' a página é válida?
        if not Page.IsValid then
            ' exibe-se a vista [erreurs]
            afficheVues(false,true,false)
        else
             ' caso contrário, a vista [infos]
            lblInfo.Text="Le dossier de candidature au DESS IAIE a été envoyé à l'adresse ["+ _
                        txtMel.Text + "]. Nous vous en souhaitons bonne réception.<br><br>Le secrétariat du DESS."
            afficheVues(false,false,true)
        end if
    end sub

</script>
<html>
...
</html>
  • Na rotina [Page_Load], executada em cada solicitação do cliente, apresentamos a vista [formulaire], ficando as restantes ocultas. Isto ocorre apenas na primeira solicitação. O procedimento recorre a um procedimento utilitário [afficheVues], ao qual são passados três valores booleanos para determinar se as três vistas devem ou não ser apresentadas.
  • Quando o procedimento [btnEnvoyer_Click] é chamado, as verificações de dados já foram efetuadas. O botão [btnEnvoyer] possui a propriedade [CausesValidation=true], que força essa verificação de dados. Todos os controlos de validação foram executados e a respetiva propriedade [IsValid] foi definida. Esta propriedade indica se os dados verificados pelo controlo eram válidos ou não. Além disso, a propriedade [IsValid] da própria página também foi definida. Esta propriedade assume o valor [vrai] apenas se a propriedade [IsValid] de todos os controlos de validação da página tiver o valor [vrai]. 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 [erreurs]. Esta contém o controlo [ValidationSummary], que retoma os atributos [ErrorMessage] de todos os controlos (ver captura de ecrã acima). Se a página for válida, é apresentada a vista [infos] com uma mensagem informativa.
  • O procedimento [lnkErreursToFormulaire_Click] é responsável por apresentar a vista [formulaire] no estado em que foi validada. Todos os campos de introdução de dados da vista [formulaire] que possuem a propriedade [EnableViewState=true] têm o seu estado automaticamente regenerado. Curiosamente, o estado dos componentes de validação não é restaurado. De facto, seria de esperar que, ao regressar à vista [erreurs], os controlos com erros exibissem o seu campo [Text]. Não é esse o caso. Por isso, forçámos a validação dos dados utilizando o método [Page.Validate] da página. Isto deve ser feito assim que o painel [vueFormulaire] for tornado visível. Existem, portanto, duas validações no total. Na prática, isto deveria ser evitado. Neste caso, o exemplo permitiu-nos introduzir novos conceitos sobre a validação da página.

8.3. Componentes ListControl e ligação de dados

Alguns dos componentes de servidor analisados permitem apresentar uma lista de valores (DropDownList, ListBox). Outros, que ainda não apresentámos, permitem apresentar várias listas de valores em tabelas HTML. Em todos os casos, é possível, por meio de um programa, associar, um a um, os valores das listas ao componente correspondente. Também é possível associar objetos mais complexos a estes componentes, tais como objetos do tipo [Array], [ArrayList], [DataSet], [HashTable], ... o que simplifica o código que associa os dados ao componente. A esta associação chama-se ligação de dados.

Todos os componentes derivados da classe [ListControl] podem ser associados a uma lista de dados. Trata-se dos componentes [DropDownList], [ListBox], [CheckButtonList], [RadioButtonList]. Cada um destes componentes pode ser ligado a uma fonte de dados. Esta pode ser diversa: [Array], [ArrayList], [DataTable], [DataSet], [HashTable],...., em geral, um objeto que implemente uma das interfaces IEnumerable, ICollection, IListSource. Apresentaremos apenas alguns deles. Um objeto [DataSet] é um objeto de imagem de uma base de dados relacional. Trata-se, portanto, de um conjunto de tabelas ligadas por relações. O objeto [DataTable] representa uma dessas tabelas. A fonte de dados atribui as propriedades [Text] e [Value] a cada um dos membros [Item] do objeto [ListControl]. Se T for o valor de [Text] e V o valor de [Value], a baliza 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

A associação de um componente [ListControl] a uma fonte de dados é feita através das seguintes propriedades:

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

A associaçã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 associação desta a um componente [ListControl] será efetuada de forma diferente:

Array A
[ListControl].DataSource=A
os campos [Text] e [Value] dos elementos de [ListControl] terão como valores os elementos de A
ArrayList AL
[ListControl].DataSource=AL
os campos [Text] e [Value] dos elementos de [ListControl] terão como valores os elementos de 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 elementos de [ListControl] terão como valores os 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 de DS.
[ListControl].DataTextField="col1", [ListControl]. DatavalueField ="col2", em que col1 e col2 são duas colunas da tabela "table". Os campos [Text] e [Value] dos elementos de [ListControl] terão como valores os das colunas col1 e col2 da tabela «table»
HashTable HT
[ListControl].DataSource=HT, [ListControl].DataTextField="key", [ListControl]. DatavalueField ="value", em que [key] e [value] são, respetivamente, as chaves e os valores de HT.

Vamos aplicar estas informações ao exemplo seguinte, [databind1.aspx]:

Temos aqui cinco ligações de dados, cada uma com quatro controlos do tipo [DropDownList], [ListBox], [CheckBoxList] e [RadioButtonList]. As cinco ligações analisadas diferem nas suas fontes de dados:

ligação
fonte de dados
1
Array
2
ArrayList
3
DataTable
4
DataSet
5
HashTable

8.3.1. Código de apresentação dos componentes

O código de apresentação dos controlos da 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>

O das ligações 2 a 5 é idêntico, exceto pelo número da ligação.

8.3.2. Ligação a uma fonte de dados do tipo «Array»

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

' os dados globais
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

' procedimento executado ao carregar a página
Sub page_Load(sender As Object, e As EventArgs)
    if not IsPostBack then
      ' criam-se as fontes  dos dados que serão associados aos componentes
      createDataSources
       ' ligação a uma tabela [Array]
      bindToArray
       ' ligação a uma lista [ArrayList]
      bindToArrayList
       ' ligação a uma tabela [DataTable]
      bindToDataTable
       ' ligação a um grupo de dados [DataSet]
      bindToDataSet
       ' ligação a um dicionário [HashTable]
      bindToHashTable
    end if
End Sub

sub createDataSources
   ' cria as fontes de dados que serão associadas aos componentes
  ' arraylist
  dim i as integer
  for i=0 to textes.length-1
    myDataListe.add(textes(i))
  next
   ' tabela de dados
   ' definem-se as suas duas colunas
  myDataTable.Columns.Add("id",Type.GetType("System.Int32"))
  myDataTable.Columns.Add("texte",Type.GetType("System.String"))
  ' preenche-se a tabela
  dim ligne as DataRow
  for i=0 to textes.length-1
    ligne=myDataTable.NewRow
    ligne("id")=i
    ligne("texte")=textes(i)
    myDataTable.Rows.Add(ligne)
  next
   ' conjunto de dados - uma única tabela
  myDataSet.Tables.Add(myDataTable)
  ' tabela hash
  for i=0 to textes.length-1
    myHashTable.add(valeurs(i),textes(i))
  next
end sub

' ligação à tabela
sub bindToArray
         ' a associação aos componentes
        with DropDownList1
            .DataSource=textes
            .DataBind
        end with
        with ListBox1
            .DataSource=textes
            .DataBind
        end with
        with CheckBoxList1
            .DataSource=textes
            .DataBind
        end with
        with RadioButtonList1
            .DataSource=textes
            .DataBind
        end with
        ' a seleção dos elementos
        ListBox1.Items(1).Selected=true
        ListBox1.Items(3).Selected=true
        CheckBoxList1.Items(0).Selected=true
        CheckBoxList1.Items(3).Selected=true
        DropDownList1.SelectedIndex=2
        RadioButtonList1.SelectedIndex=1
end sub

sub bindToArrayList
....
end sub

sub bindToDataTable
...
end sub

sub bindToDataSet
...
end sub

A ligação dos controlos aos dados é efetuada aqui apenas na primeira consulta. Posteriormente, os controlos manterão os seus elementos através do mecanismo [VIEWSTATE]. A ligação é efetuada no procedimento [bindToArray]. Sendo a fonte de dados do tipo [Array], apenas o campo [DataSource] dos componentes [ListControl] é inicializado. O preenchimento do controlo com os valores da fonte de dados associada é realizado pelo método [ListControl].DataBind. Só depois é que os objetos [ListControl] passam a ter elementos. É então possível selecionar alguns deles.

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

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

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

' procedimento executado ao carregar a página
Sub page_Load(sender As Object, e As EventArgs)
    if not IsPostBack then
      ' criam-se as fontes  de dados que serão associadas aos componentes
      createDataSources
...
    end if
End Sub

sub createDataSources
   ' criam-se as fontes de dados que serão associadas aos componentes
  ' 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] da ligação 2 é efetuada da seguinte forma no procedimento [bindToArrayList] do código de controlo:

' ligação da arraylist
sub bindToArrayList
         ' a associação aos componentes
        with DropDownList2
            .DataSource=myDataListe
            .DataBind
        end with
        with ListBox2
            .DataSource=myDataListe
            .DataBind
        end with
        with CheckBoxList2
            .DataSource=myDataListe
            .DataBind
        end with
        with RadioButtonList2
            .DataSource=myDataListe
            .DataBind
        end with
        ' a seleção dos elementos
        ListBox2.Items(1).Selected=true
        ListBox2.Items(3).Selected=true
        CheckBoxList2.Items(0).Selected=true
        CheckBoxList2.Items(3).Selected=true
        DropDownList2.SelectedIndex=2
        RadioButtonList2.SelectedIndex=1
end sub

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

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

' procedimento executado ao carregar a página
Sub page_Load(sender As Object, e As EventArgs)
    if not IsPostBack then
      ' criam-se as fontes  de dados que serão associadas aos componentes
      createDataSources
...
    end if
End Sub

sub createDataSources
   ' criam-se as fontes de dados que serão associadas aos componentes
...
   ' tabela de dados
   ' definem-se as suas duas colunas
  myDataTable.Columns.Add("id",Type.GetType("System.Int32"))
  myDataTable.Columns.Add("texte",Type.GetType("System.String"))
  ' preenche-se a tabela
  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 [texte]. A coluna [id] irá preencher o campo [Value] dos elementos de [ListControl] e a coluna [texte] irá preencher os respetivos campos [Text]. A construção do [DataTable] com duas colunas é feita da seguinte forma:

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

Cria-se, portanto, uma tabela com duas colunas:

  • a primeira, denominada «id», é do tipo inteiro
  • a segunda, denominada «texto», é do tipo cadeia de caracteres

Uma vez criada a estrutura da tabela, 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] irá conter números inteiros [0,1,..,n], enquanto a coluna [texte] irá conter os valores da tabela [data]. Feito isto, a tabela [dataListe] fica preenchida. A ligação dos quatro controlos [ListBox] da ligação 3 é efetuada da seguinte forma no procedimento [bindToDataTable] do código de controlo:

sub bindToDataTable
         ' a associação aos componentes
        with DropDownList3
            .DataSource=myDataTable
            .DataValueField="id"
            .DataTextField="texte"
            .DataBind
        end with
        with ListBox3
            .DataSource=myDataTable
            .DataValueField="id"
            .DataTextField="texte"
            .DataBind
        end with
        with CheckBoxList3
            .DataSource=myDataTable
            .DataValueField="id"
            .DataTextField="texte"
            .DataBind
        end with
        with RadioButtonList3
            .DataSource=myDataTable
            .DataValueField="id"
            .DataTextField="texte"
            .DataBind
        end with
        ' a seleção dos elementos
        ListBox3.Items(1).Selected=true
        ListBox3.Items(3).Selected=true
        CheckBoxList3.Items(0).Selected=true
        CheckBoxList3.Items(3).Selected=true
        DropDownList3.SelectedIndex=2
        RadioButtonList3.SelectedIndex=1
end sub

Cada componente [ListControl] está associado à fonte [myDataTable], atribuindo a cada um deles:

            .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 dos componentes, enquanto a coluna [texte] irá preencher os seus campos [Text].

8.3.5. Fonte de dados do tipo DataSet

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

' os dados globais
dim myDataTable as new DataTable("table1")
dim myDataSet as new DataSet
...


' procedimento executado ao carregar a página
Sub page_Load(sender As Object, e As EventArgs)
    if not IsPostBack then
      ' criam-se as fontes  de dados que serão associadas aos componentes
      createDataSources
...
    end if
End Sub

sub createDataSources
   ' criam-se as fontes de dados que serão associadas aos componentes
   ' conjunto de dados - uma única tabela
  myDataSet.Tables.Add(myDataTable)
...
end sub

Um objeto [DataSet] representa um conjunto de tabelas do tipo [DataTable]. Adiciona-se a tabela [myDataTable], criada anteriormente, à [DataSet]. A ligação dos quatro controlos [ListBox] da ligação 4 é efetuada da seguinte forma no procedimento [bindToDataSet] do código de controlo:

sub bindToDataSet
         ' a associação aos componentes
        with DropDownList4
            .DataSource=myDataSet
            .DataMember="table1"
            .DataValueField="id"
            .DataTextField="texte"
            .DataBind
        end with
        with ListBox4
            .DataSource=myDataSet
            .DataMember="table1"
            .DataValueField="id"
            .DataTextField="texte"
            .DataBind
        end with
        with CheckBoxList4
            .DataSource=myDataSet
            .DataMember="table1"
            .DataValueField="id"
            .DataTextField="texte"
            .DataBind
        end with
        with RadioButtonList4
            .DataSource=myDataSet
            .DataMember="table1"
            .DataValueField="id"
            .DataTextField="texte"
            .DataBind
        end with
        ' a seleção dos elementos
        ListBox4.Items(1).Selected=true
        ListBox4.Items(3).Selected=true
        CheckBoxList4.Items(0).Selected=true
        CheckBoxList4.Items(3).Selected=true
        DropDownList4.SelectedIndex=2
        RadioButtonList4.SelectedIndex=1
end sub

Cada componente [ListControl] está ligado à fonte de dados da seguinte forma:

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

O grupo de dados [myDataSet] é a fonte de dados. Como esta pode incluir várias tabelas, especifica-se em [DataMember] o nome da tabela que se vai utilizar. A coluna [id] desta tabela irá preencher os campos [Value] dos elementos dos componentes, enquanto a coluna [texte] irá preencher os seus campos [Text].

8.3.6. Fonte de dados do tipo HashTable

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

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

' procedimento executado ao carregar a página
Sub page_Load(sender As Object, e As EventArgs)
    if not IsPostBack then
      ' criam-se as fontes  de dados que serão associadas aos componentes
      createDataSources
...
    end if
End Sub

sub createDataSources
   ' criam-se as fontes de dados que serão associadas aos componentes
...
  ' tabela hash
  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 denominadas «key» e «value». A coluna [key] representa as chaves do dicionário e a coluna [value] os valores associados a essas chaves. A coluna [key] é aqui constituída pelo conteúdo da tabela [valeurs] e a coluna [value] pelo conteúdo da tabela [textes]. A ligação desta fonte aos controlos é efetuada no procedimento [bindToHashTable]:

sub bindToHashTable
         ' a associação aos componentes
        with DropDownList5
            .DataSource=myHashTable
            .DataValueField="key"
            .DataTextField="value"
            .DataBind
        end with
        with ListBox5
            .DataSource=myHashTable
            .DataValueField="key"
            .DataTextField="value"
            .DataBind
        end with
        with CheckBoxList5
            .DataSource=myHashTable
            .DataValueField="key"
            .DataTextField="value"
            .DataBind
        end with
        with RadioButtonList5
            .DataSource=myHashTable
            .DataValueField="key"
            .DataTextField="value"
            .DataBind
        end with
        ' a seleção dos elementos
        ListBox5.Items(1).Selected=true
        ListBox5.Items(3).Selected=true
        CheckBoxList5.Items(0).Selected=true
        CheckBoxList5.Items(3).Selected=true
        DropDownList5.SelectedIndex=2
        RadioButtonList5.SelectedIndex=1
end sub

Para cada um dos componentes, a ligação é feita através das 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 os textos pela coluna [value]. A inserção dos elementos do dicionário nos controlos é feita pela ordem das chaves, que é, a princípio, aleatória.

8.3.7. As diretivas de importação de espaços de nomes

Vários espaços de nomes são importados automaticamente para uma página ASP.NET. Não é o caso de «System.Data», onde se encontram as classes [DataTable] e [DataSet]. Por isso, é necessário importar esta classe. Isso faz-se 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 apresentar dados sob a forma de tabelas, mas vai muito além desta simples apresentação:

  • oferece a possibilidade de configurar com precisão a «apresentação visual» da tabela
  • permite a atualização da fonte de dados

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

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

O componente [DataGrid] permite apresentar numa tabela HTML fontes de dados dos tipos [Array], [ArrayList], [DataTable] e [DataSet]. 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
no caso de a fonte de dados ser um [DataSet], representa o nome da tabela a utilizar como fonte de dados. A verdadeira fonte de dados é, então, uma tabela. Se este campo não for preenchido, são apresentadas todas as tabelas do [DataSet].

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

Image

A página contém quatro componentes [DataGrid] criados com o [WebMatrix] da seguinte forma. O componente é colocado no seu local na guia [Design]:

Image

É então desenhada uma tabela HTML genérica. As propriedades de um [DataGrid] podem ser definidas na fase de conceção. É isso que fazemos 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 [Générateur de propriétés] dá acesso às principais propriedades do [DataGrid]:

Image

Desmarcamos a opção [Afficher l'en-tête] para os quatro componentes [DataGrid] e validamos a página. O outro link, [Mise en forme automatique], permite escolher entre vários estilos para a tabela HTML que será apresentada:

Image

Escolhemos [couleur i] para o [DataGrid] n.º i. Estas escolhas de design são traduzidas 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">

     ' dados globais
        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

        ' procedimento executado ao carregar a página
        Sub page_Load(sender As Object, e As EventArgs)
            if not IsPostBack then
              ' criam-se as fontes  dos dados que serão associados aos componentes
              createDataSources
               ' ligação a uma tabela [Array]
              bindToArray
               ' ligação a uma lista [ArrayList]
              bindToArrayList
               ' ligação a uma tabela [DataTable]
              bindToDataTable
               ' ligação a um grupo de dados [DataSet]
              bindToDataSet
            end if
        End Sub

        sub createDataSources
           ' cria as fontes de dados que serão associadas aos componentes
          ' arraylist
          dim i as integer
          for i=0 to textes1.length-1
            myDataListe.add(textes1(i))
          next
           ' tabela de dados
           ' definem-se as suas duas colunas
          myDataTable.Columns.Add("id",Type.GetType("System.Int32"))
          myDataTable.Columns.Add("texte1",Type.GetType("System.String"))
          myDataTable.Columns.Add("texte2",Type.GetType("System.String"))
          ' preenche-se a tabela
          dim ligne as DataRow
          for i=0 to textes1.length-1
            ligne=myDataTable.NewRow
            ligne("id")=valeurs(i)
            ligne("texte1")=textes1(i)
            ligne("texte2")=textes2(i)
            myDataTable.Rows.Add(ligne)
          next
           ' conjunto de dados - uma única tabela
          myDataSet.Tables.Add(myDataTable)
        end sub

        ' ligação de tabela
        sub bindToArray
          with DataGrid1
            .DataSource=textes1
            .DataBind
          end with
        end sub

        ' ligação de uma lista de matrizes
        sub bindToArrayList
          with DataGrid2
            .DataSource=myDataListe
            .DataBind
          end with
        end sub

        ' ligação de tabela de dados
        sub bindToDataTable
          with DataGrid3
            .DataSource=myDataTable
            .DataBind
          end with
        end sub

        ' ligação de conjunto de dados
        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 iremos comentar em pormenor. Note-se, no entanto, como é feita a ligação dos dados. Para cada um dos quatro controlos, basta a seguinte sequência:

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

onde [source de données] é do tipo [Array] ou [ArrayList] ou [DataTable] ou [DataSet]. Vemos, portanto, que temos aqui uma ferramenta poderosa para a apresentação de dados em tabelas:

  • a formatação é feita com o [WebMatrix] ou qualquer outro IDE na fase de conceção
  • a ligação aos dados é feita no código. Com o [WebMatrix], esta ligação pode ser feita na fase de conceção se a fonte de dados for uma base de dados SQL server ou uma base de dados ACCESS. Neste caso, deve-se colocar um componente do tipo [SqlDataSource] ou [AccessDataSource] na folha. Este componente pode ser ligado, na fase de conceção, à fonte física de dados, uma base de dados SQL Server ou uma base de dados ACCESS, consoante o caso. Se for atribuído à propriedade [DataSource] de um componente [DataGrid] um objeto [SqlDataSource] ou [AccessDataSource] ligado a uma fonte física, então, no modo de conceção, os dados reais no componente [DataGrid].

8.5. ViewState dos componentes de listas de dados

Propomos aqui destacar o mecanismo do [VIEWSATE] para os componentes de listas de dados. Podemos 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 verdadeiro
  • definir o seu atributo [VIEWSTATE] como falso e memorizar a sua fonte de dados na sessão, de modo a poder ligar o componente a essa fonte na próxima solicitação.

O exemplo seguinte ilustra alguns aspetos do mecanismo [VIEWSTATE] dos 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 S que [DataGrid1]
3
Button1
Botão
EnableViewState=false
botão [submit]

O componente [DataGrid1] é mantido pelo mecanismo do [VIEWSTATE]. Pretende-se determinar se este mecanismo, que regenera a visualização do [DataGrid1] a cada pedido, também regenera a sua fonte de dados. Para tal, esta está ligada ao componente [DataGrid2]. A geração deste componente a cada consulta é efetuada através de uma ligação explícita à fonte de dados do [DataGrid1]. Este possui também o seu atributo [EnableViewState] no [vrai].

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
        ' na primeira consulta, a fonte de dados é definida e associada ao primeiro datagrid
        If Not IsPostBack Then
            'definir a fonte de dados
            With DataGrid1
                .DataSource = createDataSource()
                .DataBind()
            End With
        End If
        ' em cada consulta, vincula-se a grelha de dados 2 à fonte da primeira grelha de dados
        With Datagrid2
            .DataSource = DataGrid1.DataSource
            .DataBind()
        End With
    End Sub

    Private Function createDataSource() As DataTable
        ' inicializa-se a fonte de dados
        Dim thèmes As New DataTable
        ' colunas
        With thèmes.Columns
            .Add("id", GetType(System.Int32))
            .Add("thème", GetType(System.String))
            .Add("description", GetType(System.String))
        End With
        ' a coluna «id» será a chave primária
        thèmes.Constraints.Add("cléprimaire", thèmes.Columns("id"), True)
        ' linhas
        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 S do tipo [DataTable]. Não nos deteremos no seu código, uma vez que não é o objeto deste exemplo. O que nos interessa é a forma como são construídos os dois componentes [DataGrid]:

  • o componente [DataGrid1] é associado uma vez à tabela S, aquando da primeira consulta. Posteriormente, deixa de o estar.
  • o componente [DataGrid2] é ligado à fonte [DataGrid1.DataSource] em cada nova consulta.

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

Image

Como seria de esperar, ambos os componentes apresentam a fonte de dados à qual foram associados. Utilizamos o botão [Envoyer] para enviar um [PostBack] para o servidor. A vista obtida é então a seguinte:

Image

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

  • antes mesmo de o procedimento [Page_Load] ser iniciado, os objetos [DataGrid1] e [DataGrid2] recuperaram o valor que tinham na consulta anterior, devido ao mecanismo do [viewstate]. De facto, ambos têm a sua propriedade definida de [EnableViewState] para [vrai].
  • O procedimento [Page_Load] é executado. Como se trata de uma operação [PostBack], o componente [DataGrid1] não é alterado pelo [Page_Load] (ver código). Por isso, mantém o valor recuperado através do [viewstate]. É isso que mostra o ecrã acima.
  • O componente [DataGrid2], por sua vez, está ligado ao [DataBind] e à fonte de dados [DataGrid1.DataSource]. É, portanto, reconstruído e o valor que acabara de recuperar através do [viewstate] é perdido. Seria, portanto, vantajoso que tivesse a sua propriedade ligada de [EnableViewState] a [faux], a fim de evitar a gestão desnecessária do seu estado. O ecrã acima mostra que o [DataGrid2] foi associado a uma fonte vazia. Sendo essa fonte o [DataGrid1.DataSource], deduz-se que, embora o mecanismo do [viewstate] restaure corretamente a visualização do componente [DataGrid1], não restaura, no entanto, as suas propriedades, tais como [DataSource].

Que conclusão se pode tirar deste exemplo? Deve evitar-se definir a propriedade [EnableViewState] de um contentor de dados como [vrai], caso este tenha de ser ligado (DataBind) a uma fonte de dados em cada consulta. No entanto, existem alguns casos em que, mesmo nesta situação, a propriedade [EnableViewState] do contentor deve ser mantida em [vrai]; caso contrário, os eventos que se pretende gerir não são acionados. Teremos oportunidade de ver um exemplo disso.

Frequentemente, a fonte de dados de um contentor de dados evolui ao longo das consultas. Por isso, em cada consulta, é necessário ligar o contentor à fonte de dados. É frequente que esta seja colocada na sessão para que as consultas tenham acesso à mesma. O nosso segundo exemplo ilustra 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 S que [DataGrid1]
3
Button1
Botão
EnableViewState=false
botão [submit]
4
lblInfo1
lblInfo2
lblInfo3
Etiqueta
EnableViewState=false
textos informativos

O componente [DataGrid1] é associado aos dados apenas na primeira consulta. Manterá o seu valor ao longo das consultas graças ao mecanismo do [viewstate]. O componente [DataGrid2] está ligado, em cada consulta, a uma fonte de dados à qual é adicionado um elemento em cada nova consulta. Por isso, é necessário ligar (DataBind) o componente [DataGrid2] a cada consulta. Assim, definimos o seu atributo [EnableViewState] para [faux], tal como recomendado anteriormente. Assim, na segunda consulta (utilizando o botão [Envoyer]), obtemos a seguinte resposta:

Image

O componente [DataGrid1] manteve o seu valor inicial. O componente [DataGrid2] tem mais um elemento. As três informações [1,2,2] representam o número da consulta. Verifica-se que uma das informações está errada. Vamos tentar perceber porquê.

O código de apresentação [main.aspx] da aplicação é 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
        ' na primeira consulta, a fonte de dados é definida e associada à primeira grelha de dados
        If Not IsPostBack Then
            'definir a fonte de dados
            dtThèmes = createDataSource()
            With DataGrid1
                .DataSource = dtThèmes
                .DataBind()
            End With
            ' armazenar informações na sessão
            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
        ' em cada consulta, é adicionado um novo tema
        dtThèmes = CType(Session("source"), DataTable)
        Dim nbThèmes = dtThèmes.Rows.Count
        Dim ligne As DataRow = dtThèmes.NewRow
        With ligne
            .Item("id") = nbThèmes.ToString
            .Item("thème") = "thème" + nbThèmes.ToString
            .Item("description") = "description du thème " + nbThèmes.ToString
        End With
        dtThèmes.Rows.Add(ligne)
        'vincula a grelha de dados 2 à fonte de dados
        With Datagrid2
            .DataSource = dtThèmes
            .DataBind()
        End With
        ' informações sobre o número de consultas
        numRequête1 = CType(Session("numRequête1"), Integer)
        numRequête1 += 1
        lblInfo1.Text = numRequête1.ToString
        numRequête2 = CType(Session("numRequête2"), Integer)
        numRequête2 += 1
        lblInfo2.Text = numRequête2.ToString
        numRequête3 = CType(Session("numRequête3"), entier)
        numRequête3.valeur += 1
        lblInfo3.Text = numRequête3.valeur.ToString
        ' armazenam-se algumas informações na sessão
        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 que na aplicação anterior, com a única diferença de que, neste caso, apenas incluímos três linhas no código-fonte. Vamos, em primeiro lugar, analisar a gestão da fonte de dados e dos dois contentores:


    Private Sub Page_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
        ' na primeira consulta, a fonte de dados é definida e associada ao primeiro datagrid
        If Not IsPostBack Then
            'definir a fonte de dados
            dtThèmes = createDataSource()
            With DataGrid1
                .DataSource = dtThèmes
                .DataBind()
            End With
            ' armazenar informações na sessão
            Session("source") = dtThèmes
...
        End If
        ' a cada consulta, é adicionado um novo tema
        dtThèmes = CType(Session("source"), DataTable)
        Dim nbThèmes = dtThèmes.Rows.Count
        Dim ligne As DataRow = dtThèmes.NewRow
        With ligne
            .Item("id") = nbThèmes.ToString
            .Item("thème") = "thème" + nbThèmes.ToString
            .Item("description") = "description du thème " + nbThèmes.ToString
        End With
        dtThèmes.Rows.Add(ligne)
        'vincula o datagrid2 à fonte de dados
        With Datagrid2
            .DataSource = dtThèmes
            .DataBind()
        End With
...
    End Sub

O componente [DataGrid1] só é associado à fonte de dados S na primeira consulta (não ao IsPostBack). Esta é então colocada na sessão. Posteriormente, já não o será. A fonte de dados S colocada na sessão é recuperada em cada consulta e tem uma nova linha adicionada. O componente [DataGrid2] é associado explicitamente, em cada consulta, à fonte S. É por isso que o seu conteúdo aumenta em uma linha a cada consulta. Verifica-se que, após alterar o conteúdo da fonte S, esta não é explicitamente colocada novamente na sessão através de uma operação:

            Session("source") = S

Porquê? Quando uma solicitação é iniciada, a sessão contém um objeto Session("source") do tipo [DataTable], que corresponde à fonte de dados tal como estava na última solicitação. Chamemos S ao objeto Session("source"). 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 de [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 presentes na sessão serão guardados e, portanto, o objeto Session("source"), c.a.d. S, c.a.d e [dtThèmes]. É, portanto, o novo conteúdo da fonte de dados que é guardado. Não foi necessário escrever:

            Session("source") = dtThèmes

para efetuar esta gravação, uma vez que Session("source") já é igual a [dtThèmes]. Isto deixa de ser verdade quando os dados colocados na sessão não são objetos, como é o caso das estruturas [Integer, Float, ...]. É isso que mostra a gestão dos contadores de pedidos:


    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
        ' na primeira consulta, a fonte de dados é definida e associada ao primeiro datagrid
....
            ' armazenar informações na sessão
            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
        ' em cada consulta, adiciona-se um novo tema
....
        ' informações sobre o número de consultas
        numRequête1 = CType(Session("numRequête1"), Integer)
        numRequête1 += 1
        lblInfo1.Text = numRequête1.ToString
        numRequête2 = CType(Session("numRequête2"), Integer)
        numRequête2 += 1
        lblInfo2.Text = numRequête2.ToString
        numRequête3 = CType(Session("numRequête3"), entier)
        numRequête3.valeur += 1
        lblInfo3.Text = numRequête3.valeur.ToString
        ' armazenam-se algumas informações na sessão
        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

Os contadores de pedidos são armazenados em três elementos:

  • numRequête1 e numRequête2 do tipo [Integer] — [Integer] não é uma classe, mas sim uma estrutura
  • numRequête3 do tipo [entier] — [entier] é uma classe definida para o efeito

Quando se escreve:


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

Disto, deduz-se que é desnecessário escrever:

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

para guardar o novo valor de [numRequête3] na sessão. Em contrapartida, seria necessário escrever:

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

para guardar os novos valores das estruturas [numRequête1] e [numRequête2]. Fazemo-lo apenas para [numRequête2], o que explica que, na captura de ecrã obtida após a segunda consulta, o contador [numRequête1] esteja errado.

Conclui-se, portanto, que, uma vez inseridos na sessão, os dados não precisam de ser reinseridos repetidamente se forem representados por um objeto. Nos outros casos, é necessário fazê-lo se tiverem sido alterados.

8.6. Exibição de uma lista de dados utilizando um DataGrid paginado e ordenado

O componente [DataGrid] permite apresentar o conteúdo de um [DataSet]. Até agora, sempre criámos os nossos [DataSet] «manualmente». Desta vez, utilizamos um [DataSet] proveniente de uma base de dados. Criamos a seguinte aplicação MVC:

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

8.6.1. As classes de negócio

A classe [produits] dá acesso à seguinte base de dados ACCESS:

Image

O código da classe [produits] é 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)
            ' armazena a cadeia de ligação
            Me.chaineConnexionOLEDB = chaineConnexionOLEDB
        End Sub

        Public Function getDataSet(ByVal commande As String) As DataSet
            ' cria-se um objeto DataAdapter para ler os dados da fonte OLEDB
            Dim adaptateur As New OleDbDataAdapter(commande, chaineConnexionOLEDB)
            ' cria-se uma imagem na memória do resultado da consulta
            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
            ' retorna-se o resultado
            Return contenu
        End Function
    End Class
End Namespace

A base de dados ACCESS será gerida através de um controlador OLEDB. Ao construtor da classe [produits], fornece-se a cadeia de ligação de identificação, o controlador OLEDB e a base de dados a gerir. A classe [produits] possui um método [getDataSet] que devolve um [DataSet] obtido através da execução deuma consulta SQL [select], cujo texto é passado como parâmetro. Durante a execução do método, ocorrem várias operações: criação da ligação à base de dados, execução do [select] e encerramento da ligação. Tudo isto pode gerar uma exceção, que é aqui tratada. Um programa de teste poderia ser o seguinte:

Option Explicit On 
Option Strict On

' espaços de nomes
Imports System
Imports System.Data
Imports Microsoft.VisualBasic

Namespace st.istia.univangers.fr

     ' página de teste
    Module testproduits
        Sub Main(ByVal arguments() As String)
             ' exibe o conteúdo de uma tabela de produtos
             ' a tabela encontra-se numa base de dados ACCESS, cujo pg recebe o nome do ficheiro
            Const syntaxe1 As String = "pg bdACCESS"

             ' verificação dos parâmetros do programa
            If arguments.Length <> 1 Then
                 ' mensagem de erro
                Console.Error.WriteLine(syntaxe1)
                 ' fim
                Environment.Exit(1)
            End If

             ' preparação da cadeia de ligação
            Dim chaineConnexion As String = "Provider=Microsoft.Jet.OLEDB.4.0; Ole DB Services=-4; Data Source=" + arguments(0)

             ' criação de um objeto de produtos
            Dim objProduits As produits = New produits(chaineConnexion)

             ' recuperação da tabela de produtos num conjunto de dados
            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

             ' exibição do seu conteúdo
            Dim lignes As DataRowCollection = contenu.Tables(0).Rows
            For i As Integer = 0 To lignes.Count - 1
                 ' linha i da tabela
                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

A compilação dos diferentes elementos desta aplicação é feita 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 vistas

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

n.º
nome
tipo
propriedades
função
1
txtSelect
TextBox
EnableViewState=true
campo de introdução da consulta SELECT
2
RequiredFieldValidator1
RequiredFieldValidator
EnableViewState=false
verifica se existe 1
3
txtPages
TextBox
EnableViewState=true
campo de introdução de dados - indica o número de linhas de dados a apresentar por página de resultados
4
RequiredFieldValidator2
RequiredFieldValidator
EnableViewState=false
verifica a presença de 3
5
RangeValidator1
RangeValidator
EnableViewState=false
verifica se (3) está no intervalo [1,30]
6
btnExécuter
Botão
EnableViewState=false
botão [submit]

Chamaremos a esta vista a vista [formulaire]. Permite ao utilizador executar uma consulta SELECT SQL na base de dados [produits.mdb]. A execução da consulta dá origem a uma nova vista:

n.º
nome
tipo
propriedades
função
1
lblSelect
Rótulo
EnableViewState=false
campo de informação
2
rdCroissant
rdDécroissant
RadioButton
EnableViewState=false
GroupName=rdTri
permite escolher um sentido de ordenação
3
DataGrid1
DataGrid
EnableViewState=true
AllowPaging=true
AllowSorting=true
tabela de apresentação do resultado da seleção
4
lnkRésultats
LinkButton
EnableViewState=false
botão-ligação [submit]

Chamaremos a esta vista a vista [résultats]. É esta vista que contém o [DataGrid], que irá apresentar o resultado do comando SQL select. O utilizador pode cometer um erro na sua consulta. Alguns erros serão-lhe assinalados na vista [erreurs] graças aos controlos de validação.

Image

Outros erros serão assinalados na vista [erreurs]:

Image

É apresentada a vista [erreurs]:

n.º
nome
tipo
propriedades
função
1
erreursHTML
variável
 
código HTML necessário para a exibição de erros
3
lnkForm2
LinkButton
EnableViewState=false
botão-ligação [submit]

As três vistas da aplicação são três contentores (painéis) diferentes na 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. Configuração do DataGrid

Vamos analisar a paginação do componente [DataGrid], uma opção 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"
permite a paginação
PageSize="4"
quatro linhas de dados por página
NextPageText="Suivant"
O texto do link para aceder à página seguinte da fonte de dados
PrevPageText="Précédent"
O texto do link para aceder à página anterior da fonte de dados

Estas informações podem ser introduzidas diretamente nos atributos da baliza <asp:datagrid>. Também é possível utilizar o [WebMatrix]. Na janela de propriedades do [DataGrid], siga a ligação [Générateur de propriétés]:

Image

É apresentado o seguinte assistente:

Image

Selecione a opção [Pagination]:

Image

Encontramos acima os valores dos atributos de paginação do [DataGrid] do código de apresentação.

Além disso, permitimos 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. Seja qual for o método utilizado, isso traduz-se na presença do atributo [AllowSorting=true] na baliza <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)
        ' criação de um objeto de produtos
        Dim objProduits As produits
        Try
            objProduits = New produits(ConfigurationSettings.AppSettings("OLEDBStringConnection"))
            ' insere-se o objeto na aplicação
            Application("objProduits") = objProduits
            ' sem erros
            Application("erreur") = False
        Catch ex As Exception
            'ocorreu um erro, que é registado na aplicação
            Application("erreur") = True
            Application("message") = ex.Message
        End Try
    End Sub
End Class

Quando a aplicação é iniciada (Application_Start), criamos um objeto [produits] e colocamo-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, esta é registada na aplicação. O procedimento [Application_Start] será executado apenas uma vez. Depois disso, o controlador [global.asax] deixará de intervir. Será então o controlador [main.aspx.vb] que assumirá essa função:


Imports System.Collections
Imports Microsoft.VisualBasic
Imports System.Data
Imports st.istia.univangers.fr
Imports System
Imports System.Xml

Public Class main
    Inherits System.Web.UI.Page

    ' componentes da página
    Protected WithEvents txtSelect As System.Web.UI.WebControls.TextBox
    Protected WithEvents RequiredFieldValidator1 As System.Web.UI.WebControls.RequiredFieldValidator
    Protected WithEvents txtPages As System.Web.UI.WebControls.TextBox
    Protected WithEvents RequiredFieldValidator2 As System.Web.UI.WebControls.RequiredFieldValidator
    Protected WithEvents RangeValidator1 As System.Web.UI.WebControls.RangeValidator
    Protected WithEvents btnExécuter As System.Web.UI.WebControls.Button
    Protected WithEvents vueFormulaire As System.Web.UI.WebControls.Panel
    Protected WithEvents lblSelect As System.Web.UI.WebControls.Label
    Protected WithEvents DataGrid1 As System.Web.UI.WebControls.DataGrid
    Protected WithEvents lnkRésultats As System.Web.UI.WebControls.LinkButton
    Protected WithEvents vueRésultats As System.Web.UI.WebControls.Panel
    Protected WithEvents lnkErreurs As System.Web.UI.WebControls.LinkButton
    Protected WithEvents vueErreurs As System.Web.UI.WebControls.Panel
    Protected WithEvents rdCroissant As System.Web.UI.WebControls.RadioButton
    Protected WithEvents rdDécroissant As System.Web.UI.WebControls.RadioButton
    ' dados da página
    Protected erreursHTML As String

    Private Sub Page_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
        ' verifica-se se a aplicação apresenta algum erro
        If CType(Application("erreur"), Boolean) Then
            ' a aplicação não foi inicializada corretamente
            Dim erreurs As New ArrayList
            erreurs.Add("Application momentanément indisponible (" + CType(Application("message"), String) + ")")
            afficheErreurs(erreurs, False)
            Exit Sub
        End If
        '1.ª consulta
        If Not IsPostBack Then
            ' exibe o formulário vazio
            afficheFormulaire()
        End If
    End Sub

    Private Sub afficheErreurs(ByVal erreurs As ArrayList, ByVal afficheLien As Boolean)
        ' exibe a vista de erros
        erreursHTML = ""
        For i As Integer = 0 To erreurs.Count - 1
            erreursHTML += "<li>" + erreurs(i).ToString + "</li>" + ControlChars.CrLf
        Next
        lnkErreurs.Visible = afficheLien
        ' é apresentada a vista [erreurs]
        vueErreurs.Visible = True
        vueFormulaire.Visible = False
        vueRésultats.Visible = False
    End Sub

    Private Sub afficheFormulaire()
        ' é apresentada a vista [formulaire]
        vueFormulaire.Visible = True
        vueErreurs.Visible = False
        vueRésultats.Visible = False
    End Sub

    Private Sub afficheRésultats(ByVal sqlTexte As String, ByVal données As DataView)
        ' inicializa os controlos
        lblSelect.Text = sqlTexte
        With DataGrid1
            .DataSource = données
            .PageSize = CType(txtPages.Text, Integer)
            .CurrentPageIndex = 0
            .DataBind()
        End With
        ' é apresentada a vista [résultats]
        vueRésultats.Visible = True
        vueFormulaire.Visible = False
        vueErreurs.Visible = False
    End Sub

    Private Sub btnExécuter_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnExécuter.Click
        ' página válida?
        If Not Page.IsValid Then
            afficheFormulaire()
            Exit Sub
        End If

        ' execução da consulta SELECT do cliente
        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
        ' tudo bem — exibindo os resultados
        afficheRésultats(txtSelect.Text.Trim, données)
        ' os dados são colocados na sessão
        Session("données") = données
    End Sub

    Private Sub retourFormulaire(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles lnkErreurs.Click, lnkRésultats.Click
        ' conjunto de vistas
        vueErreurs.Visible = False
        vueFormulaire.Visible = True
        vueRésultats.Visible = False
    End Sub

    Private Sub DataGrid1_PageIndexChanged(ByVal source As Object, ByVal e As System.Web.UI.WebControls.DataGridPageChangedEventArgs) Handles DataGrid1.PageIndexChanged
        ' mudança de página
        With DataGrid1
            .CurrentPageIndex = e.NewPageIndex
            .DataSource = CType(Session("données"), DataView)
            .DataBind()
        End With
    End Sub

    Private Sub DataGrid1_SortCommand(ByVal source As Object, ByVal e As System.Web.UI.WebControls.DataGridSortCommandEventArgs) Handles DataGrid1.SortCommand
        ' ordena-se a vista de dados
        Dim données As DataView = CType(Session("données"), DataView)
        données.Sort = e.SortExpression + " " + CType(IIf(rdCroissant.Checked, "asc", "desc"), String)
        ' exibe-se
        With DataGrid1
            .CurrentPageIndex = 0
            .DataSource = données
            .DataBind()
        End With
    End Sub
End Class

Ao carregar a página [Page_Load], verificamos primeiro se a aplicação conseguiu inicializar-se corretamente. Se não for esse o caso, é apresentada a vista [erreurs] sem um link de retorno para o formulário, uma vez que esse link se torna, então, desnecessário. Com efeito, apenas a vista [erreurs] pode ser apresentada se a aplicação não tiver conseguido inicializar-se corretamente. Caso contrário, é apresentada a vista [formulaire] se se tratar do primeiro pedido do cliente. Quanto ao resto, deixamos ao leitor a tarefa de compreender o código. Detemos-nos apenas em três procedimentos: o procedimento [btnExécuter_Click], que é executado quando o utilizador solicitou a execução da consulta SQL introduzida na vista [formulaire], o procedimento [DataGrid1_PageIndexChanged], que é executado quando outilizador utiliza os links [Suivant] e [Précédent] da [DataGrid] e o procedimento [DataGrid1_SortCommand], que é executado quando o utilizador clica no título de uma coluna para ordenar os dados de acordo com aordem da mesma. O sentido da ordem, crescente ou decrescente, é definido pelos dois botões de opção de ordenação.

No procedimento [btnExécuter_Click], começa-se, 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 diferentes controlos de validação da página já foram efetuadas. Para cada controlo de validação, foram definidos dois atributos:

IsValid
definido como verdadeiro se os dados verificados forem válidos, e como falso caso contrário
ErrorMessage
a mensagem de erro, caso os dados verificados se revelem inválidos

Para a própria página, foi definido um atributo [IsValid]. Este atributo só assume o valor «verdadeiro» se todos os controlos de validação tiverem o seu atributo [IsValid] definido como «verdadeiro». Caso contrário, deve ser apresentada a vista [formulaire]. Esta vista contém os controlos de validação que apresentarão o seu atributo [errorMessage]. Se a página for válida, utiliza-se o objeto do tipo [produits] criado por [Application_Start] para obter o [DataSet] correspondente à execução da consulta SQL select. Este é transformado 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 trabalhado simplesmente com o [DataSet] e escrito:


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

Um objeto [DataSet] é, na sua essência, um conjunto de tabelas ligadas por relações. Na nossa aplicação específica, o [DataSet] obtido a partir da classe [produits] contém apenas uma tabela, a que resulta da instrução [select]. Uma tabela pode ser ordenada, ao passo que um [DataSet] não pode; no entanto, o nosso objetivo é ordenar os dados obtidos. Para trabalhar com a tabela resultante do [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 dispõe de um método de ordenação. Para tal, é necessário dispor de uma vista sobre a tabela. Uma vista é um objeto do tipo [DataView]. É possível obter diferentes vistas sobre uma mesma tabela através de filtros. Uma tabela tem uma vista por predefinição, que é aquela em que não está definido nenhum filtro. Representa, portanto, a totalidade da tabela. Esta vista por predefinição é obtida através de [DataTable.DefaultView]. É possível ordenar uma vista utilizando a sua propriedade [sort], à qual voltaremos mais tarde.

Se a obtenção do [DataSet] da classe [produits] decorrer sem problemas, é apresentada a vista [résultats]; caso contrário, é apresentada a vista [erreurs]. A visualização da vista [résultats] é efetuada pela rotina [afficheRésultats], à qual são passados dois parâmetros:

  • o texto a inserir no rótulo [lblSelect]
  • o [DataView] a associar ao [DataGrid1]

Este exemplo mostra-nos a grande flexibilidade do componente [DataGrid]. Este consegue reconhecer a estrutura do [DataView] ao qual está associado e adaptar-se à mesma. 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 utiliza os links [Suivant] e [Précédent] do [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 de origem do evento — neste caso, um dos links [Suivant] ou [Précédent]
e
informações sobre o evento. O atributo e.NewPageIndex é o número da página a apresentar para responder ao pedido do cliente

O código completo do gestor de eventos é o seguinte:


    Private Sub DataGrid1_PageIndexChanged(ByVal source As Object, ByVal e As System.Web.UI.WebControls.DataGridPageChangedEventArgs) Handles DataGrid1.PageIndexChanged
        ' mudança de página
        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 apresentar ou que irá apresentar. Atribui-se a este atributo o valor [NewPageIndex] do parâmetro [e]. O [DataGrid] é, em seguida, associado ao [DataView], que tinha sido memorizado na sessão, através do procedimento [btnExécuter_Click].

Pode-se questionar se o [DataGrid] necessita do atributo [EnableViewState=true], uma vez que o seu conteúdo é calculado pelo código a cada nova execução da página. Seria de supor que não. No entanto, se o [DataGrid] tiver o atributo [EnableViewState=false], verifica-se que o evento [DataGrid1.PageIndexChanged] nunca é acionado. É por isso que se manteve o [EnableViewState=true]. Sabemos que isto faz com que o conteúdo do [DataGrid] seja colocado no campo oculto [__VIEWSTATE] da página. Isto pode causar uma sobrecarga significativa da página se o [DataGrid] for grande. Se isso for um problema, é possível gerir a paginação manualmente, sem utilizar a paginação automática do [DataGrid].

O procedimento [DataGrid1_SortCommand] é executado quando o utilizador clica no título de uma das colunas apresentadas pelo [DataGrid] para solicitar a ordenação dos dados de acordo com essa 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 de origem do evento — neste caso, um dos links [Suivant] ou [Précédent]
e
informações sobre o evento. O atributo [e.SortExpression] é o nome da coluna em que se clicou para ordenar

O código completo do gestor de eventos é o seguinte:


    Private Sub DataGrid1_SortCommand(ByVal source As Object, ByVal e As System.Web.UI.WebControls.DataGridSortCommandEventArgs) Handles DataGrid1.SortCommand
        ' ordenar a visualização de dados
        Dim données As DataView = CType(Session("données"), DataView)
        données.Sort = e.SortExpression + " " + CType(IIf(rdCroissant.Checked, "asc", "desc"), String)
        ' a exibir
        With DataGrid1
            .CurrentPageIndex = 0
            .DataSource = données
            .DataBind()
        End With
    End Sub

Recupera-se o [DataView] visualizado pelo [DataGrid] na sessão atual. Foi o procedimento [btnExécuter_Click] que o colocou aí. O componente [DataView] possui uma propriedade [Sort] à qual se atribui a expressão de ordenação. Esta segue a sintaxe [select ... order by expr1, expr2, ...], em que cada [expri] pode ser seguido pela palavra-chave [asc] para uma ordenação crescente ou [desc] para uma ordenação decrescente. A expressão [order by] aqui utilizada é [order by colonne asc/desc]. A propriedade [e.SortExpression] indica-nos o nome da coluna do [DataGrid] em que se clicou para efetuar a ordenação. A cadeia [asc/desc] é definida com base nos valores dos botões de opção do grupo [rdTri]. Uma vez definida a expressão de ordenação do [DataView], o [DataGrid] é associado a este. O [DataGrid] é posicionado na sua primeira página.

8.7. Componente DataList e ligação de dados

Passamos agora a analisar o componente [DataList]. Este oferece mais possibilidades de formatação do que o [DataGrid], mas é menos flexível. Assim, não consegue adaptar-se automaticamente à fonte de dados à qual está ligado. Se for necessário, essa adaptação terá de ser feita através de código. Se a estrutura da fonte de dados for conhecida de antemã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], criamos uma aplicação MVC análoga à anterior:

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

8.7.2. As classes de negócio

A classe [produits] é a mesma que a anterior.

8.7.3. As vistas

Quando o utilizador efetua a sua primeira consulta à aplicação, obtém a seguinte vista [résultats1]:

n.º
nome
tipo
propriedades
função
1
RadioButton1
RadioButton2
RadioButton
EnableViewState=false
permite escolher um dos dois estilos de [DataList]
2
btnChanger
Botão
EnableViewState=false
botão [submit]
3
DataList1
DataList
EnableViewState=true
campo de visualização da lista de dados

Se o utilizador escolher o estilo n.º 2, obtém a seguinte vista [résultats2]:

n.º
nome
tipo
propriedades
função
1
DataList2
DataList
EnableViewState=true
campo de visualização da lista de dados

A vista [erreurs] indica um problema de acesso à fonte de dados:

n.º
nome
tipo
propriedades
função
1
erreursHTML
variável
 
código HTML necessário para a exibição de 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("nome") %>
                            <br>
                            prix :
                            <%# databinder.eval(Container.DataItem,"preço","{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("nome") %>
                            , prix :
                            <%# databinder.eval(Container.DataItem,"preço","{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. Configuração dos componentes [DataList]

Vamos analisar os diferentes atributos de um componente [DataList]. São muitos e apresentamos aqui apenas uma pequena parte. É possível definir até sete modelos de visualização dentro de um [DataList]:

HeaderTemplate
modelo do cabeçalho do [DataList]
ItemTemplate
modelo das linhas que apresentam os elementos da lista de dados associada. Apenas este modelo é obrigatório.
AlternatingItemTemplate
Para diferenciar visualmente os elementos sucessivos apresentados, podem ser utilizados dois modelos: ItemTemplate para o elemento n e AlternatingItemTemplate para o elemento n+1
SelectedItemTemplate
modelo do elemento selecionado no [DataList]
SeparatorTemplate
modelo do separador entre dois elementos do [DataList]
EditItemTemplate
um [DataList] permite a alteração dos valores que apresenta. O [EditItemTemplate] é o modelo de um elemento do [DataList] para o qual se encontra no modo «edição»
FooterTemplate
modelo do rodapé do [DataList]

O componente [DataList1] foi criado a partir do [WebMatrix]. Na sua janela de propriedades, foi selecionado o link [Mise en forme automatique]:

1234567

No exemplo acima, o esquema [Couleur 5] irá gerar um [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 está definido nenhum modelo. Cabe-nos a nós fazê-lo. Definimos os seguintes modelos:

HeaderTemplate

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

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

Recorde-se que o modelo [ItemTemplate] é o responsável pela apresentação dos elementos da fonte de dados associada ao [DataList]. Esta fonte de dados é um conjunto de linhas de dados, cada uma contendo um ou mais valores. A linha atual da fonte de dados é representada pelo objeto [Container.DataItem]. Essa linha possui colunas. [Container.DataItem("col1")] é o valor da coluna «col1» da linha atual. Para incluir este valor no código de apresentação, escreve-se <%# Container.DataItem("col") %>. Por vezes, pretende-se apresentar um elemento da linha atual num formato especial. Neste caso, pretende-se apresentar a coluna «preço» da linha atual em euros. Para tal, utiliza-se a função [DataBinder.Eval], que aceita três parâmetros:

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

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

Teremos, portanto, um [DataList] que terá o seguinte aspeto:

Image

No exemplo acima, os dados foram dispostos à razão de quatro dados por linha. Isto é obtido com os seguintes atributos de [DataList]:

RepeatDirection
Horizontal
RepeatColumns
número de colunas pretendido

No final, o código de [DataList1] é aquele que foi apresentado no código de apresentação um pouco mais acima. Deixamos ao leitor a tarefa de analisar o código de apresentação de [DataList2]. Tal como no caso do componente [DataGrid], a maioria das propriedades do [DataList] pode ser definida através de um assistente do [WebMatrix]. Para tal, utiliza-se a ligação [Générateur de propriétés] na janela de propriedades do [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)
        ' criamos um objeto «produtos»
        Try
            Dim données As DataSet = New produits(ConfigurationSettings.AppSettings("OLEDBStringConnection")).getDataSet("select * from LISTE")
            ' insere-se o objeto na aplicação
            Application("données") = données
            ' sem erros
            Application("erreur") = False
        Catch ex As Exception
            'ocorreu um erro, que é registado na aplicação
            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ócio [produits] e colocamo-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, esta é registada na aplicação. O procedimento [Application_Start] será executado apenas uma vez. Posteriormente, o controlador [global.asax] deixará de intervir. Será então o controlador [main.aspx.vb] que realizará o 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

    ' componentes da página
    Protected WithEvents vueErreurs As System.Web.UI.WebControls.Panel
    Protected WithEvents DataList1 As System.Web.UI.WebControls.DataList
    Protected WithEvents DataList2 As System.Web.UI.WebControls.DataList
    Protected WithEvents vueRésultats1 As System.Web.UI.WebControls.Panel
    Protected WithEvents vueRésultats2 As System.Web.UI.WebControls.Panel
    Protected WithEvents RadioButton1 As System.Web.UI.WebControls.RadioButton
    Protected WithEvents RadioButton2 As System.Web.UI.WebControls.RadioButton
    Protected WithEvents btnChanger As System.Web.UI.WebControls.Button
    Protected WithEvents bandeau As System.Web.UI.WebControls.Panel
    ' dados da página
    Protected erreursHTML As String

    Private Sub Page_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
        ' verifica-se se a aplicação apresenta algum erro
        If CType(Application("erreur"), Boolean) Then
            ' a aplicação não foi inicializada corretamente
            Dim erreurs As New ArrayList
            erreurs.Add("Application momentanément indisponible (" + CType(Application("message"), String) + ")")
            afficheErreurs(erreurs, False)
            Exit Sub
        End If
        '1.ª consulta
        If Not IsPostBack Then
            ' inicializam-se os controlos
            With DataList1
                .DataSource = CType(Application("données"), DataSet)
                .DataBind()
            End With
            With DataList2
                .DataSource = CType(Application("données"), DataSet)
                .DataBind()
            End With
            ' exibe-se o formulário vazio
            afficheRésultats(True, False)
        End If
    End Sub

    Private Sub afficheErreurs(ByVal erreurs As ArrayList, ByVal afficheLien As Boolean)
        ' exibe a vista de erros
        erreursHTML = ""
        For i As Integer = 0 To erreurs.Count - 1
            erreursHTML += "<li>" + erreurs(i).ToString + "</li>" + ControlChars.CrLf
        Next
        ' exibe a vista [erreurs]
        vueErreurs.Visible = True
        vueRésultats1.Visible = False
        vueRésultats2.Visible = False
        bandeau.Visible = False
    End Sub

    Private Sub afficheRésultats(ByVal visible1 As Boolean, ByVal visible2 As Boolean)
        ' exibe a vista [résultats]
        vueRésultats1.Visible = visible1
        vueRésultats2.Visible = visible2
        vueErreurs.Visible = False
    End Sub

    Private Sub btnChanger_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnChanger.Click
        ' altera-se o estilo
        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 de cada um dos elementos de uma lista de dados. Suponhamos que se pretenda apresentar uma lista de erros com o seguinte formato:

Image

Já nos deparámos com este problema e resolvemo-lo inserindo no código de apresentação uma variável com o formato <ul><% =erreursHTML %></ul>, sendo que o valor de erreursHTML é calculado pelo controlador. Este valor contém o código HTML, correspondente a uma lista. Isto apresenta a desvantagem de que, se quisermos alterar a apresentação desta lista HTML, somos obrigados a aceder à parte do controlador, o que contraria a separação entre controlador e apresentação. O componente [Repeater] oferece-nos uma solução. Tal como no caso do [DataList], é possível definir os modelos <HeaderTemplate> para o cabeçalho, <ItemTemplate> para o elemento atual da lista de dados e <FooterTemplate> para o fim dos dados. Neste caso, 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>

Recorde-se que [Container.DataItem] representa uma linha de dados se a fonte de dados tiver várias colunas. Representa um dado se a fonte tiver apenas uma coluna. Será esse o caso aqui. A título de exemplo, criamos a seguinte aplicação:

Image

O código de apresentação 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">

     ' procedimento executado ao carregar a página
    Sub page_Load(sender As Object, e As EventArgs)
        if not IsPostBack then
          ' é criada uma fonte de dados
          with Repeater1
              .DataSource=createDataSource
              .DataBind
          end with
        end if
    End Sub

    function createDataSource as ArrayList
      ' cria uma lista de matrizes
      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>

À primeira solicitação do cliente, associamos um objeto [ArrayList] ao componente [Repeater], que se supõe representar uma lista de erros.

8.9. Application

Retomamos aqui uma aplicação já tratada com componentes de servidor. A aplicação permite realizar simulações de cálculos de impostos. Baseia-se numa classe [impot], que não iremos referir aqui. Esta classe necessita de dados que encontra numa fonte de dados OLEDB. Para o exemplo, será utilizada uma fonte ACCESS. Nesta nova versão, introduzimos as seguintes novidades:

  • a utilização de componentes de validação para verificar a validade dos dados
  • a utilização de componentes de servidor ligados a fontes de dados para a apresentação dos 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] sob a forma de contentores. Por isso, esta aplicação tem uma única página, [main.aspx].

8.9.2. As vistas da aplicação

A vista [formulaire] é o formulário de introdução de informações que permite o cálculo do imposto de um utilizador:

Image

O utilizador preenche o formulário:

Image

Utiliza o botão [Envoyer] para solicitar o cálculo do seu imposto. Obtém a seguinte vista [simulations]:

Image

Volta ao formulário através do link acima. Encontra-o tal como o tinha preenchido. Pode cometer erros de introdução de dados:

Image

Estes erros são-lhe assinalados na vista [formulaire]:

Image

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

Image

Obtém então a vista [simulations] com mais uma simulação:

Image

Por fim, se a fonte de dados não estiver disponível, isso é indicado ao utilizador na vista [erreurs]:

Image

8.9.2.1. O código de apresentação

Recorde-se 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 [formulaire]
  • [panelerreurs] para a vista [erreurs]
  • [panelsimulations] para a vista [simulations]

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

n.º
nome
tipo
propriedades
função
0
panelform
Painel
 
vista do formulário
1
rdOui
rdNon
RadioButton
GroupName=rdmarie
botões de opção
2
cvMarie
CustomValidator
ErrorMessage=Não indicou o seu estado civil
EnableClientScript=false
verifica se o programa cliente enviou efetivamente o estado civil esperado
3
txtEnfants
TextBox
 
número de filhos
4
rfvEnfants
RequiredFieldValidator
ErrorMessage=Indique o número de filhos
ControlToValidate=txtEnfants
verifica se o campo [txtEnfants] não está vazio
5
rvEnfants
RangeValidator
ErrorMessage=Introduza um número entre 1 e 30
ControlToValidate=txtEnfants
verifica se o campo [txtEnfants] está dentro do intervalo [1,30]
6
txtSalaire
TextBox
EnableViewState=true
salário anual
7
rfvSalaire
RequiredFieldValidator
ControlToValidate=txtSalário
ErrorMessage=Indique o montante do seu salário
verifica se o campo [txtSalaire] não está vazio
8
revSalaire
RegularExpressionValidator
ControlToValidate=txtSalário
ErrorMessage=Salário inválido
Expressão regular=\s*\d+\s*
verifica se o campo [txtSalaire] é uma sequência de algarismos
9
btnCalculer
Botão
CausesValidation=true
botão [submit] do formulário — inicia o cálculo do imposto
10
btnEffacer
Botão
CausesValidation=false
Botão [submit] do formulário - limpa o formulário

Pode parecer surpreendente que o controlo [cvMarié] seja responsável por verificar se o utilizador marcou efetivamente um dos dois botões de opção. Na verdade, não pode ser de outra forma se estiver a utilizar corretamente o formulário enviado pelo servidor. Como nada pode garantir isso, somos obrigados a verificar todos os parâmetros enviados. Deve-se notar também o atributo [CausesValidation=false] do botão [btnEffacer]. Com efeito, quando o utilizador utiliza este botão, não se devem verificar os dados enviados, uma vez que estes serão ignorados.

Todos os componentes do 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
panelerreurs
Painel
EnableViewState=false
visualização de erros
1
rptErreurs
Repetidor
EnableViewState=false
exibe uma lista de erros

O componente [rptErreurs] está 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 [rptErreurs] será um objeto [ArrayList] que contém uma lista de mensagens de erro. Assim, no exemplo acima, <%# Container.Dataitem%> designa a mensagem de erro atual. O contentor [panelsimulations] tem a seguinte representação visual:

n.º
nome
tipo
propriedades
função
0
panelsimulations
Painel
EnableViewState=false
vista de simulações
2
lnkForm2
LinkButton
EnableViewState=false
ligação para o formulário
1
dgSimulations
DataGrid
EnableViewState=false
responsável por apresentar as simulações colocadas num objeto [DataTable]

O componente [dgSimulations] está configurado da seguinte forma em [Webmatrix]. Na janela de propriedades do [dgSimulations], selecionamos a ligação [Mise en forme automatique] e selecionamos o esquema [Couleur 3]:

Em seguida, ainda na janela de propriedades de [dlgSimulations], selecionamos o link [Générateur de propriétés]. Solicitamos a exibição dos cabeçalhos das colunas:

Image

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

Image

Desmarcamos a frase [Créer des colonnes automatiquement ...]. Vamos definir nós próprios as quatro colunas do [DataGrid]. Este será associado a um objeto [DataTable] com quatro colunas denominadas, respetivamente, «casado», «filhos», «salário» e «imposto». Para criar uma coluna no [DataGrid], selecionamos a opção [Colonnes connexe] e utilizamos o botão de criação, conforme indicado acima. É criada uma coluna e podemos definir as suas características. A primeira coluna da tabela de simulações conterá a coluna «casado» do objeto [DataTable], que será associado ao [DataGrid]. Esta primeira coluna do [DataGrid] é definida da seguinte forma no assistente:

Image

Texte de l'en-tête
título da coluna, neste caso «Casado»
Champ de données
nome da coluna da fonte de dados que será visualizada por esta coluna do [DataGrid]. Neste caso, trata-se da coluna «casado» do [DataTable].

A segunda coluna é definida da seguinte forma:

Texte de l'en-tête
Filhos
Champ de données
filhos

A terceira coluna é definida da seguinte forma:

Texte de l'en-tête
Salário anual
Champ de données
salário
Expression de mise en forme
{0:C} - formatação monetária para obter o símbolo do euro

A quarta coluna é definida da seguinte forma:

Texte de l'en-tête
Montante do imposto
Champ de données
imposto
Expression de mise en forme
{0:C} - formatação monetária 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 de [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 encontra-se distribuído pelos ficheiros [global.asax.vb] e [main.aspx.vb]. O ficheiro [global.asax] está 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)
        ' cria-se um objeto «impot»
        Dim objImpot As impot
        Try
            objImpot = New impot(New impotsOLEDB(ConfigurationSettings.AppSettings("chaineConnexion")))
            ' coloca-se o objeto na aplicação
            Application("objImpot") = objImpot
            ' sem erros
            Application("erreur") = False
        Catch ex As Exception
            'ocorreu um erro, que é registado na aplicação
            Application("erreur") = True
            Application("message") = ex.Message
        End Try
    End Sub

    Sub Session_Start(ByVal sender As Object, ByVal e As EventArgs)
        ' início da sessão — cria-se uma lista de simulações vazia
        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 (1.ª solicitação feita à aplicação), é executado o procedimento [Application_Start]. Este procura criar um objeto do tipo [impot], obtendo os seus dados de uma fonte OLEDB. Recomenda-se ao leitor que consulte o capítulo 5, onde esta classe foi definida, caso se tenha esquecido dela. A criação do objeto [impot] pode falhar se a fonte de dados não estiver disponível. Nesse caso, o erro é armazenado na aplicação para que todas as consultas posteriores saibam que esta não conseguiu ser inicializada corretamente. Se a criação decorrer sem problemas, o objeto [impot] criado também é armazenado na aplicação. Será utilizado por todas as consultas de cálculo de impostos. Quando um cliente efetua a sua primeira consulta, é criada uma sessão para ele pelo procedimento [Application_Start]. Esta sessão destina-se a armazenar as diferentes simulações de cálculo de impostos que ele irá realizar. Estas serão armazenadas num objeto [DataTable] associado à chave de sessão «simulações». Quando a sessão é iniciada, esta chave é associada a um objeto [DataTable] vazio, mas cuja estrutura já foi definida. As informações necessárias à aplicação são colocadas 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 [chaineConnexion] identifica a cadeia de ligação à fonte OLEDB. A outra parte 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

    ' variáveis locais

    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 processado pelo código é o [Page_Load]:


    Private Sub Page_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
        ' em primeiro lugar, verifica-se o estado da aplicação
        If CType(Application("erreur"), Boolean) Then
            ' a aplicação não conseguiu inicializar-se
            ' exibe-se a vista de erros
            Dim erreurs As New ArrayList
            erreurs.Add("Application momentanément indisponible (" + CType(Application("message"), String) + ")")
            afficheErreurs(erreurs)
            Exit Sub
        End If
        ' sem erros — na primeira solicitação, apresentamos o formulário
        If Not IsPostBack Then afficheFormulaire()
    End Sub

Antes de começarmos a processar a solicitação, verificamos se a aplicação conseguiu inicializar-se corretamente. Caso contrário, exibimos a vista [erreurs] com o procedimento [afficheErreurs]. Se for a primeira solicitação (IsPostBack=false), exibimos a vista [formulaire] com [afficheFormulaire].

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


    Private Sub afficheErreurs(ByRef erreurs As ArrayList)
        ' cria a lista de erros
        With rptErreurs
            .DataSource = erreurs
            .DataBind()
        End With
        ' exibição dos contentores 
        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 [erreurs] do tipo [ArrayList]. Limitamo-nos a associar esta fonte de dados ao componente [rptErreurs] responsável pela sua visualização.

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


    Private Sub afficheFormulaire()
        ' exibe o formulário
        panelform.Visible = True
        ' os restantes contentores estão ocultos
        panelerreurs.Visible = False
        panelsimulations.Visible = False
    End Sub

Este procedimento limita-se a tornar visível o contentor [panelform]. Os componentes são apresentados com o seu valor lançado ou anterior (VIEWSTATE).

Quando o utilizador clica no botão [Calculer] da vista [formulaire], é efetuada uma transição de POST para [main.aspx]. O procedimento [Page_Load] é executado e, em seguida, o procedimento [btnCalculer_Click]:


    Private Sub btnCalculer_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnCalculer.Click
        ' verifica-se a validade dos dados introduzidos; se houver erros, estes são assinalados
        If Not Page.IsValid Then
            afficheFormulaire()
            Exit Sub
        End If
        ' sem erros — calcula-se o imposto
        Dim impot As Long = CType(Application("objImpot"), impot).calculer( _
        rdOui.Checked, CType(txtEnfants.Text, Integer), CType(txtSalaire.Text, Long))
        ' o resultado é adicionado às simulações existentes
        Dim simulations As DataTable = CType(Session.Item("simulations"), DataTable)
        Dim simulation As DataRow = simulations.NewRow
        simulation("marié") = CType(IIf(rdOui.Checked, "oui", "non"), String)
        simulation("enfants") = txtEnfants.Text.Trim
        simulation("salaire") = CType(txtSalaire.Text.Trim, Long)
        simulation("impôt") = impot
        simulations.Rows.Add(simulation)
        ' as simulações são colocadas na sessão
        Session.Item("simulations") = simulations
        ' exibe-se a página das simulações
        afficheSimulations(simulations, "Retour au formulaire")
    End Sub

O procedimento começa por verificar a validade da página. Recorde-se aqui que, quando o procedimento [btnCalculer_Click] é executado, as verificações de validação já foram realizadas e o atributo [IsValid] da página foi definido. Se a página não for válida, a vista [formulaire] é exibida novamente e o procedimento é encerrado. Os componentes de validação da vista [formulaire] cujo atributo [IsValid] esteja definido entre [false] e [ErrorMessage] exibirão o seu atributo [ErrorMessage]. Se a página for válida, o montante do imposto é calculado através do objeto do tipo [impot] que tinha sido armazenado na aplicação no momento do seu arranque. Esta nova simulação é adicionada à lista de simulações já realizadas e armazenada na sessão.

Por fim, a vista [simulations] é apresentada pelo seguinte procedimento [afficheSimulations]:


    Private Sub afficheSimulations(ByRef simulations As DataTable, ByRef lien As String)
        ' liga-se a grelha de dados à fonte «simulações»
        With dgSimulations
            .DataSource = simulations
            .DataBind()
        End With
        ' ligação
        lnkForm2.Text = lien
        ' as vistas
        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 [lien]

A fonte de dados [simulations] está associada ao componente responsável pela sua visualização. O texto do link, por sua vez, está definido na propriedade [Text] do objeto [LinkButton] da vista.

Quando o utilizador clica no botão [Effacer] da vista [formulaire], é executado o procedimento [btnEffacer_click] (sempre após o [Page_Load]):


    Private Sub btnEffacer_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnEffacer.Click
        ' exibe o formulário vazio
        razForm()
        afficheFormulaire()
    End Sub

    Private Sub razForm()
        ' esvazia o formulário
        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. Resta-nos tratar do clique nos links das vistas [erreurs] e [simulations]:


    Private Sub lnkForm1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles lnkForm1.Click
        ' exibe o formulário
        afficheFormulaire()
    End Sub

    Private Sub lnkForm2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles lnkForm2.Click
        ' exibe o formulário
        afficheFormulaire()
    End Sub

Ambos os procedimentos limitam-se a apresentar a vista [formulaire]. Sabemos que os campos desta vista irão receber um valor que é, ou o valor lançado para eles, ou o seu valor anterior. Como, neste caso, o POST do cliente não envia nenhum valor para os campos do formulário, estes irão recuperar o seu valor anterior. O formulário é, portanto, apresentado com os valores introduzidos pelo utilizador.

8.9.4. Testes

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

O leitor poderá, se assim o desejar, consultar novamente o capítulo 5, onde é explicada a forma de criar o ficheiro [impot.dll] acima referido. Feito isto, o servidor Cassini é iniciado com os parâmetros (<application-path>,/impots6). Solicita-se o URL [http://impots6/main.aspx] através de um navegador:

Image

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

Image

8.9.5. Conclusão

Temos uma aplicação MVC que utiliza apenas componentes de servidor. A utilização destes permitiu separar completamente a parte de apresentação da aplicação da sua parte de controlo. Isso ainda não tinha sido possível até agora, em que o código de apresentação continha variáveis calculadas pelo código de controlo e cujo valor incluía código HTML. Agora, isso já não acontece.