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:

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:

O ASP.NET disponibiliza componentes denominados «componentes de validação» que permitem verificar os seguintes casos:
Componente | Função |
verifica se um campo não está vazio | |
verifica se dois valores são iguais | |
verifica se um valor está entre dois limites | |
verifica se um campo cumpre uma expressão regular | |
permite ao programador definir as suas próprias regras de validação — este componente poderia substituir todos os outros | |
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 | TextBox | EnableViewState=true | campo de introdução | |
2 | RequiredFieldValidator | EnableViewState=false EnableClientScript=true ErrorMessage=O campo [nome] é obrigatório | componente de validação | |
3 | 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:
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. | |
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. | |
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]:

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" />
</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:

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:

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

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:

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:

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

n.º | nome | tipo | propriedades | função |
1 | DropDownList | EnableViewState=true | lista suspensa | |
2 | DropDownList | EnableViewState=true | lista suspensa | |
3 | CompareValidator | EnableViewState=false EnableClientScript=true ErrorMessage=Opção 1 inválida ValorParaComparar=? Operador=NãoIgual Tipo=string | componente de validação | |
4 | CompareValidator | EnableViewState=false EnableClientScript=true ErrorMessage=Opção 2 inválida ControlToCompare=? Operator=NotEqual Tipo=string | componente de validação | |
5 | 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:
campo cujo conteúdo deve ser verificado pelo componente | |
booleano — se for verdadeiro, indica que o conteúdo do campo anterior também deve ser verificado do lado do cliente | |
a mensagem de erro que o componente deve apresentar caso seja detetado um erro | |
valor com o qual o valor do campo deve ser comparado [ControlToValidate] | |
componente cujo valor deve ser comparado com o valor do campo [ControlToValidate] | |
operador de comparação entre os dois valores | |
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:

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

n.º | nome | tipo | propriedades | função |
DropDownList | EnableViewState=true | lista suspensa | ||
CompareValidator | EnableViewState=false EnableClientScript=true ErrorMessage=Diploma inválido Operator=NotEqual ValueToCompare=? Type=String | componente de validação do controlo [1] | ||
TextBox | EnableViewState=false | campo de introdução | ||
CustomValidator | EnableViewState=false EnableClientScript=true ErrorMessage=Precisão do diploma inválida ClientValidationFunction=chkOutroDiploma | componente de validação do campo [3] | ||
TextBox | EnableViewState=false | campo de introdução | ||
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] | ||
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] | ||
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:
campo cujo valor deve ser verificado pelo componente | |
booleano — se for verdadeiro, indica que o conteúdo do campo anterior também deve ser verificado do lado do cliente | |
a mensagem de erro que o componente deve apresentar caso seja detetado um erro | |
valor mínimo do campo a verificar | |
valor máximo do campo a verificar | |
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:
campo cujo valor deve ser verificado pelo componente | |
booleano — se for verdadeiro, indica que o conteúdo do campo anterior também deve ser verificado do lado do cliente | |
a mensagem de erro que o componente deve apresentar caso seja detetado um erro | |
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îtrise">Maîtrise</asp:ListItem>
<asp:ListItem Value="DESS">DESS</asp:ListItem>
<asp:ListItem Value="DEA">DEA</asp:ListItem>
</asp:DropDownList>
</td>
<td>
Si [Autre], précisez</td>
<td>
<asp:TextBox id="txtAutreDiplome" runat="server" EnableViewState="False"></asp:TextBox>
</td>
</tr>
<tr>
<td>
</td>
<td>
<p>
<asp:CompareValidator id="CompareValidator2" runat="server" ErrorMessage="Diplôme invalide" ControlToValidate="cmbDiplomes" ValueToCompare="?" Operator="NotEqual" ></asp:CompareValidator>
</p>
</td>
<td>
</td>
<td>
<asp:CustomValidator id="CustomValidator1" runat="server" ErrorMessage="Précision diplôme invalide" OnServerValidate="CustomValidator1_ServerValidate_1" EnableViewState="False" ClientValidationFunction="chckAutreDiplome"></asp:CustomValidator>
</td>
</tr>
<tr>
<td>
Année d'obtention*</td>
<td colspan="3">
<asp:TextBox id="txtAnDiplome" runat="server" Columns="4"></asp:TextBox>
<asp:RangeValidator id="RangeValidator1" runat="server" ErrorMessage="Année diplôme doit être dans l'intervalle [1990,2004]" ControlToValidate="txtAnDiplome" MinimumValue="1990" MaximumValue="2004" Type="Integer"></asp:RangeValidator>
<asp:RequiredFieldValidator id="RequiredFieldValidator2" runat="server" ErrorMessage="Année diplôme requise" ControlToValidate="txtAnDiplome"></asp:RequiredFieldValidator>
</td>
</tr>
</tbody>
</table>
</fieldset>
<p>
<asp:Button id="btnEnvoyer" runat="server" Text="Envoyer"></asp:Button>
</p>
</form>
</body>
</html>
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]:

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

