8. Componentes do Servidor ASP - 2
8.1. Introdução
Continuamos o nosso trabalho na interface do utilizador explorando:
- componentes de validação de dados
- como ligar dados a componentes do servidor
- componentes de servidor HTML
8.2. componentes de validação de dados
8.2.1. Introdução
Em aplicações baseadas em formulários, é essencial verificar a validade dos dados introduzidos nos mesmos. No exemplo de cálculo de impostos, tínhamos o seguinte formulário:

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

O ASP.NET oferece componentes chamados componentes de validação que permitem verificar o seguinte:
Componente | Função |
verifica se um campo não está vazio | |
compara dois valores entre si | |
verifica se um valor se encontra entre dois limites | |
verifica se um campo corresponde a 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 geradas pelos controlos anteriores num único local da página |
Vamos agora apresentar cada um destes componentes.
8.2.2. RequiredFieldValidator
Estamos a criar a seguinte página [requiredfieldvalidator1.aspx]:
![]() |
N.º | nome | tipo | propriedades | função |
1 | Caixa de Texto | EnableViewState=true | campo de entrada | |
2 | Validador de campo obrigatório | EnableViewState=false AtivarScriptCliente=true ErrorMessage=O campo [nome] é obrigatório | componente de validação | |
3 | Botão | EnableViewState=false Motivos de validação=true | Botão [submit] |
O campo [RequiredFieldValidator1] é utilizado para apresentar uma mensagem de erro se o campo [txtName] estiver vazio ou contiver uma sequência de espaços. As suas propriedades são as seguintes:
O campo cujo valor deve ser validado pelo componente. O que significa o valor de um componente? É o valor do atributo [value] da tag HTML correspondente. Para um [TextBox], este é o conteúdo do campo de entrada; para um [DropDownList], é o valor do item selecionado. | |
Booleano - quando verdadeiro, indica que o conteúdo do campo anterior também deve ser validado no lado do cliente. Neste caso, o formulário só será enviado pelo navegador se não contiver erros. No entanto, as verificações de validação também são realizadas no servidor, caso o cliente não seja um navegador, por exemplo. | |
A mensagem de erro que o componente deve exibir se for detetado um erro |
A validação de dados na página é realizada apenas se o botão ou link que acionou o [POST] da página tiver a propriedade [CausesValidation=true]. [true] é o valor padrão para esta propriedade.
Vamos executar esta aplicação. Primeiro, vamos usar um navegador [Mozilla]:

O código HTML recebido pelo [Mozilla] é o seguinte:
<html>
<head>
</head>
<body>
<form name="_ctl0" method="post" action="requiredfieldvalidator1.aspx" id="_ctl0">
<input type="hidden" name="__VIEWSTATE" value="dDwxNDI1MDc1NTU1Ozs+SGtdZvVxefDCDxnsqbDnqCaROsk=" />
<p>
Demande du dossier de candidature au DESS
</p>
<fieldset>
<legend>[--Identité--]</legend>
<table>
<tbody>
<tr>
<td>
Nom*</td>
<td>
<input name="txtNom" type="text" id="txtNom" />
</td>
</tr>
</tbody>
</table>
</fieldset>
<p>
<input type="submit" name="btnEnvoyer" value="Envoyer" onclick="if (typeof(Page_ClientValidate) == 'function') Page_ClientValidate(); " language="javascript" id="btnEnvoyer" />
</p>
</form>
</body>
</html>
Podemos ver que o botão [btnEnvoyer] está ligado a uma função JavaScript através do seu atributo [onclick]. Também podemos ver que a página não contém código [JavaScript]. O teste [typeof(Page_ClientValidate) == 'function'] irá falhar, e a função JavaScript não será chamada. O formulário será então enviado para o servidor, que realizará as verificações de validação. Vamos utilizar o botão [Submit] sem introduzir um nome. A resposta do servidor é a seguinte:

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

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

O código HTML recebido pelo IE é o seguinte:
<html>
<head>
</head>
<body>
<form name="_ctl0" method="post" action="requiredfieldvalidator1.aspx" language="javascript" onsubmit="ValidatorOnSubmit();" id="_ctl0">
<input type="hidden" name="__VIEWSTATE" value="dDwxNDI1MDc1NTU1Ozs+SGtdZvVxefDCDxnsqbDnqCaROsk=" />
<script language="javascript" src="/aspnet_client/system_web/1_1_4322/WebUIValidation.js"></script>
<p>
Demande du dossier de candidature au DESS
</p>
<fieldset>
<legend>[--Identité--]</legend>
<table>
<tbody>
<tr>
<td>
Nom*</td>
<td>
<input name="txtNom" type="text" id="txtNom" />
<span id="RequiredFieldValidator1" controltovalidate="txtNom" errormessage="Le champ [nom] est obligatoire" evaluationfunction="RequiredFieldValidatorEvaluateIsValid" initialvalue="" style="color:Red;visibility:hidden;">Le champ [nom] est obligatoire</span>
</td>
</tr>
</tbody>
</table>
</fieldset>
<p>
<input type="submit" name="btnEnvoyer" value="Envoyer" onclick="if (typeof(Page_ClientValidate) == 'function') Page_ClientValidate(); " language="javascript" id="btnEnvoyer" />
</p>
<script language="javascript">
<!--
var Page_Validators = new Array(document.all["RequiredFieldValidator1"]);
// -->
</script>
<script language="javascript">
<!--
var Page_ValidationActive = false;
if (typeof(clientInformation) != "undefined" && clientInformation.appName.indexOf("Explorer") != -1) {
if (typeof(Page_ValidationVer) == "undefined")
alert("Impossible de trouver la bibliothèque de scripts /aspnet_client/system_web/1_1_4322/WebUIValidation.js. Essayez de placer ce fichier manuellement ou effectuez une réinstallation en exécutant 'aspnet_regiis -c'.");
else if (Page_ValidationVer != "125")
alert("Cette page utilise une version incorrecte de WebUIValidation.js. La page requiert la version 125. La bibliothèque de scripts est " + Page_ValidationVer + ".");
else
ValidatorOnLoad();
}
function ValidatorOnSubmit() {
if (Page_ValidationActive) {
ValidatorCommonOnSubmit();
}
}
// -->
</script>
</form>
</body>
</html>
Podemos ver que este código é muito mais longo do que o recebido pelo [Mozilla]. Não entraremos em detalhes. A diferença decorre do facto de o servidor ter incluído funções JavaScript no documento HTML enviado. Por que razão existem dois códigos HTML diferentes quando o URL solicitado por ambos os navegadores é o mesmo? Já referimos anteriormente que a tecnologia ASP.NET faz com que o servidor adapte o documento HTML enviado ao cliente ao ambiente específico desse cliente. Existem vários navegadores no mercado e nem todos têm as mesmas capacidades. Os navegadores da Microsoft e da Netscape, por exemplo, não utilizam o mesmo modelo de objeto para o documento que recebem. Consequentemente, o código JavaScript utilizado para manipular este documento no lado do cliente difere entre os dois navegadores. Da mesma forma, os fornecedores de navegadores têm lançado versões sucessivas dos seus navegadores, melhorando continuamente as suas capacidades. Assim, um documento HTML que o IE5 consegue analisar pode não ser compreendido pelo IE3. Acima está um exemplo desta adaptação do lado do servidor para o cliente. Por uma razão que não é explorada em profundidade aqui, o servidor web determinou que o cliente [Mozilla] não tinha a capacidade de lidar com o código JavaScript de validação. Portanto, este código não foi incluído no documento HTML que lhe foi enviado, e a validação foi realizada no lado do servidor. Para o [IE6], este código JavaScript foi incluído no documento HTML enviado, como podemos ver. Para ver isto em ação, vamos tentar a seguinte experiência:
- parar o servidor web
- clique no botão [Submit] sem preencher o campo [Name]
Recebemos a seguinte nova página:

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

O que aconteceu? O navegador executou o código de validação JavaScript. Este código determinou que a página era válida. O navegador enviou então o formulário para o servidor web, que estava parado. O navegador percebeu isso e exibiu a página acima. Se reiniciarmos o servidor, tudo volta ao normal.
O que podemos aprender com este exemplo?
- o valor do conceito de componente de validação, que permite que qualquer formulário incorreto seja devolvido ao cliente
- o valor do [VIEWSTATE], que permite que o formulário seja devolvido exatamente como foi preenchido
- a capacidade do servidor de se adaptar ao seu cliente. O cliente é identificado pelo cabeçalho HTTP [User-Agent:] que envia ao servidor. Portanto, não é o servidor que «adivinha» com quem está a lidar. Esta adaptabilidade é de grande interesse para o programador, que não precisa de se preocupar com o tipo de cliente para a sua aplicação.
8.2.3. CompareValidator
Estamos a construir a seguinte página [comparevalidator1.aspx]:

N.º | nome | tipo | propriedades | função |
1 | Lista suspensa | EnableViewState=true | lista suspensa | |
2 | Lista suspensa | EnableViewState=true | lista suspensa | |
3 | CompareValidator | EnableViewState=false AtivarScriptCliente=true Mensagem de erro=Escolha inválida 1 Valor a comparar=? Operador=Não igual Tipo=string | componente de validação | |
4 | CompareValidator | AtivarViewState=false AtivarScriptCliente=true Mensagem de erro=Escolha inválida 2 Controlo a comparar=? Operador=Não igual Tipo=string | componente de validação | |
5 | Botão | EnableViewState=false Motivos de validação=true | Botão [submit] |
O componente [CompareValidator] é utilizado para comparar dois valores. Os operadores disponíveis são [Equal, NotEqual, LessThan, LessThanEqual, GreaterThan, GreaterThanEqual]. O primeiro valor é definido pela propriedade [ControlToValidate], o segundo por [ValueToCompare] se o primeiro valor for comparado a uma constante, ou por [ControlToCompare] se for comparado ao valor de outro componente. As propriedades importantes do componente [CompareValidator] são as seguintes:
campo cujo conteúdo deve ser validado pelo componente | |
Booleano - quando verdadeiro, indica que o conteúdo do campo anterior também deve ser validado no lado do cliente | |
a mensagem de erro que o componente deve exibir se for detetado um erro | |
o valor com o qual o valor do campo [ControlToValidate] deve ser comparado | |
o 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 HTML desta página é o seguinte:
<html>
<head>
</head>
<body>
<form runat="server">
<p>
Demande du dossier de candidature au DESS
</p>
<fieldset>
<legend>[--Options du DESS pour lesquelles vous candidatez--]</legend>
<table>
<tbody>
<tr>
<td>
Option1*</td>
<td>
Option2</td>
</tr>
<tr>
<td>
<asp:DropDownList id="cmbChoix1" runat="server">
<asp:ListItem Value="?" Selected="True">?</asp:ListItem>
<asp:ListItem Value="Automatique">Automatique</asp:ListItem>
<asp:ListItem Value="Informatique">Informatique</asp:ListItem>
</asp:DropDownList>
</td>
<td>
<asp:DropDownList id="cmbChoix2" runat="server">
<asp:ListItem Value="?">?</asp:ListItem>
<asp:ListItem Value="Automatique">Automatique</asp:ListItem>
<asp:ListItem Value="Informatique">Informatique</asp:ListItem>
</asp:DropDownList>
</td>
</tr>
<tr>
<td>
<asp:CompareValidator id="CompareValidator1" runat="server" ErrorMessage="Choix 1 invalide" ControlToValidate="cmbChoix1" ValueToCompare="?" Operator="NotEqual"></asp:CompareValidator>
</td>
<td>
<asp:CompareValidator id="CompareValidator2" runat="server" ErrorMessage="Choix 2 invalide" ControlToValidate="cmbChoix2" Operator="NotEqual" ControlToCompare="cmbChoix1"></asp:CompareValidator>
</td>
</tr>
</tbody>
</table>
</fieldset>
<p>
<asp:Button id="btnEnvoyer" runat="server" Text="Envoyer"></asp:Button>
</p>
</form>
</body>
</html>
As restrições de validação para a página são as seguintes:
- O valor selecionado em [cmbChoix1] deve ser diferente da cadeia de caracteres "?". Isto implica as seguintes propriedades para o componente [CompareValidator1]: Operator=NotEqual, ValueToCompare=?, Type=string
- O valor selecionado em [cmbChoix2] deve ser diferente do valor selecionado em [cmbChoix1]. Isto implica as seguintes propriedades para o componente [CompareValidator2]: Operador=NãoIgual, ControloParaComparar=cmbChoix1, Tipo=string
Executamos esta aplicação. Aqui está um exemplo de uma página recebida durante as trocas cliente-servidor:

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

N.º | nome | tipo | propriedades | função |
Lista suspensa | EnableViewState=true | lista suspensa | ||
CompareValidator | EnableViewState=false AtivarScriptCliente=true Mensagem de erro=Diploma inválido Operador=Não igual Valor a comparar=? Tipo=String | componente de validação do controlo [1] | ||
Caixa de Texto | EnableViewState=false | campo de entrada | ||
Validador personalizado | EnableViewState=false AtivarScriptCliente=true ErrorMessage=Especificação de grau inválida Função de validação do cliente=chkOtherDegree | componente de validação para o campo [3] | ||
Caixa de Texto | EnableViewState=false | campo de entrada | ||
Validador de intervalo | EnableViewState=false EnableClientScript=true ErrorMessage=O ano de conclusão do curso deve estar no intervalo [1990,2004] ValorMínimo=1990 MaxValue=2004 Tipo=Inteiro ControlToValidate=txtAnDiplome | componente de validação de campo [5] | ||
Validador de campo obrigatório | EnableViewState=false EnableClientScript=true Mensagem de erro=É necessário indicar o ano de conclusão ControlToValidate=txtAnDiplome | componente de validação de campo [5] | ||
Botão | EnableViewState=false Motivos de validação=true | Botão [submit] |
O campo [RangeValidator] é utilizado para verificar se o valor de um controlo se situa entre dois limites [MinValue] e [MaxValue]. As suas propriedades são as seguintes:
campo cujo valor deve ser validado pelo componente | |
Booleano - quando verdadeiro, indica que o conteúdo do campo anterior também deve ser validado no lado do cliente | |
a mensagem de erro que o componente deve exibir se for detetado um erro | |
valor mínimo do campo a ser validado | |
valor máximo do campo a ser verificado | |
Tipo do valor do campo a ser validado |
O campo [CustomValidator] permite realizar validações que não podem ser realizadas pelos componentes de validação fornecidos pelo ASP.NET. Esta validação é realizada por uma função escrita pelo programador. Esta função é executada no lado do servidor. Uma vez que a validação também pode ser realizada no lado do cliente, o programador poderá ter de desenvolver uma função JavaScript para incluir no documento HTML. As suas propriedades são as seguintes:
O campo cujo valor deve ser validado pelo componente | |
Booleano - quando verdadeiro, indica que o conteúdo do campo anterior também deve ser validado no lado do cliente | |
A mensagem de erro que o componente deve exibir se for detetado um erro | |
A função a ser executada no lado do cliente |
O código do modelo de página é o seguinte:
<%@ Page Language="VB" %>
<script runat="server">
...
</script>
<html>
<head>
</head>
<body>
<form runat="server">
<p align="left">
Demande du dossier de candidature au DESS
</p>
<fieldset>
<legend>[--Votre dernier diplôme--]</legend>
<table>
<tbody>
<tr>
<td>
Diplôme*</td>
<td>
<asp:DropDownList id="cmbDiplomes" runat="server">
<asp:ListItem Value="?">?</asp:ListItem>
<asp:ListItem Value="[Autre]">[Autre]</asp:ListItem>
<asp:ListItem Value="Maî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. No exemplo acima, o componente [CustomValidator1] chama o seguinte procedimento [CustomValidator1_ServerValidate_1]:
<%@ Page Language="VB" %>
<script runat="server">
Sub CustomValidator1_ServerValidate_1(sender As Object, e As ServerValidateEventArgs)
' field [txtAutreDiplome] must be non-empty if [cmbDiplomes]=[other]
e.isvalid=not (cmbDiplomes.selecteditem.text="[Autre]" and txtAutreDiplome.text.trim="") _
and not (cmbDiplomes.selecteditem.text<>"[Autre]" and cmbDiplomes.selecteditem.text<>"?" and txtAutreDiplome.text.trim<>"")
End Sub
</script>
....
Uma função de validação associada a um componente [CustomValidator] recebe dois parâmetros:
- sender: o objeto que desencadeou o evento
- e: o evento. O procedimento deve definir a propriedade [e.IsValid] como true se os dados validados estiverem corretos e como false caso contrário.
Aqui, são realizadas as seguintes verificações:
- a lista suspensa [cmbDiplomes] não pode ter [?] como valor. Isto é verificado pelo componente [CompareValidator2]
- o utilizador seleciona um grau na lista suspensa [cmbDiplomes]. Se o seu grau não existir na lista, pode selecionar a opção [Outro] na lista. Deve então introduzir o seu grau no campo de entrada [txtAutreDiplome]. Se [Outro] for selecionado em [cmbDiplomes], o campo [txtAutreDiplome] não pode estar vazio. Se nem [Outro] nem [?] forem selecionados em [cmbDiplomes], o campo [txtAutreDiplome] deve estar vazio. Isto é verificado pela função associada ao componente [CustomValidator1].
- O ano em que o diploma foi obtido deve estar dentro do intervalo [1900-2004]. Isto é verificado pelo [RangeValidator1]. No entanto, se o utilizador deixar o campo em branco, a função de validação do [RangeValidator1] não é utilizada. Por isso, adicionamos o componente [RequiredFieldValidator2] ao [ ] para verificar a presença de conteúdo. Esta é uma regra geral. O conteúdo de um campo não é verificado se estiver vazio. O único caso em que é verificado é quando está associado a um componente [RequiredFieldValidator].
Aqui está um exemplo de execução no navegador [Mozilla]:

Se utilizarmos o navegador [IE6], podemos adicionar uma função de validação do lado do cliente para o componente [CustomValidator1]. Para tal, este componente possui as seguintes propriedades:
- EnableClientScript=true
- ClientValidationFunction=chkAutreDiplome
A função [chkAutreDiplome] é a seguinte:
<head>
<meta http-equiv="pragma" content="no-cache" />
<script language="javascript">
function chkAutreDiplome(source,args){
// vérifie la validité du champ txtAutreDiplome
with(document.frmCandidature){
diplome=cmbDiplomes.options[cmbDiplomes.selectedIndex].text;
args.IsValid= !(diplome=="[Autre]" && txtAutreDiplome.value=="")
&& ! (diplome!="[Autre]" && diplome!="?" && txtAutreDiplome.value!="");
}
}
</script>
</head>
A função [chkAutreDiplome] recebe os mesmos dois parâmetros que a função do servidor [CustomValidator1_ServerValidate_1]:
- source: o objeto que desencadeou o evento
- args: o evento. Este possui um atributo [IsValid] que deve ser definido como true pela função se os dados validados estiverem corretos. Um valor false exibirá a mensagem de erro associada no local do componente de validação, e o formulário não será enviado para o servidor.
8.2.5. RegularExpressionValidator
Estamos a criar a seguinte página [regularexpressionvalidator1.aspx]:

N.º | nome | tipo | propriedades | função |
Caixa de Texto | EnableViewState=true | campo de entrada | ||
Validador de campo obrigatório | ControlToValidate=txtMel Exibir=Dinâmico | componente de validação de controlo [1] | ||
Validador de Expressão Regular | ControlToValidate=txtMel Exibição=Dinâmica | componente de validação de controlo [1] | ||
Botão | EnableViewState=false CausesValidation=true | Botão [submit] |
Aqui, queremos validar o formato de um endereço de e-mail. Fazemos isso utilizando um componente [RegularExpressionValidator], que nos permite validar um campo utilizando uma expressão regular. Lembre-se de que uma expressão regular é um padrão de caracteres. Por isso, verificamos o conteúdo de um campo em relação a um padrão. Uma vez que o conteúdo de um campo não é verificado se estiver vazio, também precisamos de um componente [RequiredFieldValidator] para verificar se o endereço de e-mail não está vazio. A classe [RegularExpressionValidator] tem propriedades semelhantes às das classes de componentes já abordadas:
campo cujo valor deve ser validado pelo componente | |
booleano - quando verdadeiro, indica que o conteúdo do campo anterior também deve ser validado no lado do cliente | |
a mensagem de erro que o componente deve exibir se for detetado um erro | |
a expressão regular com a qual o conteúdo de [ControlToValidate] será comparado | |
modo de exibição: Estático: o campo está sempre presente, mesmo que não exiba uma mensagem de erro; Dinâmico: o campo está presente apenas se houver uma mensagem de erro; Nenhum: a mensagem de erro não é exibida. Este campo também existe para outros componentes, mas não tinha sido utilizado até agora. |
O padrão de expressão regular para um endereço de e-mail poderia ser o seguinte: \s*[\w-]+(\.[\w-]+)*@[\w-]+(\.[\w-]+)+\s*
Um endereço de e-mail tem o formato [champ1.champ2....@champA.champB...]. Deve haver pelo menos um campo antes do símbolo @ e pelo menos dois campos depois dele. Estes campos consistem em caracteres alfanuméricos e no símbolo -. Um caractere alfanumérico é representado pelo símbolo \w. A sequência [\w-] significa o caractere \w ou o caractere -. O sinal + após uma sequência S significa que esta pode ser repetida 1 ou mais vezes; o sinal * significa que pode ser repetida 0 ou mais vezes; o sinal ? significa que pode ser repetida 0 ou 1 vez.
Um campo no endereço de e-mail corresponde ao padrão [\w-]+. O facto de ter de haver pelo menos um campo antes do sinal @ e pelo menos dois depois dele, separados pelo sinal ., corresponde ao padrão: [\w-]+(\.[\w-]+)*@[\w-]+(\.[\w-]+)+. Podemos permitir que o utilizador inclua espaços antes e depois do endereço. Estes serão removidos durante o processamento. Uma sequência de espaços, que pode estar vazia, é representada pelo padrão \s*. Daí a expressão regular para o endereço de e-mail: \s*[\w-]+(\.[\w-]+)*@[\w-]+(\.[\w-]+)+\s*.
O código de layout da página é, então, o seguinte:
<html>
<head>
</head>
<body>
<form runat="server">
<p align="left">
Demande du dossier de candidature au DESS
</p>
<fieldset>
<legend>[--Votre adresse électronique où sera envoyé le dossier de candidature--]</legend>
<table>
<tbody>
<tr>
<td>
<asp:TextBox id="txtMel" runat="server" Columns="60"></asp:TextBox>
</td>
</tr>
<tr>
<td>
<p>
<asp:RequiredFieldValidator id="RequiredFieldValidator3" runat="server" Display="Dynamic" ControlToValidate="txtMel" ErrorMessage="Votre adresse électronique est requise"></asp:RequiredFieldValidator>
</p>
<p>
<asp:RegularExpressionValidator id="RegularExpressionValidator1" runat="server" Display="Dynamic" ControlToValidate="txtMel" ErrorMessage="Votre adresse électronique n'a pas un format valide" ValidationExpression="\s*[\w-]+(\.[\w-]+)*@[\w-]+(\.[\w-]+)+\s*"></asp:RegularExpressionValidator>
</p>
</td>
</tr>
</tbody>
</table>
</fieldset>
<p>
<asp:Button id="btnEnvoyer" runat="server" Text="Envoyer"></asp:Button>
</p>
</form>
</body>
</html>
8.2.6. Resumo da validação
Por razões estéticas, poderá querer agrupar as mensagens de erro num único local, como no exemplo seguinte [summaryvalidator1.aspx], onde combinámos todos os exemplos anteriores numa única página:

Todos os controlos de validação têm as seguintes propriedades:
- [Display=Dynamic], que garante que os controlos não ocupem espaço na página se a sua mensagem de erro estiver vazia
- [EnableClientScript=false] para desativar toda a validação do lado do cliente
- [Text=*]. Esta será a mensagem exibida em caso de erro, enquanto o conteúdo do atributo [ErrorMessage] é exibido pelo controlo [ValidationSummary] apresentado abaixo.
Este conjunto de controlos é colocado num componente [Panel] chamado [vueFormulaire]. Num outro componente [Panel] chamado [vueErreurs], colocamos um controlo [ValidationSummary]:

N.º | nome | tipo | propriedades | função |
1 | Resumo da validação | Texto do cabeçalho=Ocorreram os seguintes erros, AtivarScriptCliente=false, MostrarResumo=true | exibe os erros de todos os controlos de validação na página | |
2 | Botão de ligação | ValidationCauses=false | ligação de volta ao formulário |
Para o link [lnkErrorsToForm], não é necessário ativar a validação, uma vez que este link não envia quaisquer valores para verificação.
Quando todos os dados são válidos, a seguinte vista [info] é apresentada ao utilizador:

1
N.º | Nome | tipo | propriedades | função |
1 | Rótulo | Mensagem informativa para o utilizador |
O código HTML desta página é o seguinte:
<html>
<head>
</head>
<body>
<form runat="server">
<p align="left">
Demande du dossier de candidature au DESS
</p>
<p>
<hr />
<asp:panel id="vueErreurs" runat="server">
<p align="left">
<asp:ValidationSummary id="ValidationSummary1" runat="server" ShowMessageBox="True" BorderColor="#C04000" BorderWidth="1px" BackColor="#FFFFC0" HeaderText="Les erreurs suivantes se sont produites"></asp:ValidationSummary>
</p>
<p>
<asp:LinkButton id="lnkErreursToFormulaire" onclick="lnkErreursToFormulaire_Click" runat="server" CausesValidation="False">Retour au formulaire</asp:LinkButton>
</p>
</asp:panel>
<asp:panel id="vueFormulaire" runat="server">
....
<asp:Button id="btnEnvoyer" onclick="btnEnvoyer_Click" runat="server" Text="Envoyer"></asp:Button>
</p>
</asp:panel>
<asp:panel id="vueInfos" runat="server">
<asp:Label id="lblInfo" runat="server"></asp:Label>
</asp:panel>
</form>
</body>
</html>
Aqui estão alguns exemplos dos resultados obtidos. Enviamos a vista [form] sem introduzir quaisquer valores:

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

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

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

O código de controlo da página é o seguinte:
<%@ Page Language="VB" %>
<script runat="server">
' procedure executed when the page is loaded
Sub page_Load(sender As Object, e As EventArgs)
' on the 1st request, we present the [form] view
if not ispostback then
afficheVues(true,false,false)
end if
end sub
sub afficheVues(byval formulaireVisible as boolean, _
erreursVisible as boolean, infosVisible as boolean)
' set of views
vueFormulaire.visible=formulaireVisible
vueErreurs.visible=erreursVisible
vueInfos.visible=infosVisible
end sub
Sub CustomValidator1_ServerValidate(sender As Object, e As ServerValidateEventArgs)
...
End Sub
Sub CustomValidator2_ServerValidate(sender As Object, e As ServerValidateEventArgs)
...
End Sub
Sub CustomValidator1_ServerValidate_1(sender As Object, e As ServerValidateEventArgs)
...
End Sub
Sub lnkErreursToFormulaire_Click(sender As Object, e As EventArgs)
' displays the form view
afficheVues(true,false,false)
' redo validity checks
Page.validate
End Sub
Sub btnEnvoyer_Click(sender As Object, e As EventArgs)
' is the page valid?
if not Page.IsValid then
' the [errors] view is displayed
afficheVues(false,true,false)
else
' otherwise the view [infos]
lblInfo.Text="Le dossier de candidature au DESS IAIE a été envoyé à l'adresse ["+ _
txtMel.Text + "]. Nous vous en souhaitons bonne réception.<br><br>Le secrétariat du DESS."
afficheVues(false,false,true)
end if
end sub
</script>
<html>
...
</html>
- No procedimento [Page_Load], que é executado em cada pedido do cliente, apresentamos a vista [form], enquanto as outras ficam ocultas. Isto é feito apenas no primeiro pedido. O procedimento chama um procedimento utilitário [displayViews], ao qual passamos três valores booleanos para determinar se as três vistas devem ou não ser apresentadas.
- Quando o procedimento [btnEnvoyer_Click] é chamado, a validação de dados já foi realizada. O botão [btnEnvoyer] tem a propriedade [CausesValidation=true], que aciona esta validação de dados. Todos os controlos de validação foram executados e a sua propriedade [IsValid] foi definida. Esta propriedade indica se os dados validados pelo controlo eram válidos ou não. Além disso, a propriedade [IsValid] da própria página também foi definida. Ela é [true] apenas se a propriedade [IsValid] de todos os controlos de validação na página tiver o valor [true]. Assim, o procedimento [btnEnvoyer_Click] começa por verificar se a página é válida ou não. Se for inválida, é apresentada a vista [errors]. Esta vista contém o controlo [ValidationSummary], que lista os atributos [ErrorMessage] de todos os controlos (ver captura de ecrã acima). Se a página for válida, é apresentada a vista [info] juntamente com uma mensagem informativa.
- O procedimento [lnkErrorsToForm_Click] é responsável por exibir a vista [form] no estado em que foi validada. Uma vez que todos os campos de entrada na vista [form] têm a propriedade [EnableViewState=true], o seu estado é automaticamente regenerado. Curiosamente, o estado dos componentes de validação não é restaurado. Seria de esperar que, ao regressar à vista [errors], os controlos inválidos exibissem o seu campo [Text]. Não é esse o caso. Por isso, forçámos a validação de dados utilizando o método [Page.Validate] da página. Isto deve ser feito assim que o painel [formView] for tornado visível. Existem, assim, um total de duas validações. Na prática, isto deve ser evitado. Aqui, o exemplo permitiu-nos introduzir novos conceitos relativos à validação de páginas.
8.3. Componentes ListControl e ligação de dados
Vários dos componentes de servidor abordados permitem-lhe apresentar uma lista de valores (DropDownList, ListBox). Outros que ainda não abordámos permitem-lhe apresentar várias listas de valores em tabelas HTML. Para todos eles, é possível associar programaticamente os valores nas listas ao componente correspondente, um por um. Também é possível associar objetos mais complexos a estes componentes, tais como objetos do tipo [Array], [ArrayList], [DataSet], [HashTable], etc., o que simplifica o código que associa os dados ao componente. Esta associação é chamada de ligação de dados.
Todos os componentes derivados da classe [ListControl] podem ser associados a uma lista de dados. Estes incluem os componentes [DropDownList], [ListBox], [CheckButtonList] e [RadioButtonList]. Cada um destes componentes pode ser vinculado a uma fonte de dados. Isto pode assumir várias formas: [Array], [ArrayList], [DataTable], [DataSet], [HashTable], ... geralmente um objeto que implementa uma das interfaces IEnumerable, ICollection ou IListSource. Apresentaremos aqui apenas alguns deles. Um objeto [DataSet] é uma representação de uma base de dados relacional. É, portanto, um conjunto de tabelas ligadas por relações. O objeto [DataTable] representa uma tabela desse tipo. A fonte de dados define as propriedades [Text] e [Value] de cada [Item] no objeto [ListControl]. Se T for o valor de [Text] e V for o valor de [Value], a tag HTML gerada para cada elemento de [ListControl] é a seguinte:
<option value="V">T</option> | |
<input type="checkbox" value="V">T | |
<input type="radio" value="V">T |
Um componente [ListControl] é associado a uma fonte de dados utilizando as seguintes propriedades:
uma fonte de dados [Array], [ArrayList], [DataTable], [DataSet], [HashTable], ... | |
se a fonte de dados for um [DataSet], representa o nome da tabela a ser utilizada como fonte de dados. A fonte de dados real é, então, uma tabela. | |
se a fonte de dados for uma tabela ([DataTable], [DataSet]), representa o nome da coluna da tabela que fornecerá valores ao campo [Text] dos itens do [ListControl] | |
se a fonte de dados for uma tabela ([DataTable], [DataSet]), representa o nome da coluna da tabela que fornecerá valores ao campo [Value] dos elementos [ListControl] |
A ligação de um componente [ListControl] a uma fonte de dados não inicializa o componente com os valores da fonte de dados. A operação [ListControl].DataBind é que o faz.
Dependendo da natureza da fonte de dados, a ligação a um componente [ListControl] será feita de forma diferente:
[ListControl].DataSource=A Os campos [Text] e [Value] dos elementos [ListControl] terão os valores dos elementos em A | |
[ListControl].DataSource=AL Os campos [Text] e [Value] dos elementos [ListControl] terão os valores dos elementos em 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 itens [ListControl] terão os valores das colunas col1 e col2 da tabela DT | |
[ListControl].DataSource=DS, [ListControl].DataSource="table", em que "table" é o nome de uma das tabelas em DS. [ListControl].DataTextField="col1", [ListControl].DataValueField ="col2" onde col1 e col2 são duas colunas da tabela "table". Os campos [Text] e [Value] dos elementos [ListControl] terão os valores das colunas col1 e col2 da tabela "table" | |
[ListControl].DataSource = HT, [ListControl].DataTextField = "key", [ListControl].DataValueField = "value", onde [key] e [value] são as chaves e os valores de HT, respetivamente. |
Aplicamos esta informação ao seguinte exemplo [databind1.aspx]:
![]() |
Aqui temos cinco ligações de dados, cada uma com quatro controlos dos tipos [DropDownList], [ListBox], [CheckBoxList] e [RadioButtonList]. As cinco ligações analisadas diferem nas suas fontes de dados:
ligação | fonte de dados |
Matriz | |
Lista de matrizes | |
DataTable | |
Conjunto de dados | |
Tabela de hash |
8.3.1. Código de apresentação do componente
O código de apresentação para os controlos na ligação 1 é o seguinte:
<td>
<asp:DropDownList id="DropDownList1" runat="server"></asp:DropDownList>
</td>
<td>
<asp:ListBox id="ListBox1" runat="server" SelectionMode="Multiple"></asp:ListBox>
</td>
<td>
<asp:CheckBoxList id="CheckBoxList1" runat="server"></asp:CheckBoxList>
</td>
<td>
<asp:RadioButtonList id="RadioButtonList1" runat="server"></asp:RadioButtonList>
</td>
A ligação para as ligações 2 a 5 é idêntica, exceto pelo número da ligação.
8.3.2. Ligação a uma fonte de dados de matriz
A ligação dos quatro controlos [ListBox] acima é feita da seguinte forma no procedimento [Page_Load] do código do controlo:
' global data
dim textes() as string={"un","deux","trois","quatre"}
dim valeurs() as string={"1","2","3","4"}
dim myDataListe as new ArrayList
dim myDataTable as new DataTable("table1")
dim myDataSet as new DataSet
dim myHashTable as new HashTable
' procedure executed when the page is loaded
Sub page_Load(sender As Object, e As EventArgs)
if not IsPostBack then
' create the data sources to be linked to the components
createDataSources
' link to an array [Array]
bindToArray
' link to a list [ArrayList]
bindToArrayList
' link to a table [DataTable]
bindToDataTable
' link to a data group [DataSet]
bindToDataSet
' link to a dictionary [HashTable]
bindToHashTable
end if
End Sub
sub createDataSources
' creates data sources to be linked to components
' arraylist
dim i as integer
for i=0 to textes.length-1
myDataListe.add(textes(i))
next
' datatable
' we define its two columns
myDataTable.Columns.Add("id",Type.GetType("System.Int32"))
myDataTable.Columns.Add("texte",Type.GetType("System.String"))
' fill the table
dim ligne as DataRow
for i=0 to textes.length-1
ligne=myDataTable.NewRow
ligne("id")=i
ligne("texte")=textes(i)
myDataTable.Rows.Add(ligne)
next
' dataset - a single table
myDataSet.Tables.Add(myDataTable)
' hashtable
for i=0 to textes.length-1
myHashTable.add(valeurs(i),textes(i))
next
end sub
' panel connection
sub bindToArray
' association with components
with DropDownList1
.DataSource=textes
.DataBind
end with
with ListBox1
.DataSource=textes
.DataBind
end with
with CheckBoxList1
.DataSource=textes
.DataBind
end with
with RadioButtonList1
.DataSource=textes
.DataBind
end with
' item selection
ListBox1.Items(1).Selected=true
ListBox1.Items(3).Selected=true
CheckBoxList1.Items(0).Selected=true
CheckBoxList1.Items(3).Selected=true
DropDownList1.SelectedIndex=2
RadioButtonList1.SelectedIndex=1
end sub
sub bindToArrayList
....
end sub
sub bindToDataTable
...
end sub
sub bindToDataSet
...
end sub
Os controlos são ligados aos dados aqui apenas na primeira solicitação. Depois disso, os controlos manterão os seus elementos através do mecanismo [VIEWSTATE]. A ligação é realizada no procedimento [bindToArray]. Uma vez que a fonte de dados é do tipo [Array], apenas o campo [DataSource] dos componentes [ListControl] é inicializado. O controlo é preenchido com valores da fonte de dados associada utilizando o método [ListControl].DataBind. Só então é que os objetos [ListControl] têm elementos. Pode então selecionar alguns deles.
8.3.3. Ligação a uma fonte de dados ArrayList
A fonte de dados [myDataList] é inicializada no procedimento [createDataSources]:
' global data
dim textes() as string={"un","deux","trois","quatre"}
dim myDataListe as new ArrayList
..
' procedure executed when the page is loaded
Sub page_Load(sender As Object, e As EventArgs)
if not IsPostBack then
' create the data sources to be linked to the components
createDataSources
...
end if
End Sub
sub createDataSources
' creates data sources to be linked to components
' arraylist
dim i as integer
for i=0 to textes.length-1
myDataListe.add(textes(i))
next
...
end sub
A ligação dos quatro controlos [ListBox] na ligação 2 é feita da seguinte forma no procedimento [bindToArrayList] do código do controlo:
' liaison arraylist
sub bindToArrayList
' l'association aux composants
with DropDownList2
.DataSource=myDataListe
.DataBind
end with
with ListBox2
.DataSource=myDataListe
.DataBind
end with
with CheckBoxList2
.DataSource=myDataListe
.DataBind
end with
with RadioButtonList2
.DataSource=myDataListe
.DataBind
end with
' la sélection des éléments
ListBox2.Items(1).Selected=true
ListBox2.Items(3).Selected=true
CheckBoxList2.Items(0).Selected=true
CheckBoxList2.Items(3).Selected=true
DropDownList2.SelectedIndex=2
RadioButtonList2.SelectedIndex=1
end sub
Uma vez que a fonte de dados é do tipo [ArrayList], apenas o campo [DataSource] dos componentes [ListControl] é inicializado.
8.3.4. Fonte de dados do tipo DataTable
A fonte de dados [myDataTable] é inicializada no procedimento [createDataSources]:
' global data
dim textes() as string={"un","deux","trois","quatre"}
dim myDataTable as new DataTable("table1")
...
' procedure executed when the page is loaded
Sub page_Load(sender As Object, e As EventArgs)
if not IsPostBack then
' create the data sources to be linked to the components
createDataSources
...
end if
End Sub
sub createDataSources
' creates data sources to be linked to components
...
' datatable
' we define its two columns
myDataTable.Columns.Add("id",Type.GetType("System.Int32"))
myDataTable.Columns.Add("texte",Type.GetType("System.String"))
' fill the table
dim ligne as DataRow
for i=0 to textes.length-1
ligne=myDataTable.NewRow
ligne("id")=i
ligne("texte")=textes(i)
myDataTable.Rows.Add(ligne)
next
...
end sub
Começamos por criar um objeto [DataTable] com duas colunas: [id] e [text]. A coluna [id] irá preencher o campo [Value] dos itens [ListControl], e a coluna [text] irá preencher os seus campos [Text]. O [DataTable] com duas colunas é criado da seguinte forma:
myDataTable.Columns.Add("id",Type.GetType("System.Int32"))
myDataTable.Columns.Add("texte",Type.GetType("System.String"))
Assim, criamos uma tabela com duas colunas:
- a primeira, chamada "id", é do tipo inteiro
- a segunda, chamada "text", é do tipo string
Agora que a estrutura da tabela foi criada, podemos preenchê-la com o seguinte código:
dim ligne as DataRow
for i=0 to textes.length-1
ligne=myDataTable.NewRow
ligne("id")=i
ligne("texte")=textes(i)
myDataTable.Rows.Add(ligne)
next
A coluna [id] conterá números inteiros [0,1,..,n], enquanto a coluna [text] conterá os valores da matriz [data]. Uma vez feito isto, a tabela [dataList] é preenchida. A ligação dos quatro controlos [ListBox] na ligação 3 é realizada da seguinte forma no procedimento [bindToDataTable] do código de controlo:
sub bindToDataTable
' l'association aux composants
with DropDownList3
.DataSource=myDataTable
.DataValueField="id"
.DataTextField="texte"
.DataBind
end with
with ListBox3
.DataSource=myDataTable
.DataValueField="id"
.DataTextField="texte"
.DataBind
end with
with CheckBoxList3
.DataSource=myDataTable
.DataValueField="id"
.DataTextField="texte"
.DataBind
end with
with RadioButtonList3
.DataSource=myDataTable
.DataValueField="id"
.DataTextField="texte"
.DataBind
end with
' la sélection des éléments
ListBox3.Items(1).Selected=true
ListBox3.Items(3).Selected=true
CheckBoxList3.Items(0).Selected=true
CheckBoxList3.Items(3).Selected=true
DropDownList3.SelectedIndex=2
RadioButtonList3.SelectedIndex=1
end sub
Cada componente [ListControl] é vinculado à fonte de dados [myDataTable] definindo o seguinte para cada um:
A tabela [myDataTable] é a fonte de dados. A coluna [id] desta tabela irá preencher os campos [Value] dos elementos do componente, enquanto a coluna [text] irá preencher os seus campos [Text].
8.3.5. Fonte de dados do tipo DataSet
A fonte de dados [myDataSet] é inicializada no procedimento [createDataSources]:
' global data
dim myDataTable as new DataTable("table1")
dim myDataSet as new DataSet
...
' procedure executed when the page is loaded
Sub page_Load(sender As Object, e As EventArgs)
if not IsPostBack then
' create the data sources to be linked to the components
createDataSources
...
end if
End Sub
sub createDataSources
' creates data sources to be linked to components
' dataset - a single table
myDataSet.Tables.Add(myDataTable)
...
end sub
Um objeto [DataSet] representa uma coleção de tabelas [DataTable]. Adicionamos a [myDataTable] criada anteriormente ao [DataSet]. A ligação dos quatro controlos [ListBox] na ligação 4 é feita da seguinte forma no procedimento [bindToDataSet] do código do controlo:
sub bindToDataSet
' l'association aux composants
with DropDownList4
.DataSource=myDataSet
.DataMember="table1"
.DataValueField="id"
.DataTextField="texte"
.DataBind
end with
with ListBox4
.DataSource=myDataSet
.DataMember="table1"
.DataValueField="id"
.DataTextField="texte"
.DataBind
end with
with CheckBoxList4
.DataSource=myDataSet
.DataMember="table1"
.DataValueField="id"
.DataTextField="texte"
.DataBind
end with
with RadioButtonList4
.DataSource=myDataSet
.DataMember="table1"
.DataValueField="id"
.DataTextField="texte"
.DataBind
end with
' la sélection des éléments
ListBox4.Items(1).Selected=true
ListBox4.Items(3).Selected=true
CheckBoxList4.Items(0).Selected=true
CheckBoxList4.Items(3).Selected=true
DropDownList4.SelectedIndex=2
RadioButtonList4.SelectedIndex=1
end sub
Cada componente [ListControl] está ligado à fonte de dados da seguinte forma:
O conjunto de dados [myDataSet] é a fonte de dados. Uma vez que pode conter várias tabelas, especificamos o nome da tabela a utilizar em [DataMember]. A coluna [id] desta tabela irá preencher os campos [Value] dos elementos do componente, enquanto a coluna [text] irá preencher os seus campos [Text].
8.3.6. Fonte de dados HashTable
A fonte de dados [myHashTable] é inicializada no procedimento [createDataSources]:
' global data
dim textes() as string={"un","deux","trois","quatre"}
dim valeurs() as string={"1","2","3","4"}
dim myHashTable as new HashTable
...
' procedure executed when the page is loaded
Sub page_Load(sender As Object, e As EventArgs)
if not IsPostBack then
' create the data sources to be linked to the components
createDataSources
...
end if
End Sub
sub createDataSources
' creates data sources to be linked to components
...
' hashtable
for i=0 to textes.length-1
myHashTable.add(valeurs(i),textes(i))
next
end sub
O dicionário [myHashTable] pode ser visto como uma tabela com duas colunas chamadas «key» e «value». A coluna [key] representa as chaves do dicionário e a coluna [value] representa os valores associados a elas. Aqui, a coluna [key] consiste no conteúdo da matriz [values] e a coluna [value] consiste no conteúdo da matriz [texts]. A ligação desta fonte aos controlos é realizada no procedimento [bindToHashTable]:
sub bindToHashTable
' l'association aux composants
with DropDownList5
.DataSource=myHashTable
.DataValueField="key"
.DataTextField="value"
.DataBind
end with
with ListBox5
.DataSource=myHashTable
.DataValueField="key"
.DataTextField="value"
.DataBind
end with
with CheckBoxList5
.DataSource=myHashTable
.DataValueField="key"
.DataTextField="value"
.DataBind
end with
with RadioButtonList5
.DataSource=myHashTable
.DataValueField="key"
.DataTextField="value"
.DataBind
end with
' la sélection des éléments
ListBox5.Items(1).Selected=true
ListBox5.Items(3).Selected=true
CheckBoxList5.Items(0).Selected=true
CheckBoxList5.Items(3).Selected=true
DropDownList5.SelectedIndex=2
RadioButtonList5.SelectedIndex=1
end sub
Para cada componente, a ligação é estabelecida utilizando as seguintes instruções:
A fonte de dados é o dicionário [myHashTable]. Os valores dos controlos são fornecidos pela coluna [key] do dicionário e o texto pela coluna [value]. Os elementos do dicionário são inseridos nos controlos pela ordem das chaves, que inicialmente é aleatória.
8.3.7. Diretivas de importação de namespaces
Vários namespaces são importados automaticamente para uma página ASP.NET. Este não é o caso de "System.Data", onde se encontram as classes [DataTable] e [DataSet]. Por conseguinte, esta classe deve ser importada. Isto é feito da seguinte forma:
<%@ Page Language="VB" %>
<%@ import Namespace="System.Data" %>
<script runat="server">
...
</script>
<html>
...
</html>
8.4. Componente DataGrid e ligação de dados
O componente [DataGrid] permite-lhe apresentar dados em formato de tabela, mas vai muito além da simples apresentação:
- oferece a capacidade de configurar com precisão a «apresentação visual» da tabela
- permite-lhe atualizar a fonte de dados
O componente [DataGrid] é simultaneamente poderoso e complexo. Iremos apresentá-lo passo a passo.
8.4.1. Exibição de uma fonte de dados do tipo Array, ArrayList, DataTable ou DataSet
O componente [DataGrid] permite-lhe apresentar fontes de dados do tipo [Array], [ArrayList], [DataTable] e [DataSet] numa tabela HTML. Para estes quatro tipos de dados, basta associar a fonte à propriedade [DataSource] do componente [DataGrid]:
uma fonte de dados [Array], [ArrayList], [DataTable], [DataSet], ... | |
se a fonte de dados for um [DataSet], representa o nome da tabela a ser utilizada como fonte de dados. A fonte de dados real é, então, uma tabela. Se este campo for deixado em branco, todas as tabelas no [DataSet] são apresentadas. |
Apresentamos agora a página [datagrid1.aspx], que mostra um [DataGrid] associado a quatro fontes de dados diferentes:

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

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

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

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