n.º | nome | tipo | propriedades | função |
TextBox | EnableViewState=true | campo de introdução | ||
RequiredFieldValidator | ControlToValidate=txtMel Display=Dynamic | componente de validação do controlo [1] | ||
RegularExpressionValidator | ControlToValidate=txtMel Display=Dynamic | componente de validação do controlo [1] | ||
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:
campo cujo valor deve ser verificado pelo componente | |
booleano — se for verdadeiro, indica que o conteúdo do campo anterior também deve ser verificado do lado do cliente | |
a mensagem de erro que o componente deve apresentar caso seja detetado um erro | |
a expressão regular com a qual o conteúdo de [ControlToValidate] será comparado | |
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:

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

n.º | nome | tipo | propriedades | função |
1 | ValidationSummary | HeaderText=Ocorreram os seguintes erros, EnableClientScript=false, ShowSummary=true | exibe os erros de todos os controlos de validação da página | |
2 | 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]:

1
n.º | nome | tipo | propriedades | função |
1 | 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:

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

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

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

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:
<option value="V">T</option> | |
<input type="checkbox" value="V">T | |
<input type="radio" value="V">T |
A associação de um componente [ListControl] a uma fonte de dados é feita através das seguintes propriedades:
uma fonte de dados [Array], [ArrayList], [DataTable], [DataSet], [HashTable], ... | |
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. | |
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] | |
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:
[ListControl].DataSource=A os campos [Text] e [Value] dos elementos de [ListControl] terão como valores os elementos de A | |
[ListControl].DataSource=AL os campos [Text] e [Value] dos elementos de [ListControl] terão como valores os elementos de AL | |
[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 | |
[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» | |
[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 |
Array | |
ArrayList | |
DataTable | |
DataSet | |
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:
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:
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:
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]:
uma fonte de dados [Array], [ArrayList], [DataTable], [DataSet], ... | |
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:

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

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

Vamos utilizar os dois links acima. O link [Générateur de propriétés] dá acesso às principais propriedades do [DataGrid]:

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:

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:
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 | DataGrid | EnableViewState=true | exibe uma fonte de dados S | |
2 | DataGrid | EnableViewState=true | exibe a mesma fonte S que [DataGrid1] | |
3 | 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:

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:

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 | DataGrid | EnableViewState=true | exibe uma fonte de dados S | |
2 | DataGrid | EnableViewState=false | exibe a mesma fonte S que [DataGrid1] | |
3 | Botão | EnableViewState=false | botão [submit] | |
4 | 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:

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 :
<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:
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:
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:
para guardar o novo valor de [numRequête3] na sessão. Em contrapartida, seria necessário escrever:
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:

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 | TextBox | EnableViewState=true | campo de introdução da consulta SELECT | |
2 | RequiredFieldValidator | EnableViewState=false | verifica se existe 1 | |
3 | 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 | RequiredFieldValidator | EnableViewState=false | verifica a presença de 3 | |
5 | RangeValidator | EnableViewState=false | verifica se (3) está no intervalo [1,30] | |
6 | 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 | Rótulo | EnableViewState=false | campo de informação | |
2 | RadioButton | EnableViewState=false GroupName=rdTri | permite escolher um sentido de ordenação | |
3 | DataGrid | EnableViewState=true AllowPaging=true AllowSorting=true | tabela de apresentação do resultado da seleção | |
4 | 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.

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

É apresentada a vista [erreurs]:
![]() |
n.º | nome | tipo | propriedades | função |
1 | variável | código HTML necessário para a exibição de erros | ||
3 | 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> Exemple : select id, nom, prix from LISTE
</P>
<P>
<asp:TextBox id="txtSelect" runat="server" Columns="60"></asp:TextBox></P>
<P>
<asp:RequiredFieldValidator id="RequiredFieldValidator1" runat="server" EnableViewState="False" EnableClientScript="False"
ErrorMessage="Indiquez la requête [select] à exécuter" ControlToValidate="txtSelect" Display="Dynamic"></asp:RequiredFieldValidator></P>
<P>Nombre de lignes par page :
<asp:TextBox id="txtPages" runat="server" Columns="3"></asp:TextBox></P>
<P>
<asp:RequiredFieldValidator id="RequiredFieldValidator2" runat="server" EnableViewState="False" EnableClientScript="False"
ErrorMessage="Indiquez le nombre de lignes par page désirées" ControlToValidate="txtPages" Display="Dynamic"></asp:RequiredFieldValidator>
<asp:RangeValidator id="RangeValidator1" runat="server" EnableViewState="False" EnableClientScript="False"
ErrorMessage="Vous devez indiquer un nombre entre 1 et 30" ControlToValidate="txtPages" Display="Dynamic"
Type="Integer" MinimumValue="1" MaximumValue="30"></asp:RangeValidator></P>
<P>
<asp:Button id="btnExécuter" runat="server" EnableViewState="False" Text="Exécuter"></asp:Button></P>
</asp:panel>
<asp:Panel id="vueRésultats" runat="server">
<P>Résultats de la requête
<asp:Label id="lblSelect" runat="server"></asp:Label></P>
<P>Tri
<asp:RadioButton id="rdCroissant" runat="server" Text="croissant" Checked="True" GroupName="rdTri"></asp:RadioButton>
<asp:RadioButton id="rdDécroissant" runat="server" Text="décroissant" GroupName="rdTri"></asp:RadioButton></P>
<P>
<asp:DataGrid id="DataGrid1" runat="server" BorderColor="#CC9966" BorderStyle="None" BorderWidth="1px"
BackColor="White" CellPadding="4" AllowPaging="True" PageSize="4" AllowSorting="True">
<SelectedItemStyle Font-Bold="True" ForeColor="#663399" BackColor="#FFCC66"></SelectedItemStyle>
<ItemStyle ForeColor="#330099" BackColor="White"></ItemStyle>
<HeaderStyle Font-Bold="True" ForeColor="#FFFFCC" BackColor="#990000"></HeaderStyle>
<FooterStyle ForeColor="#330099" BackColor="#FFFFCC"></FooterStyle>
<PagerStyle NextPageText="Suivant" PrevPageText="Précédent" HorizontalAlign="Center"
ForeColor="#330099" BackColor="#FFFFCC"></PagerStyle>
</asp:DataGrid></P>
<P>
<asp:LinkButton id="lnkRésultats" runat="server" EnableViewState="False">Retour au formulaire</asp:LinkButton></P>
</asp:Panel>
<asp:Panel id="vueErreurs" runat="server">
<P>Les erreurs suivantes se sont produites :</P>
<% =erreursHTML %>
<P>
<asp:LinkButton id="lnkErreurs" runat="server" EnableViewState="False">Retour vers le formulaire</asp:LinkButton></P>
</asp:Panel>
</form>
</body>
</HTML>
8.6.3. 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écédent" ...></PagerStyle>
</asp:DataGrid></P>
permite a paginação | |
quatro linhas de dados por página | |
O texto do link para aceder à página seguinte da fonte de dados | |
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]:

É apresentado o seguinte assistente:

Selecione a opção [Pagination]:

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]
[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:
definido como verdadeiro se os dados verificados forem válidos, e como falso caso contrário | |
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
o objeto de origem do evento — neste caso, um dos links [Suivant] ou [Précédent] | |
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
o objeto de origem do evento — neste caso, um dos links [Suivant] ou [Précédent] | |
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 | RadioButton | EnableViewState=false | permite escolher um dos dois estilos de [DataList] | |
2 | Botão | EnableViewState=false | botão [submit] | |
3 | 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 | 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 | 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]:
modelo do cabeçalho do [DataList] | |
modelo das linhas que apresentam os elementos da lista de dados associada. Apenas este modelo é obrigatório. | |
Para diferenciar visualmente os elementos sucessivos apresentados, podem ser utilizados dois modelos: ItemTemplate para o elemento n e AlternatingItemTemplate para o elemento n+1 | |
modelo do elemento selecionado no [DataList] | |
modelo do separador entre dois elementos do [DataList] | |
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» | |
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:
| |
|
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:

No exemplo acima, os dados foram dispostos à razão de quatro dados por linha. Isto é obtido com os seguintes atributos de [DataList]:
Horizontal | |
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]
[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:

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:

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:

O utilizador preenche o formulário:

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

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

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

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

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

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

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 |
Painel | vista do formulário | |||
RadioButton | GroupName=rdmarie | botões de opção | ||
CustomValidator | ErrorMessage=Não indicou o seu estado civil EnableClientScript=false | verifica se o programa cliente enviou efetivamente o estado civil esperado | ||
TextBox | número de filhos | |||
RequiredFieldValidator | ErrorMessage=Indique o número de filhos ControlToValidate=txtEnfants | verifica se o campo [txtEnfants] não está vazio | ||
RangeValidator | ErrorMessage=Introduza um número entre 1 e 30 ControlToValidate=txtEnfants | verifica se o campo [txtEnfants] está dentro do intervalo [1,30] | ||
TextBox | EnableViewState=true | salário anual | ||
RequiredFieldValidator | ControlToValidate=txtSalário ErrorMessage=Indique o montante do seu salário | verifica se o campo [txtSalaire] não está vazio | ||
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 | ||
Botão | CausesValidation=true | botão [submit] do formulário — inicia o cálculo do imposto | ||
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 |
Painel | EnableViewState=false | visualização de erros | ||
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 |
Painel | EnableViewState=false | vista de simulações | ||
LinkButton | EnableViewState=false | ligação para o formulário | ||
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:

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

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:

título da coluna, neste caso «Casado» | |
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:
Filhos | |
filhos |
A terceira coluna é definida da seguinte forma:
Salário anual | |
salário | |
{0:C} - formatação monetária para obter o símbolo do euro |
A quarta coluna é definida da seguinte forma:
Montante do imposto | |
imposto | |
{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é" HeaderText="Marié"></asp:BoundColumn>
<asp:BoundColumn DataField="enfants" HeaderText="Enfants"></asp:BoundColumn>
<asp:BoundColumn DataField="salaire" HeaderText="Salaire annuel" DataFormatString="{0:C}"></asp:BoundColumn>
<asp:BoundColumn DataField="impôt" HeaderText="Montant de l'impôt" DataFormatString="{0:C}"></asp:BoundColumn>
</Columns>
<PagerStyle HorizontalAlign="Center" ForeColor="#8C4510" Mode="NumericPages"></PagerStyle>
</asp:DataGrid></P>
<P>
<asp:LinkButton id="lnkForm2" runat="server" EnableViewState="False">Retour au formulaire</asp:LinkButton></P>
</asp:panel>
</FORM>
</body>
</HTML>
8.9.3. 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:
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:

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

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.






