Selecionamos [color i] para o [DataGrid] #i. Estas escolhas de design refletem-se no código de apresentação da página:
<html>
<head>
</head>
<body>
<form runat="server">
<p>
Liaison de données avec un DataGrid
</p>
<hr />
<p>
<table>
<tbody>
</tbody>
</table>
<table border="1">
<tbody>
<tr>
<td>
Array</td>
<td>
ArrayList</td>
<td>
DataTable</td>
<td>
DataSet</td>
</tr>
<tr>
<td>
<asp:DataGrid id="DataGrid1" runat="server" ShowHeader="False" CellPadding="4" BackColor="White" BorderColor="#CC9966" BorderWidth="1px" BorderStyle="None">
<FooterStyle forecolor="#330099" backcolor="#FFFFCC"></FooterStyle>
<HeaderStyle font-bold="True" forecolor="#FFFFCC" backcolor="#990000"></HeaderStyle>
<PagerStyle horizontalalign="Center" forecolor="#330099" backcolor="#FFFFCC"></PagerStyle>
<SelectedItemStyle font-bold="True" forecolor="#663399" backcolor="#FFCC66"></SelectedItemStyle>
<ItemStyle forecolor="#330099" backcolor="White"></ItemStyle>
</asp:DataGrid>
</td>
<td>
<asp:DataGrid id="DataGrid2" runat="server" ShowHeader="False" CellPadding="4" BackColor="White" BorderColor="#3366CC" BorderWidth="1px" BorderStyle="None">
<FooterStyle forecolor="#003399" backcolor="#99CCCC"></FooterStyle>
<HeaderStyle font-bold="True" forecolor="#CCCCFF" backcolor="#003399"></HeaderStyle>
<PagerStyle horizontalalign="Left" forecolor="#003399" backcolor="#99CCCC" mode="NumericPages"></PagerStyle>
<SelectedItemStyle font-bold="True" forecolor="#CCFF99" backcolor="#009999"></SelectedItemStyle>
<ItemStyle forecolor="#003399" backcolor="White"></ItemStyle>
</asp:DataGrid>
</td>
<td>
<asp:DataGrid id="DataGrid3" runat="server" ShowHeader="False" CellPadding="3" BackColor="#DEBA84" BorderColor="#DEBA84" BorderWidth="1px" BorderStyle="None" CellSpacing="2">
<FooterStyle forecolor="#8C4510" backcolor="#F7DFB5"></FooterStyle>
<HeaderStyle font-bold="True" forecolor="White" backcolor="#A55129"></HeaderStyle>
<PagerStyle horizontalalign="Center" forecolor="#8C4510" mode="NumericPages"></PagerStyle>
<SelectedItemStyle font-bold="True" forecolor="White" backcolor="#738A9C"></SelectedItemStyle>
<ItemStyle forecolor="#8C4510" backcolor="#FFF7E7"></ItemStyle>
</asp:DataGrid>
</td>
<td>
<asp:DataGrid id="DataGrid4" runat="server" ShowHeader="False" CellPadding="3" BackColor="White" BorderColor="#E7E7FF" BorderWidth="1px" BorderStyle="None" GridLines="Horizontal">
<FooterStyle forecolor="#4A3C8C" backcolor="#B5C7DE"></FooterStyle>
<HeaderStyle font-bold="True" forecolor="#F7F7F7" backcolor="#4A3C8C"></HeaderStyle>
<PagerStyle horizontalalign="Right" forecolor="#4A3C8C" backcolor="#E7E7FF" mode="NumericPages"></PagerStyle>
<SelectedItemStyle font-bold="True" forecolor="#F7F7F7" backcolor="#738A9C"></SelectedItemStyle>
<AlternatingItemStyle backcolor="#F7F7F7"></AlternatingItemStyle>
<ItemStyle forecolor="#4A3C8C" backcolor="#E7E7FF"></ItemStyle>
</asp:DataGrid>
</td>
</tr>
</tbody>
</table>
</p>
<asp:Button id="Button1" runat="server" Text="Envoyer"></asp:Button>
</form>
</body>
</html>
No modo de design, definimos apenas propriedades de formatação. É no código de controlo que associamos dados aos quatro componentes:
<%@ Page Language="VB" %>
<%@ import Namespace="system.data" %>
<script runat="server">
' global data
dim textes1() as string={"un","deux","trois","quatre"}
dim textes2() as string={"one","two","three","for"}
dim valeurs() as string={"1","2","3","4"}
dim myDataListe as new ArrayList
dim myDataTable as new DataTable("table1")
dim myDataSet as new DataSet
' procedure executed when the page is loaded
Sub page_Load(sender As Object, e As EventArgs)
if not IsPostBack then
' create the data sources to be linked to the components
createDataSources
' link to an array [Array]
bindToArray
' link to a list [ArrayList]
bindToArrayList
' link to a table [DataTable]
bindToDataTable
' link to a data group [DataSet]
bindToDataSet
end if
End Sub
sub createDataSources
' creates data sources to be linked to components
' arraylist
dim i as integer
for i=0 to textes1.length-1
myDataListe.add(textes1(i))
next
' datatable
' we define its two columns
myDataTable.Columns.Add("id",Type.GetType("System.Int32"))
myDataTable.Columns.Add("texte1",Type.GetType("System.String"))
myDataTable.Columns.Add("texte2",Type.GetType("System.String"))
' fill the table
dim ligne as DataRow
for i=0 to textes1.length-1
ligne=myDataTable.NewRow
ligne("id")=valeurs(i)
ligne("texte1")=textes1(i)
ligne("texte2")=textes2(i)
myDataTable.Rows.Add(ligne)
next
' dataset - a single table
myDataSet.Tables.Add(myDataTable)
end sub
' panel connection
sub bindToArray
with DataGrid1
.DataSource=textes1
.DataBind
end with
end sub
' arraylist link
sub bindToArrayList
with DataGrid2
.DataSource=myDataListe
.DataBind
end with
end sub
' datatable link
sub bindToDataTable
with DataGrid3
.DataSource=myDataTable
.DataBind
end with
end sub
' dataset link
sub bindToDataSet
with DataGrid4
.DataSource=myDataSet
.DataBind
end with
end sub
</script>
<html>
...
</html>
O código é bastante semelhante ao do exemplo anterior, pelo que não o comentaremos especificamente. Note, no entanto, como é feita a ligação de dados. Para cada um dos quatro controlos, a seguinte sequência é suficiente:
onde [data source] é do tipo [Array], [ArrayList], [DataTable] ou [DataSet]. Podemos ver, portanto, que esta é uma ferramenta poderosa para apresentar dados em tabelas:
- O layout é criado utilizando o [WebMatrix] ou qualquer outro IDE durante a fase de design
- A ligação de dados é feita no código. Com o [WebMatrix], pode ser feita em tempo de design se a fonte de dados for uma base de dados SQL Server ou uma base de dados Access. Neste caso, um componente [SqlDataSource] ou [AccessDataSource] é colocado no formulário. Este componente pode ser ligado em tempo de design à fonte de dados física, seja uma base de dados SQL Server ou uma base de dados Access, conforme apropriado. Se atribuir um objeto [SqlDataSource] ou [AccessDataSource] ligado a uma fonte física à propriedade [DataSource] de um componente [DataGrid], os dados reais aparecerão no componente [DataGrid] no modo de design.
8.5. ViewState dos componentes de lista de dados
Aqui, pretendemos destacar o mecanismo [VIEWSTATE] para componentes de lista de dados. Pode hesitar entre dois métodos para manter o estado de um componente de lista de dados entre duas solicitações do cliente:
- definir o seu atributo [VIEWSTATE] como true
- definir o seu atributo [VIEWSTATE] como false e armazenar a sua fonte de dados na sessão, para que o componente possa ser ligado a essa fonte durante a próxima solicitação.
O exemplo seguinte ilustra certos aspetos do mecanismo [VIEWSTATE] para contentores de dados. Na primeira solicitação do cliente, a aplicação apresenta a seguinte vista:
![]() |
N.º | nome | tipo | propriedades | função |
1 | DataGrid | EnableViewState=true | exibe uma fonte de dados S | |
2 | DataGrid | EnableViewState=true | exibe a mesma fonte de dados S que [DataGrid1] | |
3 | Botão | EnableViewState=false | Botão [submit] |
O componente [DataGrid1] é mantido pelo mecanismo [VIEWSTATE]. Queremos determinar se este mecanismo, que regenera a apresentação do [DataGrid1] a cada pedido, também regenera a sua fonte de dados. Para tal, a fonte de dados é ligada ao componente [DataGrid2]. A sua geração a cada pedido é realizada através de uma ligação explícita à fonte de dados do [DataGrid1]. O seu atributo [EnableViewState] também é definido como [true].
O código de apresentação [main.aspx] da aplicação é o seguinte:
<%@ page src="main.aspx.vb" inherits="main" autoeventwireup="false" %>
<HTML>
<HEAD>
<title></title>
</HEAD>
<body>
<form runat="server">
<table>
<tr>
<td align="center">DataGrid 1</td>
<td align="center">
DataGrid 2</td>
</tr>
<tr>
<td>
<asp:DataGrid id="DataGrid1" runat="server" ...>
<SelectedItemStyle ...></SelectedItemStyle>
....
</asp:DataGrid></td>
<td>
<asp:DataGrid id="Datagrid2" runat="server" ...>
....
</asp:DataGrid></td>
</tr>
</table>
<asp:Button id="Button1" runat="server" Text="Envoyer"></asp:Button>
</form>
</body>
</HTML>
O controlador [main.aspx.vb] é o seguinte:
Imports System.Data
Public Class main
Inherits System.Web.UI.Page
Protected WithEvents DataGrid1 As System.Web.UI.WebControls.DataGrid
Protected WithEvents Datagrid2 As System.Web.UI.WebControls.DataGrid
Protected WithEvents Button1 As System.Web.UI.WebControls.Button
Private Sub Page_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
' on the 1st query, the data source is defined and linked to the 1st datagrid
If Not IsPostBack Then
'define data source
With DataGrid1
.DataSource = createDataSource()
.DataBind()
End With
End If
' for each query, datagrid2 is linked to the source of the 1st datagrid
With Datagrid2
.DataSource = DataGrid1.DataSource
.DataBind()
End With
End Sub
Private Function createDataSource() As DataTable
' initialize the data source
Dim thèmes As New DataTable
' columns
With thèmes.Columns
.Add("id", GetType(System.Int32))
.Add("thème", GetType(System.String))
.Add("description", GetType(System.String))
End With
' column id will be primary key
thèmes.Constraints.Add("cléprimaire", thèmes.Columns("id"), True)
' lines
Dim ligne As DataRow
For i As Integer = 0 To 4
ligne = thèmes.NewRow
ligne.Item("id") = i.ToString
ligne.Item("thème") = "thème" + i.ToString
ligne.Item("description") = "description du thème " + i.ToString
thèmes.Rows.Add(ligne)
Next
Return thèmes
End Function
Private Sub InitializeComponent()
End Sub
End Class
O método [createDataSource] cria uma fonte de dados S do tipo [DataTable]. Não nos deteremos no seu código, uma vez que não é o foco deste exemplo. Este é o método utilizado para construir os dois componentes [DataGrid] que nos interessam:
- o componente [DataGrid1] é vinculado à tabela S uma vez, durante a primeira consulta. A partir daí, deixa de estar vinculado.
- O componente [DataGrid2] é vinculado à fonte [DataGrid1.DataSource] a cada nova consulta.
Durante a primeira consulta, obtemos a seguinte visualização:

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

Observamos que o componente [DataGrid1] manteve o seu valor, mas o componente [DataGrid2] não. Explicação:
- Mesmo antes do procedimento [Page_Load] iniciar, os objetos [DataGrid1] e [DataGrid2] já recuperaram os valores que tinham durante o pedido anterior, devido ao mecanismo [viewstate]. Na verdade, ambos têm a sua propriedade [EnableViewState] definida como [true].
- O procedimento [Page_Load] é executado. Uma vez que se trata de uma operação [PostBack], o componente [DataGrid1] não é modificado pelo [Page_Load] (ver código). Por conseguinte, mantém o valor recuperado através do [viewstate]. É isto que o ecrã acima mostra.
- O componente [DataGrid2], por outro lado, está vinculado via [DataBind] à fonte de dados [DataGrid1.DataSource]. Por isso, é reconstruído, e o valor que acabara de recuperar através do [viewstate] é perdido. Teria sido, portanto, vantajoso definir a sua propriedade [EnableViewState] como [false] para evitar uma gestão desnecessária do estado. O ecrã acima mostra que o [DataGrid2] foi vinculado a uma fonte vazia. Uma vez que esta fonte é [DataGrid1.DataSource], podemos concluir que, embora o mecanismo [viewstate] restaure com sucesso a exibição do componente [DataGrid1], não restaura as suas propriedades, tais como [DataSource].
O que podemos concluir deste exemplo? Deve evitar definir a propriedade [EnableViewState] de um contentor de dados como [true] se este precisar de ser vinculado (DataBind) a uma fonte de dados em cada pedido. No entanto, existem certos casos em que, mesmo neste cenário, a propriedade [EnableViewState] do contentor deve permanecer definida como [true]; caso contrário, os eventos que pretende tratar não serão acionados. Encontraremos um exemplo disso mais adiante.
Frequentemente, a fonte de dados de um contentor de dados muda ao longo das solicitações. Portanto, o contentor deve ser vinculado à fonte de dados em cada solicitação. É comum que a fonte de dados tenha o escopo da sessão para que as solicitações possam aceder a ela. O nosso segundo exemplo demonstra este mecanismo. A aplicação fornece apenas uma única vista:
![]() |
N.º | nome | tipo | propriedades | função |
1 | DataGrid | EnableViewState=true | exibe uma fonte de dados S | |
2 | DataGrid | EnableViewState=false | exibe a mesma fonte de dados S que [DataGrid1] | |
3 | Botão | EnableViewState=false | Botão [submit] | |
4 | Rótulo | EnableViewState=false | texto informativo |
O componente [DataGrid1] é vinculado aos dados apenas durante o primeiro pedido. Ele manterá o seu valor entre pedidos graças ao mecanismo [viewstate]. O componente [DataGrid2] é vinculado a uma fonte de dados a cada pedido, à qual é adicionado um item a cada novo pedido. Portanto, o componente [DataGrid2] deve ser vinculado (DataBind) a cada pedido. Por isso, definimos o seu atributo [EnableViewState] como [false], tal como recomendado anteriormente. Assim, durante a segunda solicitação (usando o botão [Submit]), obtemos a seguinte resposta:

O componente [DataGrid1] manteve o seu valor inicial. O componente [DataGrid2] tem mais um item. Os três valores [1,2,2] representam o número da consulta. Podemos ver que um dos valores está incorreto. Vamos tentar perceber porquê.
O código de apresentação da aplicação [main.aspx] é o seguinte:
<%@ Page src="main.aspx.vb" inherits="main" autoeventwireup="false" Language="vb" %>
<HTML>
<HEAD>
<title></title>
</HEAD>
<body>
<form runat="server">
<table>
<tr>
<td align="center" bgColor="#ccffcc">DataGrid 1</td>
<td align="center" bgColor="#ffff99">DataGrid 2</td>
</tr>
<tr>
<td vAlign="top">
<asp:DataGrid id="DataGrid1" runat="server" ...>
...
</asp:DataGrid>
</td>
<td vAlign="top">
<asp:DataGrid id="Datagrid2" runat="server" ... EnableViewState="False">
....
</asp:DataGrid>
</td>
</tr>
</table>
<P>Numéro de requête :
<asp:Label id="lblInfo1" runat="server" EnableViewState="False"></asp:Label>,
<asp:Label id="lblInfo2" runat="server" EnableViewState="False"></asp:Label>,
<asp:Label id="lblInfo3" runat="server" EnableViewState="False"></asp:Label></P>
<P>
<asp:Button id="Button1" runat="server" Text="Envoyer" EnableViewState="False"></asp:Button></P>
</form>
</body>
</HTML>
O código do controlador [main.aspx.vb] é o seguinte:
Imports System.Data
Imports System
Public Class main
Inherits System.Web.UI.Page
Protected WithEvents DataGrid1 As System.Web.UI.WebControls.DataGrid
Protected WithEvents Datagrid2 As System.Web.UI.WebControls.DataGrid
Protected WithEvents Button1 As System.Web.UI.WebControls.Button
Protected WithEvents lblInfo1 As System.Web.UI.WebControls.Label
Protected WithEvents lblInfo2 As System.Web.UI.WebControls.Label
Protected WithEvents lblInfo3 As System.Web.UI.WebControls.Label
Dim dtThèmes As DataTable
Dim numRequête1 As Integer
Dim numRequête2 As Integer
Dim numRequête3 As New entier
Private Sub Page_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
' on the 1st query, the data source is defined and linked to the 1st datagrid
If Not IsPostBack Then
'define data source
dtThèmes = createDataSource()
With DataGrid1
.DataSource = dtThèmes
.DataBind()
End With
' store information in the session
Session("source") = dtThèmes
numRequête1 = 0 : Session("numRequête1") = numRequête1
numRequête2 = 0 : Session("numRequête2") = numRequête2
numRequête3.valeur = 0 : Session("numRequête3") = numRequête3
End If
' a new theme is added to each query
dtThèmes = CType(Session("source"), DataTable)
Dim nbThèmes = dtThèmes.Rows.Count
Dim ligne As DataRow = dtThèmes.NewRow
With ligne
.Item("id") = nbThèmes.ToString
.Item("thème") = "thème" + nbThèmes.ToString
.Item("description") = "description du thème " + nbThèmes.ToString
End With
dtThèmes.Rows.Add(ligne)
'links datagrid2 with the data source
With Datagrid2
.DataSource = dtThèmes
.DataBind()
End With
' info no. of requests
numRequête1 = CType(Session("numRequête1"), Integer)
numRequête1 += 1
lblInfo1.Text = numRequête1.ToString
numRequête2 = CType(Session("numRequête2"), Integer)
numRequête2 += 1
lblInfo2.Text = numRequête2.ToString
numRequête3 = CType(Session("numRequête3"), entier)
numRequête3.valeur += 1
lblInfo3.Text = numRequête3.valeur.ToString
' store some information in the session
Session("numRequête2") = numRequête2
End Sub
Private Function createDataSource() As DataTable
....
End Function
End Class
Public Class entier
Private _valeur As Integer
Public Property valeur() As Integer
Get
Return _valeur
End Get
Set(ByVal Value As Integer)
_valeur = Value
End Set
End Property
End Class
Não reproduzimos o código do método [createDataSource]. É o mesmo da aplicação anterior, exceto que apenas três linhas estão incluídas no código-fonte. Vejamos primeiro como a fonte de dados e os dois contentores são geridos:
Private Sub Page_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
' on the 1st query, the data source is defined and linked to the 1st datagrid
If Not IsPostBack Then
'define data source
dtThèmes = createDataSource()
With DataGrid1
.DataSource = dtThèmes
.DataBind()
End With
' store information in the session
Session("source") = dtThèmes
...
End If
' a new theme is added to each query
dtThèmes = CType(Session("source"), DataTable)
Dim nbThèmes = dtThèmes.Rows.Count
Dim ligne As DataRow = dtThèmes.NewRow
With ligne
.Item("id") = nbThèmes.ToString
.Item("thème") = "thème" + nbThèmes.ToString
.Item("description") = "description du thème " + nbThèmes.ToString
End With
dtThèmes.Rows.Add(ligne)
'links datagrid2 with the data source
With Datagrid2
.DataSource = dtThèmes
.DataBind()
End With
...
End Sub
O componente [DataGrid1] é vinculado à fonte de dados S apenas durante a primeira consulta (não IsPostBack). Em seguida, é colocado na sessão. Não será colocado lá novamente a partir daí. A fonte de dados S colocada na sessão é recuperada a cada solicitação, e uma nova linha é adicionada a ela. O componente [DataGrid2] é explicitamente vinculado à fonte S a cada solicitação. É por isso que o seu conteúdo aumenta em uma linha a cada solicitação. Observe que, após modificar o conteúdo da fonte S, ela não é explicitamente colocada de volta na sessão por meio de uma operação:
Porquê? Quando uma consulta é iniciada, a sessão contém um objeto Session("source") do tipo [DataTable], que é a fonte de dados tal como estava durante a última consulta. Vamos chamar ao objeto Session("source") de S. Quando escrevemos:
dtThèmes = CType(Session("source"), DataTable)
[dtThèmes] e [S] são duas referências ao mesmo objeto [DataTable]. Assim, quando o código em [Page_Load] adiciona um elemento à tabela referenciada por [dtThèmes], adiciona-o simultaneamente à tabela referenciada por [S]. No final da execução da página, todos os objetos da sessão serão guardados, incluindo o objeto Session("source"), ou seja, S, ou seja, [dtThemes]. Assim, é de facto o novo conteúdo da fonte de dados que é guardado. Não havia necessidade de escrever:
para realizar este salvamento, porque Session("source") já é igual a [dtThèmes]. Isto deixa de ser verdade quando os dados colocados na sessão não são objetos, tais como as estruturas [Integer, Float, ...]. Isto é demonstrado pela gestão do contador de consultas:
Dim numRequête1 As Integer
Dim numRequête2 As Integer
Dim numRequête3 As New entier
Private Sub Page_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
' on the 1st query, the data source is defined and linked to the 1st datagrid
....
' store information in the session
Session("source") = dtThèmes
numRequête1 = 0 : Session("numRequête1") = numRequête1
numRequête2 = 0 : Session("numRequête2") = numRequête2
numRequête3.valeur = 0 : Session("numRequête3") = numRequête3
End If
' a new theme is added to each query
....
' info no. of requests
numRequête1 = CType(Session("numRequête1"), Integer)
numRequête1 += 1
lblInfo1.Text = numRequête1.ToString
numRequête2 = CType(Session("numRequête2"), Integer)
numRequête2 += 1
lblInfo2.Text = numRequête2.ToString
numRequête3 = CType(Session("numRequête3"), entier)
numRequête3.valeur += 1
lblInfo3.Text = numRequête3.valeur.ToString
' store some information in the session
Session("numRequête2") = numRequête2
End Sub
Private Function createDataSource() As DataTable
....
End Function
Private Sub InitializeComponent()
End Sub
End Class
Public Class entier
Private _valeur As Integer
Public Property valeur() As Integer
Get
Return _valeur
End Get
Set(ByVal Value As Integer)
_valeur = Value
End Set
End Property
Armazenamos os contadores de pedidos em três elementos:
- numRequest1 e numRequest2 do tipo [Integer] — [Integer] não é uma classe, mas uma estrutura
- numRequest3 do tipo [integer] - [integer] é uma classe definida para este fim
Quando escrevemos:
numRequête1 = CType(Session("numRequête1"), Integer)
..
numRequête2 = CType(Session("numRequête2"), Integer)
..
numRequête3 = CType(Session("numRequête3"), entier)
..
- A estrutura [Session("numRequest1")] é copiada para [numRequest1]. Assim, quando o elemento [numRequest1] é modificado, o próprio elemento [Session("numRequest1")] não é
- O mesmo se aplica a [Session("numRequest2")] e [numRequest2]
- Os elementos [Session("numRequête3")] e [numRequête3] são ambos referências ao mesmo objeto do tipo [integer]. O objeto referenciado pode ser modificado por qualquer uma das referências.
A partir disto, podemos concluir que é desnecessário escrever:
para armazenar o novo valor de [numRequest3] na sessão. Em vez disso, deve escrever:
para armazenar os novos valores das estruturas [numRequête1] e [numRequête2]. Fazemos isto apenas para [numRequête2], o que explica por que razão, na captura de ecrã obtida após a segunda consulta, o contador [numRequête1] está incorreto.
Devemos, portanto, notar que, uma vez que os dados tenham sido adicionados a uma sessão, não precisam de ser adicionados repetidamente se forem representados por um objeto. Nos outros casos, devem ser adicionados se tiverem sido modificados.
8.6. Exibir uma lista de dados utilizando um DataGrid paginado e ordenado
O componente [DataGrid] permite-lhe apresentar o conteúdo de um [DataSet]. Até agora, sempre construímos os nossos [DataSets] «manualmente». Desta vez, estamos a utilizar um [DataSet] proveniente de uma base de dados. Estamos a construir a seguinte aplicação MVC:
![]() |
As três vistas serão incorporadas no código de apresentação do controlador [main.aspx] como contentores. Por conseguinte, esta aplicação tem uma única página, [main.aspx].
8.6.1. As classes de negócio
A classe [products] fornece acesso à seguinte base de dados ACCESS:

O código para a classe [products] é o seguinte:
Imports System
Imports System.Data
Imports System.Data.OleDb
Imports System.Xml
Namespace st.istia.univangers.fr
Public Class produits
Private chaineConnexionOLEDB As String
Public Sub New(ByVal chaineConnexionOLEDB As String)
' save the connection string
Me.chaineConnexionOLEDB = chaineConnexionOLEDB
End Sub
Public Function getDataSet(ByVal commande As String) As DataSet
' create a DataAdapter object to read data from source OLEDB
Dim adaptateur As New OleDbDataAdapter(commande, chaineConnexionOLEDB)
' create a memory image of the select result
Dim contenu As New DataSet
Try
adaptateur.Fill(contenu)
Catch e As Exception
Throw New Exception("Erreur d'accès à la base de données (" + e.Message + ")")
End Try
' we return the result
Return contenu
End Function
End Class
End Namespace
A base de dados ACCESS será gerida através de um controlador OLEDB. Fornecemos ao construtor da classe [products] a cadeia de ligação, o controlador OLEDB e a base de dados a ser gerida. A classe [products] possui um método [getDataSet] que retorna um [DataSet] obtido através da execução de uma consulta SQL [select], cujo texto é passado como parâmetro. Durante o método, ocorrem várias operações: estabelecer a ligação à base de dados, executar a consulta [select] e encerrar a ligação. Tudo isto pode gerar uma exceção, que é tratada aqui. Um programa de teste poderia ter o seguinte aspeto:
Option Explicit On
Option Strict On
' namespaces
Imports System
Imports System.Data
Imports Microsoft.VisualBasic
Namespace st.istia.univangers.fr
' test pg
Module testproduits
Sub Main(ByVal arguments() As String)
' displays the contents of a product table
' the table is in a ACCESS database whose pg receives the file name
Const syntaxe1 As String = "pg bdACCESS"
' checking program parameters
If arguments.Length <> 1 Then
' error msg
Console.Error.WriteLine(syntaxe1)
' end
Environment.Exit(1)
End If
' prepare the connection chain
Dim chaineConnexion As String = "Provider=Microsoft.Jet.OLEDB.4.0; Ole DB Services=-4; Data Source=" + arguments(0)
' creation of a product object
Dim objProduits As produits = New produits(chaineConnexion)
' retrieve the product table from a dataset
Dim contenu As DataSet
Try
contenu = objProduits.getDataSet("select id,nom,prix from liste")
Catch ex As Exception
Console.Error.WriteLine(("L'erreur suivante s'est produite : " + ex.Message))
Environment.Exit(2)
End Try
' display its contents
Dim lignes As DataRowCollection = contenu.Tables(0).Rows
For i As Integer = 0 To lignes.Count - 1
' table line i
Console.Out.WriteLine(lignes(i).Item("id").ToString + "," + lignes(i).Item("nom").ToString + _
"," + lignes(i).Item("prix").ToString)
Next
End Sub
End Module
End Namespace
Os vários componentes desta aplicação são compilados da seguinte forma:
dos>vbc /t:library /r:system.dll /r:system.data.dll /r:system.xml.dll produits.vb
dos>vbc /r:produits.dll /r:system.data.dll /r:system.xml.dll /r:system.dll testproduits.vb
dos>dir
06/05/2004 16:52 118 784 produits.mdb
07/05/2004 11:07 902 produits.vb
07/04/2004 07:01 1 532 testproduits.vb
07/05/2004 14:21 3 584 produits.dll
07/05/2004 14:22 4 608 testproduits.exe
dos>testproduits produits.mdb
1,produit1,10
2,produit2,20
3,produit3,30
8.6.2. As Visualizações
Agora que temos a classe de acesso aos dados, vamos escrever os controladores e as vistas para esta aplicação web. Vamos primeiro ver como funciona. A primeira vista é a seguinte:
![]() |
N.º | nome | tipo | propriedades | função |
1 | Caixa de Texto | EnableViewState=true | campo de entrada de consulta de seleção | |
2 | Validador de campo obrigatório | EnableViewState=false | verifica a presença de 1 | |
3 | TextBox | EnableViewState=true | campo de entrada - especifica o número de linhas de dados a apresentar por página de resultados | |
4 | Validador de campo obrigatório | 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 [form]. Ela permite ao utilizador executar uma consulta SQL SELECT na base de dados [products.mdb]. A execução da consulta cria uma nova vista:
![]() |
N.º | nome | tipo | propriedades | função |
1 | Rótulo | EnableViewState=false | campo de informação | |
2 | Botão de opção | EnableViewState=false GroupName=rdSort | permite-lhe escolher uma ordem de classificação | |
3 | DataGrid | EnableViewState=true Permitir paginação=true PermitirOrdenação=true | tabela que apresenta os resultados da seleção | |
4 | LinkButton | EnableViewState=false | [submit] |
Chamaremos a esta vista a vista [results]. Ela contém o [DataGrid] que exibirá os resultados da instrução SQL SELECT. O utilizador pode cometer um erro na sua consulta. Alguns erros serão-lhe comunicados na vista [errors] graças aos controlos de validação.

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

A vista [errors] é apresentada:
![]() |
N.º | nome | tipo | propriedades | função |
1 | variável | Código HTML necessário para exibir erros | ||
3 | LinkButton | EnableViewState=false | Botão [submit] |
As três vistas da aplicação são três contentores (painéis) diferentes dentro da mesma página [main.aspx]. O seu código de apresentação é o seguinte:
<%@ Page src="main.aspx.vb" inherits="main" autoeventwireup="false" Language="vb" %>
<HTML>
<HEAD>
</HEAD>
<body>
<P>Liaison de données avec un DataGrid</P>
<HR width="100%" SIZE="1">
<form runat="server">
<asp:panel id="vueFormulaire" runat="server">
<P>Commande [select] à exécuter sur la table LISTE</P>
<P> 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. Configurar o DataGrid
Vamos analisar mais detalhadamente a paginação do componente [DataGrid], uma funcionalidade com a qual nos deparamos pela primeira vez. No código acima, esta paginação é controlada pelos seguintes atributos:
<asp:DataGrid id="DataGrid1" runat="server" PageSize="4" AllowPaging="True" ...>
...
<PagerStyle NextPageText="Suivant" PrevPageText="Précédent" ...></PagerStyle>
</asp:DataGrid></P>
ativa a paginação | |
quatro linhas de dados por página | |
O texto do link para ir para a página seguinte da fonte de dados | |
O texto do link para ir para a página anterior da fonte de dados |
Esta informação pode ser introduzida diretamente nos atributos da tag <asp:datagrid>. Também pode utilizar o [WebMatrix]. Na janela de propriedades [DataGrid], clique na ligação [Gerador de Propriedades]:

Aparece o seguinte assistente:

Selecione a opção [Pagination]:

Acima, vemos os valores dos atributos de paginação para o [DataGrid] no código de apresentação.
Além disso, ativamos a ordenação dos dados numa das colunas do [DataGrid]. Existem várias formas de o fazer. Uma delas consiste em definir a propriedade [AllowSorting=true] na janela de propriedades do [DataGrid]. Também é possível utilizar o gerador de propriedades. Independentemente do método utilizado, isto resulta na presença do atributo [AllowSorting=true] na tag <asp:DataGrid> do componente:
<asp:DataGrid id="DataGrid1" runat="server" ... AllowPaging="True" PageSize="4" AllowSorting="True">
8.6.4. Os controladores
O controlador [global.asax, global.asax.vb] é o seguinte:
[global.asax]
[global.asax.vb]
Imports st.istia.univangers.fr
Imports System.Configuration
Public Class Global
Inherits System.Web.HttpApplication
Sub Application_Start(ByVal sender As Object, ByVal e As EventArgs)
' create a product object
Dim objProduits As produits
Try
objProduits = New produits(ConfigurationSettings.AppSettings("OLEDBStringConnection"))
' put the object in the application
Application("objProduits") = objProduits
' no error
Application("erreur") = False
Catch ex As Exception
'there has been an error, we note it in the application
Application("erreur") = True
Application("message") = ex.Message
End Try
End Sub
End Class
Quando a aplicação é iniciada (Application_Start), criamos um objeto [products] e armazenamo-lo na aplicação para que esteja disponível para todos os pedidos de todos os clientes. Se ocorrer uma exceção durante esta criação, ela é registada na aplicação. O procedimento [Application_Start] será executado apenas uma vez. Depois disso, o controlador [global.asax] deixará de estar envolvido. O controlador [main.aspx.vb] tratará então do resto:
Imports System.Collections
Imports Microsoft.VisualBasic
Imports System.Data
Imports st.istia.univangers.fr
Imports System
Imports System.Xml
Public Class main
Inherits System.Web.UI.Page
' components page
Protected WithEvents txtSelect As System.Web.UI.WebControls.TextBox
Protected WithEvents RequiredFieldValidator1 As System.Web.UI.WebControls.RequiredFieldValidator
Protected WithEvents txtPages As System.Web.UI.WebControls.TextBox
Protected WithEvents RequiredFieldValidator2 As System.Web.UI.WebControls.RequiredFieldValidator
Protected WithEvents RangeValidator1 As System.Web.UI.WebControls.RangeValidator
Protected WithEvents btnExécuter As System.Web.UI.WebControls.Button
Protected WithEvents vueFormulaire As System.Web.UI.WebControls.Panel
Protected WithEvents lblSelect As System.Web.UI.WebControls.Label
Protected WithEvents DataGrid1 As System.Web.UI.WebControls.DataGrid
Protected WithEvents lnkRésultats As System.Web.UI.WebControls.LinkButton
Protected WithEvents vueRésultats As System.Web.UI.WebControls.Panel
Protected WithEvents lnkErreurs As System.Web.UI.WebControls.LinkButton
Protected WithEvents vueErreurs As System.Web.UI.WebControls.Panel
Protected WithEvents rdCroissant As System.Web.UI.WebControls.RadioButton
Protected WithEvents rdDécroissant As System.Web.UI.WebControls.RadioButton
' data page
Protected erreursHTML As String
Private Sub Page_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
' check for application errors
If CType(Application("erreur"), Boolean) Then
' the application has not initialized correctly
Dim erreurs As New ArrayList
erreurs.Add("Application momentanément indisponible (" + CType(Application("message"), String) + ")")
afficheErreurs(erreurs, False)
Exit Sub
End If
'1st request
If Not IsPostBack Then
' the empty form is displayed
afficheFormulaire()
End If
End Sub
Private Sub afficheErreurs(ByVal erreurs As ArrayList, ByVal afficheLien As Boolean)
' displays the error view
erreursHTML = ""
For i As Integer = 0 To erreurs.Count - 1
erreursHTML += "<li>" + erreurs(i).ToString + "</li>" + ControlChars.CrLf
Next
lnkErreurs.Visible = afficheLien
' the [errors] view is displayed
vueErreurs.Visible = True
vueFormulaire.Visible = False
vueRésultats.Visible = False
End Sub
Private Sub afficheFormulaire()
' the [form] view is displayed
vueFormulaire.Visible = True
vueErreurs.Visible = False
vueRésultats.Visible = False
End Sub
Private Sub afficheRésultats(ByVal sqlTexte As String, ByVal données As DataView)
' initialize controls
lblSelect.Text = sqlTexte
With DataGrid1
.DataSource = données
.PageSize = CType(txtPages.Text, Integer)
.CurrentPageIndex = 0
.DataBind()
End With
' the [results] view is displayed
vueRésultats.Visible = True
vueFormulaire.Visible = False
vueErreurs.Visible = False
End Sub
Private Sub btnExécuter_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnExécuter.Click
' valid page?
If Not Page.IsValid Then
afficheFormulaire()
Exit Sub
End If
' execute query SELECT customer
Dim données As DataView
Try
données = CType(Application("objProduits"), produits).getDataSet(txtSelect.Text.Trim).Tables(0).DefaultView
Catch ex As Exception
Dim erreurs As New ArrayList
erreurs.Add("erreur d'accès à la base de données (" + ex.Message + ")")
afficheErreurs(erreurs, True)
Exit Sub
End Try
' all's well - the results are in
afficheRésultats(txtSelect.Text.Trim, données)
' put the data in the session
Session("données") = données
End Sub
Private Sub retourFormulaire(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles lnkErreurs.Click, lnkRésultats.Click
' set of views
vueErreurs.Visible = False
vueFormulaire.Visible = True
vueRésultats.Visible = False
End Sub
Private Sub DataGrid1_PageIndexChanged(ByVal source As Object, ByVal e As System.Web.UI.WebControls.DataGridPageChangedEventArgs) Handles DataGrid1.PageIndexChanged
' change page
With DataGrid1
.CurrentPageIndex = e.NewPageIndex
.DataSource = CType(Session("données"), DataView)
.DataBind()
End With
End Sub
Private Sub DataGrid1_SortCommand(ByVal source As Object, ByVal e As System.Web.UI.WebControls.DataGridSortCommandEventArgs) Handles DataGrid1.SortCommand
' sort the dataview
Dim données As DataView = CType(Session("données"), DataView)
données.Sort = e.SortExpression + " " + CType(IIf(rdCroissant.Checked, "asc", "desc"), String)
' we display it
With DataGrid1
.CurrentPageIndex = 0
.DataSource = données
.DataBind()
End With
End Sub
End Class
Quando a página carrega [Page_Load], verificamos primeiro se a aplicação conseguiu inicializar corretamente. Caso contrário, exibimos a vista [errors] sem um link de retorno para o formulário, uma vez que esse link se torna desnecessário. Na verdade, apenas a vista [errors] pode ser exibida se a aplicação não conseguiu inicializar corretamente. Caso contrário, exibimos a vista [form] se esta for a primeira solicitação do cliente. Quanto ao resto, deixamos ao leitor a tarefa de compreender o código. Vamos concentrar-nos apenas em três procedimentos: o procedimento [btnExecute_Click], que é executado quando o utilizador solicita a execução da consulta SQL introduzida na vista [form], o procedimento [DataGrid1_PageIndexChanged], que é executado quando o utilizador utiliza os links [Next] e [Previous] no [DataGrid], e o procedimento [DataGrid1_SortCommand], que é executado quando o utilizador clica num cabeçalho de coluna para ordenar os dados nessa ordem. A ordem de ordenação — ascendente ou descendente — é determinada pelos dois botões de opção de ordenação.
No procedimento [btnExécuter_Click], começamos, portanto, por verificar se a página é válida ou não. Quando o procedimento [btnExécuter_Click] é executado, as verificações relacionadas com os vários controlos de validação da página já foram realizadas. Para cada controlo de validação, foram definidos dois atributos:
definido como true se os dados verificados forem válidos, false caso contrário | |
a mensagem de erro se os dados verificados forem inválidos |
Para a própria página, foi definido um atributo [IsValid]. Este é verdadeiro apenas se todos os controlos de validação tiverem o seu atributo [IsValid] definido como verdadeiro. Se não for esse o caso, a vista [form] deve ser apresentada. Esta vista contém os controlos de validação que irão apresentar o seu atributo [errorMessage]. Se a página for válida, utilizamos o objeto [products] criado por [Application_Start] para obter o [DataSet] correspondente à execução da consulta SQL SELECT. Convertemos isto num objeto [DataView]:
Dim données As DataView
Try
données = CType(Application("objProduits"), produits).getDataSet(txtSelect.Text.Trim).Tables(0).DefaultView
...
Poderíamos ter simplesmente trabalhado com o [DataSet] e escrito:
Dim données As DataSet
Try
données = CType(Application("objProduits"), produits).getDataSet(txtSelect.Text.Trim)
...
Um objeto [DataSet] é, essencialmente, uma coleção de tabelas ligadas por relações. Na nossa aplicação específica, o [DataSet] obtido a partir da classe [products] contém apenas uma tabela, a que resulta da instrução [select]. Uma tabela pode ser ordenada, ao passo que um [DataSet] não; no entanto, estamos interessados em ordenar os dados recuperados. Para trabalhar com a tabela de resultados da instrução [select], poderíamos ter escrito:
Dim données As DataTable
Try
données = CType(Application("objProduits"), produits).getDataSet(txtSelect.Text.Trim).Tables(0)
...
O objeto [DataTable], embora represente uma tabela de base de dados, não possui um método de ordenação. Para o fazer, é necessária uma vista da tabela. Uma vista é um objeto do tipo [DataView]. Pode ter diferentes vistas da mesma tabela utilizando filtros. Uma tabela tem uma vista padrão, que é aquela em que não estão definidos filtros. Representa, portanto, a tabela na sua totalidade. Esta vista padrão é obtida através de [DataTable.DefaultView]. Pode ordenar uma vista utilizando a sua propriedade [sort], que discutiremos mais adiante.
Se a recuperação do [DataSet] da classe [products] for bem-sucedida, a vista [results] é exibida; caso contrário, a vista [errors] é exibida. A vista [results] é exibida através do procedimento [displayResults], ao qual são passados dois parâmetros:
- o texto a colocar no rótulo [lblSelect]
- o [DataView] a ligar ao [DataGrid1]
Este exemplo demonstra a grande flexibilidade do componente [DataGrid]. Ele consegue reconhecer a estrutura do [DataView] ao qual está vinculado e adaptar-se a ela. Por fim, o procedimento [btnExécuter_Click] armazena o [DataView] que acabou de obter na sessão do utilizador, para que este esteja disponível quando o utilizador solicitar outras páginas do mesmo [DataView].
O procedimento [DataGrid1_PageIndexChanged] é executado quando o utilizador clica nos links [Next] e [Previous] no [DataGrid]. Recebe dois parâmetros:
Private Sub DataGrid1_PageIndexChanged(ByVal source As Object, ByVal e As System.Web.UI.WebControls.) Handles DataGrid1.PageIndexChanged
o objeto que desencadeou o evento — neste caso, um dos links [Next] ou [Previous] | |
informações sobre o evento. A propriedade e.NewPageIndex é o número da página a ser exibida em resposta ao pedido do cliente |
O código completo para o manipulador de eventos é o seguinte:
Private Sub DataGrid1_PageIndexChanged(ByVal source As Object, ByVal e As System.Web.UI.WebControls.DataGridPageChangedEventArgs) Handles DataGrid1.PageIndexChanged
' change page
With DataGrid1
.CurrentPageIndex = e.NewPageIndex
.DataSource = CType(Session("données"), DataView)
.DataBind()
End With
End Sub
O componente [DataGrid] possui um atributo [CurrentPageIndex] que indica o número da página que está a ser exibida ou que será exibida. Atribuímos o valor [NewPageIndex] do parâmetro [e] a este atributo. O [DataGrid] é então vinculado ao [DataView] que foi guardado na sessão pelo procedimento [btnExécuter_Click].
Poder-se-á questionar se o [DataGrid] necessita do atributo [EnableViewState=true], uma vez que o seu conteúdo é calculado pelo código sempre que a página é recarregada. Poder-se-á pensar que não. No entanto, se o [DataGrid] tiver o atributo [EnableViewState=false], observamos que o evento [DataGrid1.PageIndexChanged] nunca é acionado. É por isso que mantivemos [EnableViewState=true]. Sabemos que isto faz com que o conteúdo do [DataGrid] seja armazenado no campo oculto da página [__VIEWSTATE]. Isto pode tornar a página significativamente mais lenta se o [DataGrid] for grande. Se isto for um problema, pode gerir a paginação por si próprio sem utilizar a paginação automática do [DataGrid].
O procedimento [DataGrid1_SortCommand] é executado quando o utilizador clica no cabeçalho de uma das colunas apresentadas pelo [DataGrid] para solicitar a ordenação dos dados na ordem dessa coluna. Recebe dois parâmetros:
Private Sub DataGrid1_SortCommand(ByVal source As Object, ByVal e As System.Web.UI.WebControls.DataGridSortCommandEventArgs) Handles DataGrid1.SortCommand
o objeto que desencadeou o evento — neste caso, um dos links [Next] ou [Previous] | |
informações sobre o evento. A propriedade [e.SortExpression] é o nome da coluna clicada para ordenação |
O código completo para o manipulador de eventos é o seguinte:
Private Sub DataGrid1_SortCommand(ByVal source As Object, ByVal e As System.Web.UI.WebControls.DataGridSortCommandEventArgs) Handles DataGrid1.SortCommand
' sort the dataview
Dim données As DataView = CType(Session("données"), DataView)
données.Sort = e.SortExpression + " " + CType(IIf(rdCroissant.Checked, "asc", "desc"), String)
' we display it
With DataGrid1
.CurrentPageIndex = 0
.DataSource = données
.DataBind()
End With
End Sub
Recuperamos o [DataView] exibido pelo [DataGrid] na sessão atual. Foi colocado lá pelo procedimento [btnExécuter_Click]. O componente [DataView] possui uma propriedade [Sort] à qual atribuímos a expressão de ordenação. Esta segue a sintaxe [select ... order by expr1, expr2, ...], em que cada [expr] pode ser seguida pela palavra-chave [asc] para ordenação ascendente ou [desc] para ordenação descendente. A expressão [order by] aqui utilizada é [order by column asc/desc]. A propriedade [e.SortExpression] fornece-nos o nome da coluna do [DataGrid] em que se clicou para ordenação. A cadeia [asc/desc] é definida com base nos valores dos botões de opção no grupo [rdTri]. Assim que a expressão de ordenação do [DataView] é definida, o [DataGrid] é vinculado a ela. Posicionamos o [DataGrid] na sua primeira página.
8.7. Componente DataList e ligação de dados
Vamos agora concentrar-nos no componente [DataList]. Este oferece mais opções de formatação do que o [DataGrid], mas é menos flexível. Assim, não se pode adaptar automaticamente à fonte de dados à qual está ligado. Esta adaptação deve ser feita através de código, se desejado. Se a estrutura da fonte de dados for conhecida antecipadamente, então este componente oferece opções de formatação que podem torná-lo preferível ao [DataGrid].
8.7.1. Aplicação
Para ilustrar a utilização do [DataList], iremos construir uma aplicação MVC semelhante à anterior:
![]() |
As três vistas serão incorporadas no código de apresentação do controlador [main.aspx] como contentores. Por conseguinte, esta aplicação tem uma única página [main.aspx].
8.7.2. Classes de Negócio
A classe [products] é a mesma de antes.
8.7.3. As vistas
Quando o utilizador faz o seu primeiro pedido à aplicação, vê a seguinte vista [results1]:
![]() |
N.º | nome | tipo | propriedades | função |
1 | Botão de opção | EnableViewState=false | permite-lhe escolher um dos dois estilos [DataList] | |
2 | Botão | EnableViewState=false | Botão [submit] | |
3 | DataList | EnableViewState=true | Campo de exibição da lista de dados |
Se o utilizador selecionar o estilo n.º 2, verá a seguinte vista [results2]:
![]() |
N.º | nome | tipo | propriedades | função |
1 | DataList | EnableViewState=true | Campo de exibição da lista de dados |
A vista [errors] indica um problema ao aceder à fonte de dados:
![]() |
N.º | nome | tipo | propriedades | função |
1 | variável | Código HTML necessário para apresentar erros |
As três vistas da aplicação são três contentores (painéis) diferentes dentro da mesma página [main.aspx]. O seu código de apresentação é o seguinte:
<%@ Page src="main.aspx.vb" inherits="main" autoeventwireup="false" Language="vb" %>
<HTML>
<HEAD>
</HEAD>
<body>
<P>Liaison de données avec un DataList</P>
<HR width="100%" SIZE="1">
<form runat="server" ID="Form1">
<asp:Panel Runat="server" ID="bandeau">
<P>Choisissez votre style :
<asp:RadioButton id="RadioButton1" runat="server" EnableViewState="False" Text="1" GroupName="rdstyle"
Checked="True"></asp:RadioButton>
<asp:RadioButton id="RadioButton2" runat="server" EnableViewState="False" Text="2" GroupName="rdstyle"></asp:RadioButton>
<asp:Button id="btnChanger" runat="server" EnableViewState="False" Text="Changer"></asp:Button></P>
<HR width="100%" SIZE="1">
</asp:Panel>
<asp:Panel id="vueRésultats1" runat="server">
<P>
<asp:DataList id="DataList1" runat="server" BorderColor="Tan" BorderWidth="1px" BackColor="LightGoldenrodYellow"
CellPadding="2" RepeatDirection="Horizontal" RepeatColumns="4" ForeColor="Black">
<SelectedItemStyle ForeColor="GhostWhite" BackColor="DarkSlateBlue"></SelectedItemStyle>
<HeaderTemplate>
Contenu de la table [liste] de la base [produits]
</HeaderTemplate>
<AlternatingItemStyle BackColor="PaleGoldenrod"></AlternatingItemStyle>
<ItemTemplate>
nom :
<%# Container.DataItem("nom") %>
<br>
prix :
<%# databinder.eval(Container.DataItem,"prix","{0:C}") %>
<br>
</ItemTemplate>
<FooterStyle BackColor="Tan"></FooterStyle>
<HeaderStyle Font-Bold="True" BackColor="Tan"></HeaderStyle>
</asp:DataList></P>
</asp:Panel>
<asp:Panel id="vueRésultats2" runat="server">
<P>
<asp:DataList id="DataList2" runat="server">
<HeaderTemplate>
Contenu de la table [liste] de la base [produits]
<HR width="100%" SIZE="1">
</HeaderTemplate>
<AlternatingItemStyle BackColor="Teal"></AlternatingItemStyle>
<SeparatorStyle BackColor="LightSkyBlue"></SeparatorStyle>
<ItemStyle BackColor="#C0C000"></ItemStyle>
<ItemTemplate>
nom :
<%# Container.DataItem("nom") %>
, prix :
<%# databinder.eval(Container.DataItem,"prix","{0:C}") %>
<BR>
</ItemTemplate>
<SeparatorTemplate>
<HR width="100%" SIZE="1">
</SeparatorTemplate>
<HeaderStyle BackColor="#C0C0FF"></HeaderStyle>
</asp:DataList></P>
</asp:Panel>
<asp:Panel id="vueErreurs" runat="server">
<P>Les erreurs suivantes se sont produites :</P>
<%= erreursHTML %>
</asp:Panel>
</form>
</body>
</HTML>
8.7.4. Configurar componentes [DataList]
Vamos dar uma olhada nos vários atributos de um componente [DataList]. Existem muitos deles, e abordaremos apenas uma pequena seleção aqui. Pode definir até sete modelos de exibição dentro de um [DataList]:
Modelo de cabeçalho [DataList] | |
modelo para as linhas que exibem os itens na lista de dados associada. Apenas este modelo é obrigatório. | |
Para distinguir visualmente entre itens exibidos sucessivamente, podem ser utilizados dois modelos: ItemTemplate para o item n, AlternatingItemTemplate para o item n+1 | |
Modelo para o item selecionado na [DataList] | |
Modelo para o separador entre dois elementos na [DataList] | |
Uma [DataList] permite-lhe editar os valores que apresenta. [EditItemTemplate] é o modelo para um item na [DataList] que se encontra atualmente no modo «editar» | |
Modelo para o rodapé da [DataList] |
O componente [DataList1] foi criado com o [WebMatrix]. Na janela de propriedades, foi selecionado o link [AutoFormat]:
![]() | 1234567 ![]() |
Acima, o esquema [Color 5] irá gerar uma [DataList] com estilos para os seguintes modelos: HeaderTemplate (1), ItemTemplate (2, 6), AlternatingTemplate (3, 5), SelectedItemTemplate (4), FooterTemplate (7). O código gerado é o seguinte:
<asp:DataList id="DataList1" runat="server" BorderColor="Tan" BorderWidth="1px" BackColor="LightGoldenrodYellow"
CellPadding="2" ForeColor="Black">
<SelectedItemStyle ForeColor="GhostWhite" BackColor="DarkSlateBlue"></SelectedItemStyle>
<AlternatingItemStyle BackColor="PaleGoldenrod"></AlternatingItemStyle>
<FooterStyle BackColor="Tan"></FooterStyle>
<HeaderStyle Font-Bold="True" BackColor="Tan"></HeaderStyle>
</asp:DataList></P>
Não foram definidos modelos. Cabe-nos a nós fazê-lo. Definimos os seguintes modelos:
| |
|
Lembre-se de que o modelo [ItemTemplate] é utilizado para apresentar os itens da fonte de dados associada à [DataList]. Esta fonte de dados é uma coleção de linhas de dados, cada uma contendo um ou mais valores. A linha atual na fonte de dados é representada pelo objeto [Container.DataItem]. Essa linha possui colunas. [Container.DataItem("col1")] é o valor da coluna "col1" na linha atual. Para incluir este valor no código de apresentação, escrevemos <%# Container.DataItem("col") %>. Por vezes, queremos apresentar um elemento da linha atual num formato especial. Aqui, queremos apresentar a coluna "price" da linha atual em euros. Utilizamos a função [DataBinder.Eval], que recebe três parâmetros:
- a linha atual [Container.DataItem]
- o nome da coluna a formatar
- a string de formatação no formato {0:format}, em que [format] é um dos formatos aceites pelo método [string.format].
Assim, o código <%# DataBinder.Eval(Container.DataItem,"price",{0:C}) %> exibirá a coluna [price] da linha atual no formato monetário (formato C=Currency).
Teremos, portanto, uma [DataList] com o seguinte aspeto:

Acima, os dados foram organizados com quatro itens de dados por linha. Isto é conseguido utilizando os seguintes atributos [DataList]:
Horizontal | |
número desejado de colunas |
Em última análise, o código para [DataList1] é o apresentado no trecho de código acima. Deixamos ao leitor a tarefa de estudar o código de apresentação para [DataList2]. Tal como acontece com o componente [DataGrid], a maioria das propriedades [DataList] pode ser definida utilizando um assistente [WebMatrix]. Para o fazer, utilize a ligação [Property Generator] na janela de propriedades [DataList]:
![]() | ![]() |
8.7.5. Os controladores
O controlador [global.asax, global.asax.vb] é o seguinte:
[global.asax]
[global.asax.vb]
Imports System
Imports System.Web
Imports System.Web.SessionState
Imports st.istia.univangers.fr
Imports System.Configuration
Imports System.Data
Imports System.Collections
Public Class Global
Inherits System.Web.HttpApplication
Sub Application_Start(ByVal sender As Object, ByVal e As EventArgs)
' create a product object
Try
Dim données As DataSet = New produits(ConfigurationSettings.AppSettings("OLEDBStringConnection")).getDataSet("select * from LISTE")
' put the object in the application
Application("données") = données
' no error
Application("erreur") = False
Catch ex As Exception
'there has been an error, we note it in the application
Application("erreur") = True
Application("message") = ex.Message
End Try
End Sub
End Class
Quando a aplicação é iniciada (Application_Start), criamos um [dataset] a partir da classe de negócios [products] e adicionamo-lo à aplicação para que esteja disponível para todos os pedidos de todos os clientes. Se ocorrer uma exceção durante esta criação, esta é registada na aplicação. O procedimento [Application_Start] será executado apenas uma vez. Depois disso, o controlador [global.asax] deixará de estar envolvido. O controlador [main.aspx.vb] irá então tratar do trabalho:
Imports System.Collections
Imports Microsoft.VisualBasic
Imports System.Data
Imports st.istia.univangers.fr
Imports System
Imports System.Xml
Public Class main
Inherits System.Web.UI.Page
' components page
Protected WithEvents vueErreurs As System.Web.UI.WebControls.Panel
Protected WithEvents DataList1 As System.Web.UI.WebControls.DataList
Protected WithEvents DataList2 As System.Web.UI.WebControls.DataList
Protected WithEvents vueRésultats1 As System.Web.UI.WebControls.Panel
Protected WithEvents vueRésultats2 As System.Web.UI.WebControls.Panel
Protected WithEvents RadioButton1 As System.Web.UI.WebControls.RadioButton
Protected WithEvents RadioButton2 As System.Web.UI.WebControls.RadioButton
Protected WithEvents btnChanger As System.Web.UI.WebControls.Button
Protected WithEvents bandeau As System.Web.UI.WebControls.Panel
' data page
Protected erreursHTML As String
Private Sub Page_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
' check for application errors
If CType(Application("erreur"), Boolean) Then
' the application has not initialized correctly
Dim erreurs As New ArrayList
erreurs.Add("Application momentanément indisponible (" + CType(Application("message"), String) + ")")
afficheErreurs(erreurs, False)
Exit Sub
End If
'1st request
If Not IsPostBack Then
' initialize controls
With DataList1
.DataSource = CType(Application("données"), DataSet)
.DataBind()
End With
With DataList2
.DataSource = CType(Application("données"), DataSet)
.DataBind()
End With
' the empty form is displayed
afficheRésultats(True, False)
End If
End Sub
Private Sub afficheErreurs(ByVal erreurs As ArrayList, ByVal afficheLien As Boolean)
' displays the error view
erreursHTML = ""
For i As Integer = 0 To erreurs.Count - 1
erreursHTML += "<li>" + erreurs(i).ToString + "</li>" + ControlChars.CrLf
Next
' the [errors] view is displayed
vueErreurs.Visible = True
vueRésultats1.Visible = False
vueRésultats2.Visible = False
bandeau.Visible = False
End Sub
Private Sub afficheRésultats(ByVal visible1 As Boolean, ByVal visible2 As Boolean)
' the [results] view is displayed
vueRésultats1.Visible = visible1
vueRésultats2.Visible = visible2
vueErreurs.Visible = False
End Sub
Private Sub btnChanger_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnChanger.Click
' change the style
afficheRésultats(RadioButton1.Checked, RadioButton2.Checked)
End Sub
End Class
8.8. Componente Repeater e ligação de dados
O componente [Repeater] permite repetir o código HTML para cada item de uma lista de dados. Suponhamos que queremos apresentar uma lista de erros no seguinte formato:

Já nos deparámos com este problema e resolvemo-lo incluindo uma variável no código de apresentação na forma <% =erreursHTML %>, onde o valor de erreursHTML é calculado pelo controlador. Este valor contém código HTML, especificamente o de uma lista. A desvantagem é que, se quiser modificar a apresentação desta lista HTML, tem de ir à secção do controlador, o que vai contra a separação entre controlador e apresentação. O componente [Repeater] oferece uma solução. Tal como no [DataList], podemos definir o <HeaderTemplate> para o cabeçalho, o <ItemTemplate> para o item atual na lista de dados e o <FooterTemplate> para o final dos dados. Aqui, poderíamos ter a seguinte definição para o componente [Repeater]:
<asp:Repeater id="Repeater1" runat="server" EnableViewState="False">
<HeaderTemplate>
Les erreurs suivantes se sont produites :
<ul>
</HeaderTemplate>
<ItemTemplate>
<li>
<%# Container.DataItem %>
</li>
</ItemTemplate>
<FooterTemplate>
</ul>
</FooterTemplate>
</asp:Repeater>
Note que [Container.DataItem] representa uma linha de dados se a fonte de dados tiver várias colunas. Representa um único ponto de dados se a fonte tiver apenas uma coluna. Este será o caso aqui. Por exemplo, estamos a construir a seguinte aplicação:

O código de layout da página é o seguinte:
<html>
<head>
</head>
<body>
<form runat="server">
<p>
Liaison de données avec un composant [Repeater]
</p>
<hr />
<p>
<asp:Repeater id="Repeater1" runat="server" EnableViewState="False">
<ItemTemplate>
<li>
<%# Container.DataItem %>
</li>
</ItemTemplate>
<HeaderTemplate>
Les erreurs suivantes se sont produites :
<ul>
</HeaderTemplate>
<FooterTemplate>
</ul>
</FooterTemplate>
</asp:Repeater>
</p>
</form>
</body>
</html>
O código de controlo é o seguinte:
<%@ Page Language="VB" %>
<script runat="server">
' procedure executed when the page is loaded
Sub page_Load(sender As Object, e As EventArgs)
if not IsPostBack then
' create a data source
with Repeater1
.DataSource=createDataSource
.DataBind
end with
end if
End Sub
function createDataSource as ArrayList
' create an arraylist
dim erreurs as new ArrayList
dim i as integer
for i=0 to 5
erreurs.add("erreur-"+i.ToString)
next
return erreurs
end function
</script>
<html>
...
</html>
Na primeira solicitação do cliente, associamos um objeto [ArrayList] ao componente [Repeater], que deve representar uma lista de erros.
8.9. Aplicação
Aqui, revisamos uma aplicação anteriormente implementada com componentes do lado do servidor. A aplicação permite aos utilizadores simular cálculos de impostos. Ela depende de uma classe [impot], que não revisaremos aqui. Esta classe requer dados que recupera de uma fonte de dados OLEDB. Para este exemplo, utilizaremos uma fonte de dados ACCESS. Introduzimos as seguintes novas funcionalidades nesta versão:
- a utilização de componentes de validação para verificar a validade dos dados
- a utilização de componentes do servidor ligados a fontes de dados para apresentar resultados
8.9.1. A estrutura MVC da aplicação
A estrutura MVC da aplicação é a seguinte:
![]() |
As três vistas serão incorporadas no código de apresentação do controlador [main.aspx] como contentores. Portanto, esta aplicação tem uma única página [main.aspx].
8.9.2. As vistas da aplicação
A vista [form] é o formulário para introduzir informações utilizadas para calcular o imposto de um utilizador:

O utilizador preenche o formulário:

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

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

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

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

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

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

8.9.2.1. O código de apresentação
Lembre-se de que a página [main.aspx] reúne todas as vistas. Trata-se de um único formulário com três contentores:
- [panelform] para a vista [form]
- [panelerrors] para a vista [errors]
- [panelsimulations] para a vista [simulations]
Vamos agora detalhar os componentes destes três contentores. O contentor [panelform] tem a seguinte representação visual:
![]() |
N.º | nome | tipo | propriedades | função |
Painel | visualização do formulário | |||
Botão de opção | Nome do grupo=rdmarie | botões de opção | ||
Validador personalizado | Mensagem de erro=Não especificou o seu estado civil EnableClientScript=false | verifica se o programa cliente enviou o estado civil esperado | ||
TextBox | número de filhos | |||
Validador de campo obrigatório | Mensagem de erro=Introduza o número de filhos ControlToValidate=txtChildren | verifica se o campo [txtChildren] não está vazio | ||
Validador de intervalo | ErrorMessage=Introduza um número entre 1 e 30 ControlToValidate=txtChildren | verifica se o campo [txtChildren] está dentro do intervalo [1,30] | ||
Caixa de Texto | EnableViewState=true | salário anual | ||
Validador de campo obrigatório | ControlToValidate=txtSalary Mensagem de erro=Introduza o valor do seu salário | verifica se o campo [txtSalary] não está vazio | ||
Validador de Expressão Regular | ControlToValidate=txtSalary Mensagem de erro=Salário inválido Expressão regular=\s*\d+\s* | verifica se o campo [txtSalary] é uma sequência de dígitos | ||
Botão | ValidationTriggers=true | Botão [submit] no formulário - inicia o cálculo do imposto | ||
Botão | Validação=false | Botão [submit] do formulário - limpa o formulário |
Poderá ficar surpreendido com a verificação [cvMarié], que verifica se o utilizador selecionou um dos dois botões de opção. Isto é necessário porque não há forma de ter a certeza de que o utilizador está a utilizar o formulário enviado pelo servidor. Uma vez que nada pode garantir isso, temos de verificar todos os parâmetros enviados. Repare também no atributo [CausesValidation=false] do botão [btnEffacer]. Quando o utilizador clica neste botão, os dados enviados não devem ser verificados, uma vez que serão ignorados.
Todos os componentes no contentor têm a propriedade [EnableViewState=false], exceto [panelForm, txtEnfants, txtSalaire, rdOui, rdNon]. O contentor [panelerreurs] tem a seguinte representação visual:
![]() |
N.º | nome | tipo | propriedades | função |
Painel | EnableViewState=false | visualização de erro | ||
Repetidor | EnableViewState=false | exibe uma lista de erros |
O componente [rptErrors] é definido da seguinte forma:
<asp:Repeater id="rptErreurs" runat="server" EnableViewState="False">
<ItemTemplate>
<li>
<%# Container.Dataitem%>
</li>
</ItemTemplate>
<HeaderTemplate>
Les erreurs suivantes se sont produites
<ul>
</HeaderTemplate>
<FooterTemplate>
</ul>
</FooterTemplate>
</asp:Repeater>
A lista de dados associada ao componente [rptErrors] será um objeto [ArrayList] contendo uma lista de mensagens de erro. Assim, como mostrado acima, <%# Container.Dataitem%> refere-se à mensagem de erro atual. O contentor [panelsimulations] tem a seguinte representação visual:
![]() |
N.º | nome | tipo | propriedades | função |
Painel | EnableViewState=false | visualização da simulação | ||
LinkButton | EnableViewState=false | ligação para o formulário | ||
DataGrid | EnableViewState=false | responsável pela exibição das simulações colocadas num objeto [DataTable] |
O componente [dgSimulations] é configurado da seguinte forma no [Webmatrix]. Na janela de propriedades do [dgSimulations], selecionamos o link [AutoFormat] e escolhemos o esquema [Color 3]:
![]() | ![]() |
Em seguida, ainda na janela de propriedades [dlgSimulations], selecionamos o link [Property Generator]. Solicitamos que os cabeçalhos das colunas sejam exibidos:

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

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

título da coluna, aqui «Casado» | |
Nome da coluna na fonte de dados que será exibida por esta coluna do [DataGrid]. Aqui, é a coluna "married" do [DataTable]. |
A segunda coluna é definida da seguinte forma:
Filhos | |
filhos |
A terceira coluna é definida da seguinte forma:
Salário anual | |
salário | |
{0:C} - exibição da moeda para obter o símbolo do euro |
A quarta coluna é definida da seguinte forma:
Montante do imposto | |
imposto | |
{0:C} - exibição da moeda para obter o símbolo do euro |
Colocamos o código de apresentação e o código de controlo em dois ficheiros separados. O primeiro estará em [main.aspx] e o segundo em [main.aspx.vb]. O código para [main.aspx] é o seguinte:
<%@ page src="main.aspx.vb" inherits="main" AutoEventWireUp="false" %>
<HTML>
<HEAD>
<title>Calculer votre impôt</title>
</HEAD>
<body>
<P>Calculer votre impôt</P>
<HR width="100%" SIZE="1">
<FORM id="Form1" runat="server">
<asp:panel id="panelform" Runat="server">
<TABLE id="Table1" cellSpacing="1" cellPadding="1" border="0">
<TR>
<TD height="19">Etes-vous marié(e)</TD>
<TD height="19">
<asp:RadioButton id="rdOui" runat="server" GroupName="rdMarie"></asp:RadioButton>Oui
<asp:RadioButton id="rdNon" runat="server" GroupName="rdMarie" Checked="True"></asp:RadioButton>Non
<asp:CustomValidator id="cvMarie" runat="server" ErrorMessage="Vous n'avez pas indiqué votre état marital"
Display="Dynamic" EnableClientScript="False" Visible="False" EnableViewState="False"></asp:CustomValidator></TD>
</TR>
<TR>
<TD>Nombre d'enfants</TD>
<TD>
<asp:TextBox id="txtEnfants" runat="server" Columns="3" MaxLength="3"></asp:TextBox>
<asp:RequiredFieldValidator id="rfvEnfants" runat="server" ErrorMessage="Indiquez le nombre d'enfants" Display="Dynamic"
ControlToValidate="txtEnfants" EnableViewState="False"></asp:RequiredFieldValidator>
<asp:RangeValidator id="rvEnfants" runat="server" ErrorMessage="Tapez un nombre entre 1 et 30" Display="Dynamic"
ControlToValidate="txtEnfants" Type="Integer" MaximumValue="30" MinimumValue="0" EnableViewState="False"></asp:RangeValidator></TD>
</TR>
<TR>
<TD>Salaire annuel (euro)</TD>
<TD>
<asp:TextBox id="txtSalaire" runat="server" Columns="10" MaxLength="10"></asp:TextBox>
<asp:RequiredFieldValidator id="rfvSalaire" runat="server" ErrorMessage="Indiquez le montant de votre salaire"
Display="Dynamic" ControlToValidate="txtSalaire" EnableViewState="False"></asp:RequiredFieldValidator>
<asp:RegularExpressionValidator id="revSalaire" runat="server" ErrorMessage="Salaire invalide" Display="Dynamic"
ControlToValidate="txtSalaire" ValidationExpression="\s*\d+\s*" EnableViewState="False"></asp:RegularExpressionValidator></TD>
</TR>
</TABLE>
<P>
<asp:Button id="btnCalculer" runat="server" Text="Calculer"></asp:Button>
<asp:Button id="btnEffacer" runat="server" Text="Effacer" CausesValidation="False"></asp:Button></P>
</asp:panel>
<asp:panel id="panelerreurs" runat="server" EnableViewState="False">
<asp:Repeater id="rptErreurs" runat="server" EnableViewState="False">
<ItemTemplate>
<li>
<%# Container.Dataitem%>
</li>
</ItemTemplate>
<HeaderTemplate>
Les erreurs suivantes se sont produites
<ul>
</HeaderTemplate>
<FooterTemplate>
</ul>
</FooterTemplate>
</asp:Repeater>
</asp:panel>
<asp:panel id="panelsimulations" runat="server" EnableViewState="False">
<P>
<asp:DataGrid id="dgSimulations" runat="server" BorderColor="#DEBA84" BorderStyle="None" CellSpacing="2"
BorderWidth="1px" BackColor="#DEBA84" CellPadding="3" AutoGenerateColumns="False" EnableViewState="False">
<SelectedItemStyle Font-Bold="True" ForeColor="White" BackColor="#738A9C"></SelectedItemStyle>
<ItemStyle ForeColor="#8C4510" BackColor="#FFF7E7"></ItemStyle>
<HeaderStyle Font-Bold="True" ForeColor="White" BackColor="#A55129"></HeaderStyle>
<FooterStyle ForeColor="#8C4510" BackColor="#F7DFB5"></FooterStyle>
<Columns>
<asp:BoundColumn DataField="marié" 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 está distribuído pelos ficheiros [global.asax.vb] e [main.aspx.vb]. O ficheiro [global.asax] é 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)
' create an impot object
Dim objImpot As impot
Try
objImpot = New impot(New impotsOLEDB(ConfigurationSettings.AppSettings("chaineConnexion")))
' put the object in the application
Application("objImpot") = objImpot
' no error
Application("erreur") = False
Catch ex As Exception
'there has been an error, we note it in the application
Application("erreur") = True
Application("message") = ex.Message
End Try
End Sub
Sub Session_Start(ByVal sender As Object, ByVal e As EventArgs)
' start of session - create a list of empty simulations
Dim simulations As New DataTable("simulations")
simulations.Columns.Add("marié", Type.GetType("System.String"))
simulations.Columns.Add("enfants", Type.GetType("System.String"))
simulations.Columns.Add("salaire", Type.GetType("System.Int32"))
simulations.Columns.Add("impôt", Type.GetType("System.Int32"))
Session.Item("simulations") = simulations
End Sub
End Class
Quando a aplicação é iniciada (primeira solicitação feita à aplicação), o procedimento [Application_Start] é executado. Ele tenta criar um objeto do tipo [tax] recuperando os seus dados de uma fonte OLEDB. Recomenda-se ao leitor que reveja o Capítulo 5, onde esta classe foi definida, caso a tenha esquecido. A criação do objeto [impot] pode falhar se a fonte de dados não estiver disponível. Neste caso, o erro é armazenado na aplicação para que todas as solicitações subsequentes saibam que não foi possível inicializá-lo corretamente. Se a criação for bem-sucedida, o objeto [impot] criado também é armazenado na aplicação. Ele será utilizado por todas as solicitações de cálculo de impostos. Quando um cliente faz a sua primeira solicitação, é criada uma sessão para ele pelo procedimento [Application_Start]. Esta sessão destina-se a armazenar as várias simulações de cálculo de impostos que irá realizar. Estas serão armazenadas num objeto [DataTable] associado à chave de sessão "simulations". Quando a sessão é iniciada, esta chave é associada a um objeto [DataTable] vazio cuja estrutura foi definida. A informação necessária à aplicação é colocada no seu ficheiro de configuração [wenConfig]:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<appSettings>
<add key="chaineConnexion" value="Provider=Microsoft.Jet.OLEDB.4.0; Ole DB Services=-4; Data Source=D:\data\devel\aspnet\poly\webforms2\vs\impots6\impots.mdb" />
</appSettings>
</configuration>
A chave [connectionString] especifica a cadeia de ligação à fonte OLEDB. O resto do código de controlo encontra-se em [main.aspx.vb]:
Imports System.Collections
Imports Microsoft.VisualBasic
Imports st.istia.univangers.fr
Imports System
Imports System.Web.UI.Control
Imports System.Data
Public Class main
Inherits System.Web.UI.Page
Protected WithEvents rdOui As System.Web.UI.WebControls.RadioButton
Protected WithEvents rdNon As System.Web.UI.WebControls.RadioButton
Protected WithEvents txtEnfants As System.Web.UI.WebControls.TextBox
Protected WithEvents txtSalaire As System.Web.UI.WebControls.TextBox
Protected WithEvents btnCalculer As System.Web.UI.WebControls.Button
Protected WithEvents btnEffacer As System.Web.UI.WebControls.Button
Protected WithEvents panelform As System.Web.UI.WebControls.Panel
Protected WithEvents lnkForm2 As System.Web.UI.WebControls.LinkButton
Protected WithEvents panelerreurs As System.Web.UI.WebControls.Panel
Protected WithEvents rfvEnfants As System.Web.UI.WebControls.RequiredFieldValidator
Protected WithEvents rvEnfants As System.Web.UI.WebControls.RangeValidator
Protected WithEvents rfvSalaire As System.Web.UI.WebControls.RequiredFieldValidator
Protected WithEvents rptErreurs As System.Web.UI.WebControls.Repeater
Protected WithEvents dgSimulations As System.Web.UI.WebControls.DataGrid
Protected WithEvents revSalaire As System.Web.UI.WebControls.RegularExpressionValidator
Protected WithEvents cvMarie As System.Web.UI.WebControls.CustomValidator
Protected WithEvents panelsimulations As System.Web.UI.WebControls.Panel
' local variables
Private Sub Page_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
...
End Sub
Private Sub afficheErreurs(ByRef erreurs As ArrayList)
...
End Sub
Private Sub afficheFormulaire()
...
End Sub
Private Sub afficheSimulations(ByRef simulations As DataTable, ByRef lien As String)
...
End Sub
Private Sub btnCalculer_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnCalculer.Click
....
End Sub
Private Sub lnkForm1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs)
...
End Sub
Private Sub lnkForm2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles lnkForm2.Click
...
End Sub
Private Sub btnEffacer_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnEffacer.Click
...
End Sub
Private Sub razForm()
...
End Sub
Private Sub cvMarie_ServerValidate(ByVal source As System.Object, ByVal args As System.Web.UI.WebControls.ServerValidateEventArgs)
...
End Sub
End Class
O primeiro evento tratado pelo código é [Page_Load]:
Private Sub Page_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
' first, we look at the state of the application
If CType(Application("erreur"), Boolean) Then
' the application failed to initialize
' the error view is displayed
Dim erreurs As New ArrayList
erreurs.Add("Application momentanément indisponible (" + CType(Application("message"), String) + ")")
afficheErreurs(erreurs)
Exit Sub
End If
' no errors - on the 1st request, the form is presented
If Not IsPostBack Then afficheFormulaire()
End Sub
Antes de processar o pedido, verificamos se a aplicação foi inicializada corretamente. Caso contrário, apresentamos a vista [errors] utilizando o procedimento [displayErrors]. Se este for o primeiro pedido (IsPostBack=false), apresentamos a vista [form] utilizando [displayForm].
O procedimento que apresenta a vista [errors] é o seguinte:
Private Sub afficheErreurs(ByRef erreurs As ArrayList)
' builds a list of errors
With rptErreurs
.DataSource = erreurs
.DataBind()
End With
' container display
panelerreurs.Visible = True
panelform.Visible = False
panelsimulations.Visible = False
Exit Sub
End Sub
O procedimento recebe como parâmetro uma lista de mensagens de erro em [errors] do tipo [ArrayList]. Basta ligar esta fonte de dados ao componente [rptErrors] responsável pela sua apresentação.
O procedimento que exibe a vista [form] é o seguinte:
Private Sub afficheFormulaire()
' displays the form
panelform.Visible = True
' the other containers are hidden
panelerreurs.Visible = False
panelsimulations.Visible = False
End Sub
Este procedimento simplesmente torna o contentor [panelform] visível. Os componentes são apresentados com o seu valor enviado ou anterior (VIEWSTATE).
Quando o utilizador clica no botão [Calculate] na vista [form], é enviada uma solicitação POST para [main.aspx]. O procedimento [Page_Load] é executado, seguido pelo procedimento [btnCalculate_Click]:
Private Sub btnCalculer_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnCalculer.Click
' we check the validity of the data entered; if there are any errors, we report them
If Not Page.IsValid Then
afficheFormulaire()
Exit Sub
End If
' no errors - tax is calculated
Dim impot As Long = CType(Application("objImpot"), impot).calculer( _
rdOui.Checked, CType(txtEnfants.Text, Integer), CType(txtSalaire.Text, Long))
' the result is added to existing simulations
Dim simulations As DataTable = CType(Session.Item("simulations"), DataTable)
Dim simulation As DataRow = simulations.NewRow
simulation("marié") = CType(IIf(rdOui.Checked, "oui", "non"), String)
simulation("enfants") = txtEnfants.Text.Trim
simulation("salaire") = CType(txtSalaire.Text.Trim, Long)
simulation("impôt") = impot
simulations.Rows.Add(simulation)
' put the simulations in the session
Session.Item("simulations") = simulations
' the simulations page is displayed
afficheSimulations(simulations, "Retour au formulaire")
End Sub
O procedimento começa por verificar a validade da página. Recorde-se que, quando o procedimento [btnCalculate_Click] é executado, os controlos de validação já tiveram de fazer o seu trabalho e o atributo [IsValid] da página já foi definido. Se a página for inválida, a vista [form] é apresentada novamente e o procedimento termina. Os componentes de validação da vista [form] cujo atributo [IsValid] esteja definido como [false] exibirão o seu atributo [ErrorMessage]. Se a página for válida, o montante do imposto é calculado utilizando o objeto [tax] que foi armazenado na aplicação no momento do arranque. Esta nova simulação é adicionada à lista de simulações já realizadas e armazenadas na sessão.
Por fim, a vista [simulations] é exibida pelo seguinte procedimento [displaySimulations]:
Private Sub afficheSimulations(ByRef simulations As DataTable, ByRef lien As String)
' we link the datagrid to the simulations source
With dgSimulations
.DataSource = simulations
.DataBind()
End With
' link
lnkForm2.Text = lien
' the views
panelsimulations.Visible = True
panelerreurs.Visible = False
panelform.Visible = False
End Sub
O procedimento tem dois parâmetros:
- uma lista de simulações em [simulations] do tipo [DataTable]
- um texto de ligação em [link]
A fonte de dados [simulations] está associada ao componente responsável pela sua exibição. O texto do link é colocado na propriedade [Text] do objeto [LinkButton] na vista.
Quando o utilizador clica no botão [Delete] na vista [form], o procedimento [btnDelete_click] é executado (sempre após [Page_Load]):
Private Sub btnEffacer_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnEffacer.Click
' displays the empty form
razForm()
afficheFormulaire()
End Sub
Private Sub razForm()
' empty the form
rdOui.Checked = False
rdNon.Checked = True
txtEnfants.Text = ""
txtSalaire.Text = ""
End Sub
O código acima é suficientemente simples para não necessitar de comentários. Ainda precisamos de tratar os cliques nos links de visualização [errors] e [simulations]:
Private Sub lnkForm1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles lnkForm1.Click
' displays the form
afficheFormulaire()
End Sub
Private Sub lnkForm2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles lnkForm2.Click
' displays the form
afficheFormulaire()
End Sub
Ambos os procedimentos limitam-se a apresentar a vista [form]. Sabemos que os campos nesta vista receberão um valor que é ou o valor enviado para eles ou o seu valor anterior. Uma vez que, neste caso, o pedido POST do cliente não envia quaisquer valores para os campos do formulário, estes reverterão para os seus valores anteriores. O formulário é, portanto, apresentado com os valores introduzidos pelo utilizador.
8.9.4. Testes
Todos os ficheiros necessários à aplicação são colocados numa pasta denominada <application-path>: ![]() | A pasta [bin] contém a DLL com as classes [impot], [impotsData] e [impotsOLEDB] necessárias para a aplicação: |
Se desejar, o leitor pode consultar o Capítulo 5, que explica como criar o ficheiro [impot.dll] mencionado acima. Feito isto, o servidor Cassini é iniciado com os parâmetros (<application-path>,/impots6). Acedemos então ao URL [http://impots6/main.aspx] utilizando um navegador:

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

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






















