7. Componentes de servidor ASP - 1
7.1. Introduction
Neste capítulo, descrevemos a tecnologia recomendada no ASP.NET para construir a interface do usuário. Sabemos que há duas fases bem distintas no processamento de uma página .aspx pelo servidor web:
- primeiramente, ocorre a execução do controlador da página. Este é constituído por código localizado na própria página .aspx (solução WebMatrix) ou em um arquivo separado (solução Visual Studio.NET).
- Em seguida, o código de apresentação da página .aspx é executado para ser transformado em código HTML, que é enviado ao cliente.

O ASP.NET oferece três bibliotecas de tags para escrever o código de apresentação da página:
- as tags clássicas HTML. É o que temos usado até agora.
- as tags HTML do servidor
- as tags webforms
Independentemente da biblioteca de tags utilizada, a função do controlador de página permanece a mesma. Ele deve calcular o valor dos parâmetros dinâmicos que aparecem no código de apresentação. Até agora, esses parâmetros dinâmicos eram simples: eram objetos do tipo [String]. Assim, se no código de apresentação tivermos uma tag <%=nome%>:
- o controlador de página declara uma variável [nom] do tipo [String] e calcula seu valor
- quando o controlador de página conclui seu trabalho e o código de apresentação é executado para gerar a resposta HTML, a tag <%=nom%> é substituída pelo valor calculado pelo código de controle
Sabemos que a separação entre controlador e apresentação é arbitrária e que é possível misturar código de controlador e código de apresentação na mesma página. Já explicamos por que esse método não é recomendado e continuaremos a respeitar a separação entre controlador e apresentação.
As tags [HTML serveur] e [WebForms] permitem introduzir no código de apresentação objetos mais complexos do que o simples objeto [String]. Isso, às vezes, apresenta um interesse real. Tomemos o exemplo de um formulário com uma lista. Essa lista deve ser apresentada ao cliente com um código HTML semelhante a este:
<select name="uneListe" size="3">
<option value="opt1">option1</option>
<option value="opt2">option2</option>
<option value="opt3" selected>option3</option>
</select>
O conteúdo da lista e a opção a ser selecionada são elementos dinâmicos e, portanto, devem ser gerados pelo controlador de página. Já nos deparamos com esse problema e o resolvemos inserindo no código de apresentação a tag
Essa tag será substituída pelo valor [String] da variável [uneListeHTML]. Esse valor, calculado pelo controlador, deverá ser o código HTML da lista, c.a.d. “<select name=..>...</select>”. Isso não é particularmente difícil de fazer e parece uma solução elegante que evita colocar código de geração diretamente na parte de apresentação da página. Aqui, haveria um loop com verificações a serem inseridas nela, o que a tornaria bastante “poluída”. No entanto, esse método apresenta uma desvantagem. A separação entre controle e apresentação de uma página também serve para delimitar duas áreas de competência:
- o do desenvolvedor .NET, responsável pelo controlador da página
- aquele do designer gráfico, que se encarrega da parte de apresentação da página
Aqui, vemos que a geração do código HTML da lista foi transferida para o controlador. O designer gráfico pode querer intervir nesse código HTML para modificar o aspecto “visual” da lista. Ele será obrigado a trabalhar na parte [contrôleur] e, portanto, a sair de sua área de competência, com os riscos de erros introduzidos inadvertidamente no código que isso acarreta.
As bibliotecas de tags de servidor resolvem esse problema. Elas oferecem um objeto que representa uma lista HTML. Assim, a biblioteca [WebForms] oferece a seguinte tag:
Essa tag representa um objeto do tipo [ListBox] que pode ser manipulado pelo controlador de página. Esse objeto possui propriedades para representar as diferentes opções da lista HTML e indicar a opção selecionada. O controlador de página, portanto, atribuirá os valores adequados a essas propriedades. Quando a parte de apresentação for executada, a tag
será substituída pelo código HTML, que representa os objetos [uneListe] e c.a.d. o código “<select ..>...</select>”. Por enquanto, não há diferença fundamental em relação ao método anterior, exceto por ser uma forma de codificação orientada a objetos, o que é interessante. Voltemos ao nosso designer gráfico, que precisa alterar a “aparência” da lista. As tags de servidor possuem atributos de estilo (BackColor, Bordercolor, BorderWidth, ...) que permitem definir a aparência visual do objeto HTML correspondente. Assim, poderemos escrever:
<asp:ListBox id="ListBox1" runat="server" BackColor="#ffff99"></asp:ListBox></P>
A vantagem é que o designer gráfico permanece no código de apresentação para fazer essas modificações. Trata-se de uma vantagem clara em relação ao método anterior. As bibliotecas de tags de servidor facilitam, portanto, a construção da parte de apresentação das páginas, o que chamamos nos capítulos anteriores de interface do usuário. O objetivo deste capítulo é apresentá-las. Veremos que, às vezes, elas oferecem objetos complexos, como calendários ou tabelas vinculadas a fontes de dados. Elas são extensíveis, c.a.d, de modo que o usuário pode criar sua própria biblioteca de tags. Assim, ele pode criar uma tag que gere um banner em uma página. Todas as páginas que utilizarem essa tag terão, então, o mesmo banner.
A geração HTML realizada para uma tag se adapta ao tipo de navegador do cliente. Quando o navegador faz uma solicitação ao servidor web, ele envia, entre seus cabeçalhos HTTP, um cabeçalho [User-Agent: xx], em que [xx] identifica o cliente. Veja um exemplo:
Com essa informação, o servidor web pode identificar as capacidades do cliente, especialmente o tipo de código HTML que ele é capaz de processar. De fato, ao longo do tempo, surgiram várias versões da linguagem HTML. Os navegadores mais recentes suportam as versões mais atuais da linguagem, o que não acontece com os navegadores mais antigos. Dependendo do cabeçalho HTTP [User-Agent:] que o cliente enviou, o servidor enviará uma versão HTML que ele será capaz de compreender. Essa é uma ideia interessante e útil, pois o desenvolvedor não precisa se preocupar com o tipo de navegador do cliente que acessa seu aplicativo.
Por fim, versões avançadas do IDE, como o Visual Studio.NET, o WebMatrix, etc., permitem um design “ao estilo Windows” da interface web. Essas ferramentas, embora não sejam indispensáveis, oferecem, no entanto, uma ajuda decisiva ao desenvolvedor. O desenvolvedor projeta a interface da Web por meio de componentes gráficos que insere nessa interface. Ele tem acesso direto às propriedades de cada um dos componentes da interface, podendo configurá-las como desejar. Essas propriedades serão traduzidas no código HTML de apresentação da interface em atributos da tag <asp:> do componente. A vantagem para o desenvolvedor é que ele não precisa se lembrar nem da lista nem da sintaxe dos atributos de cada tag. Essa é uma vantagem considerável quando não se conhece perfeitamente as bibliotecas de tags de servidor oferecidas pelo ASP.NET. Quando essa sintaxe for dominada, alguns desenvolvedores poderão preferir codificar diretamente as tags no código de apresentação da página, sem passar pela fase de design gráfico. Nesse caso, um IDE não é mais necessário. Um simples editor de texto é suficiente. Dependendo da forma de trabalho, o foco recai então nos componentes (uso de um IDE) ou nas tags (uso de um editor de texto). Há equivalência entre esses dois termos. O componente é o objeto que será manipulado pelo código de controle da página. O IDE nos dá acesso às suas propriedades na fase de concepção. Os valores atribuídos a essas propriedades são traduzidos imediatamente nos atributos da tag do componente no código de apresentação. Na fase de execução, o código de controle da página manipulará o componente e atribuirá valores a algumas de suas propriedades. O código de apresentação, por sua vez, irá gerar o código HTML do componente utilizando, por um lado, os atributos definidos na fase de projeto para a tag de servidor correspondente e, por outro, os valores das propriedades do componente calculados pelo código de controle.
7.2. O contexto de execução dos exemplos
Ilustraremos o projeto de interfaces web baseadas em componentes de servidor com programas cujo contexto de execução será, na maioria das vezes, o seguinte:
- a aplicação web será composta por uma única página P contendo um formulário F,
- o cliente fará sua primeira solicitação diretamente a essa página P. Isso consistirá em acessar a URL da página P por meio de um navegador. Portanto, será feita uma solicitação GET para essa URL P. O servidor fornecerá a página P e, consequentemente, o formulário F que ela contém,
- o usuário preencherá o formulário e o enviará, c.a.d, ou seja, realizará uma ação que forçará o navegador a enviar o formulário F ao servidor. A operação POST do navegador terá sempre como destino a página P. O servidor exibirá novamente a página P com o formulário F, cujo conteúdo pode ter sido alterado pela ação do usuário.
- Em seguida, as etapas 2 e 3 serão retomadas.
Trata-se de um processo de execução bem específico, fora do qual certos conceitos expostos a seguir deixam de funcionar. Não estamos mais em um contexto de arquitetura MVC, no qual um aplicativo multipáginas é controlado por uma página específica que chamamos de “controlador de aplicativo”. Nesse tipo de arquitetura, o POST dos formulários tem como destino o controlador e não os próprios formulários. No entanto, veremos que construir um formulário com componentes de servidor implica que esse formulário seja enviado para si mesmo.
7.3. O componente Label
7.3.1. Utilização
A tag <asp:label> permite inserir um texto dinâmico no código de apresentação de uma página. Portanto, ela não faz nada além do que a tag <%=variável%> utilizada até agora. O estudo dessa primeira tag nos permitirá descobrir o mecanismo das tags de servidor. Criamos uma página com uma parte de controle [form1.aspx.vb] e uma parte de apresentação [form1.aspx]. O objetivo é exibir a hora:

Esse problema já foi abordado no capítulo 2, e o leitor é convidado a consultá-lo caso deseje saber como foi tratado. O código de apresentação [form1.aspx] é o seguinte:
<%@ page src="form1.aspx.vb" inherits="form1" AutoEventWireup="false" %>
<HTML>
<HEAD>
<title>Webforms</title>
</HEAD>
<body>
<asp:Label Runat="server" ID="lblHeure" />
</body>
</HTML>
Estamos introduzindo a tag <asp:label>. Nas bibliotecas de tags, o atributo [runat="server"] é obrigatório. O atributo ID identifica o componente. O controlador deverá referenciá-lo com esse identificador. O código do controlador [form1.aspx.vb] é o seguinte:
Imports System.Web.UI.WebControls
Public Class form1
Inherits System.Web.UI.Page
Protected lblHeure As Label
Private Sub Page_Init(ByVal sender As Object, ByVal e As System.EventArgs) Handles MyBase.Init
' salva a consulta atual em request.txt na pasta da página
Dim requestFileName As String = Me.MapPath(Me.TemplateSourceDirectory) + "\request.txt"
Me.Request.SaveAs(requestFileName, True)
End Sub
Private Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles MyBase.Load
' insere a hora em lblHeure
lblHeure.Text = "Il est " + Date.Now.ToString("T")
End Sub
End Class
O controlador deve atribuir um valor ao objeto [lblHeure] do tipo [System.Web.UI.WebControls.Label]. Todos os objetos exibidos pelas tags <asp:> pertencem ao espaço de nomes [System.Web.UI.WebControls]. Portanto, é possível importar sistematicamente esse espaço de nomes:
Imports System.Web.UI.WebControls
O objeto [Label] possui diversas propriedades, entre elas a propriedade [Text], que representa o texto que será exibido pela tag <asp:label> correspondente. Aqui, atribuímos a essa propriedade a hora atual. Fazemos isso no procedimento [Form_Load] do controlador, que é executado sistematicamente. No procedimento [Form_Init], que também é sempre executado, mas antes do procedimento [Form_Load], armazenamos a solicitação do cliente em um arquivo [request.txt] na pasta do aplicativo. Teremos a oportunidade de examinar esse arquivo para compreender certos aspectos do funcionamento das páginas que utilizam tags de servidor.
O objeto [Label] possui diversas propriedades, métodos e eventos. Recomenda-se ao leitor que consulte a documentação sobre a classe [Label] para conhecê-los. Daqui em diante, será sempre assim. Para cada tag, apresentaremos apenas as poucas propriedades de que precisamos.
7.3.2. Os testes
Colocamos os arquivos (form1.aspx, form1.aspx.vb) em uma pasta <application-path> e iniciamos o Cassini com os parâmetros (<application-path>,/form1). Em seguida, acessamos a URL [http://localhost/form1/form1.aspx]. Obtemos o seguinte resultado:

O código HTML recebido pelo navegador é o seguinte:
<HTML>
<HEAD>
<title>Webforms</title>
</HEAD>
<body>
<span id="lblHeure">Il est 19:39:37</span>
</body>
</HTML>
Percebe-se que a tag do servidor
<asp:Label Runat="server" ID="lblHeure" />
foi transformada no seguinte código HTML:
Foi a propriedade [Text] do objeto [lblHeure] que foi inserida entre as tags <span> e </span>. A solicitação feita pelo cliente e armazenada em [request.txt] é a seguinte:
GET /form1/form1.aspx HTTP/1.1
Cache-Control: max-age=0
Connection: keep-alive
Keep-Alive: 300
Accept: application/x-shockwave-flash,text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,image/jpeg,image/gif;q=0.2,*/*;q=0,1
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Accept-Encoding: gzip,deflate
Accept-Language: en-us,en;q=0.5
Host: localhost
User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.7b) Gecko/20040316
Tudo muito normal.
7.3.3. Criando o aplicativo com WebMatrix
Criamos manualmente o código de apresentação da página [form1.aspx]. Esse método pode ser utilizado se você conhecer as tags. Nesse caso, basta um simples editor de texto para criar a interface do usuário. Para começar, muitas vezes é necessária uma ferramenta de design gráfico combinada com a geração automática de código, pois ainda não se conhece a sintaxe das tags necessárias. Agora, vamos criar o mesmo aplicativo com a ferramenta WebMatrix. Após iniciar o WebMatrix, selecionamos a opção [File/New File]:

Criamos uma página ASP.NET chamada [form2.aspx]. Após confirmar o assistente anterior, obtemos a janela de design da página [form2.aspx]:


Vale lembrar que WebMatrix coloca o código de controle da página e o de apresentação no mesmo arquivo, neste caso, [form2.aspx]. A aba [All] exibe o conteúdo desse arquivo de texto. Já é possível constatar que ele não está vazio:

A desvantagem desse tipo de ferramenta é que, muitas vezes, elas geram código desnecessário. É o que ocorre aqui, onde o WebMatrix gerou uma tag <form> HTML, embora não tenhamos a intenção de criar um formulário... Além disso, percebe-se que o documento não possui a tag <title>. Corrigimos esses dois problemas imediatamente para obter a seguinte nova versão:

O que chamamos de código controlador será inserido entre as tags <script> e </script>, o que garante uma separação, pelo menos visual, entre os dois tipos de código: controle e apresentação. Voltamos à guia [Design] para desenhar nossa interface. Uma lista de componentes está disponível em uma janela de ferramentas à esquerda da janela de design:

A janela de ferramentas oferece acesso a dois tipos de componentes:
- os componentes [WebControls], que se traduzem em tags <asp:>
- os componentes [HTML Elements], que se traduzem em tags HTML clássicas. No entanto, é possível adicionar aos atributos de uma tag HTML o atributo [runat="server"]. Nesse caso, a tag HTML e seus atributos ficam acessíveis ao controlador por meio de um objeto cujas propriedades correspondem às da tag HTML que ele representa. Anteriormente, chamamos essas tags de tags HTML do servidor.
Clique duas vezes no componente [Label] da lista de controles [WebControls]. Na guia [Design], obtém-se o seguinte resultado:

Na guia [All], o código passou a ser o seguinte:
<%@ Page Language="VB" %>
<html>
<head>
<title>webforms</title>
</head>
<body>
<asp:Label id="Label1" runat="server">Label</asp:Label>
</body>
</html>
Pode-se notar, em primeiro lugar, que a tag <script> foi perdida. Foi gerada uma tag <asp:label>. Ela tem o nome [Label1] e o valor [Label]. Voltemos à aba [Design] para alterar esses dois valores. Clicamos uma vez no componente [Label] para exibir a janela de propriedades desse componente, no canto inferior direito:

Recomenda-se ao leitor que consulte as propriedades do objeto [Label]. Duas delas nos interessam aqui:
- Texto: é o texto que o rótulo deve exibir — colocamos a string vazia (c.a.d. nada)
- ID: é o seu identificador — colocamos lblHeure
A aba [Design] fica assim:

e o código de [All] passa a ser:
<%@ Page Language="VB" %>
<html>
<head>
<title>webforms</title>
</head>
<body>
<asp:Label id="lblHeure" runat="server"></asp:Label>
</body>
</html>
A parte de apresentação da página está concluída. Resta-nos escrever o código de controle responsável por inserir a hora na propriedade [Text] de [lblHeure]. Adicionamos na guia [All] o seguinte código:
<%@ Page Language="VB" %>
<script runat="server">
Private Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles MyBase.Load
' insere a hora em lblHeure
lblHeure.Text = "Il est " + Date.Now.ToString("T")
End Sub
</script>
<html>
<head>
<title>webforms</title>
</head>
<body>
<asp:Label id="lblHeure" runat="server"></asp:Label>
</body>
</html>
Observe-se que, no código do controlador, o objeto [lblHeure] não é declarado como havia sido feito anteriormente:
Protected lblHeure As New System.Web.UI.WebControls.Label
De fato, todos os componentes de servidor <asp:> da parte de apresentação são declarados implicitamente na parte de código de controle. Portanto, declará-los gera um erro de compilação, indicando que o objeto já está declarado. Estamos prontos para a execução. Selecionamos a opção [View/Start] ou o atalho [F5]. O Cassini é iniciado automaticamente com os seguintes parâmetros:

Aceitamos esses valores. O navegador padrão do sistema é iniciado automaticamente para acessar a URL [http://localhost/form2.aspx]. Obtemos o seguinte resultado:

A partir de agora, utilizaremos principalmente a ferramenta WebMatrix para facilitar a criação e os testes dos pequenos programas que escreveremos.
7.4. O componente Literal
7.4.1. Utilização
A tag <asp:literal> permite inserir um texto dinâmico no código de apresentação de uma página, assim como a tag <asp:label>. Seu principal atributo é [Text], que representa o texto que será inserido tal como está no fluxo HTML da página. Essa tag é suficiente se não houver a intenção de formatar o texto que se deseja inserir no fluxo HTML. De fato, enquanto a classe [Label] permite aplicar formatação por meio de atributos como [BorderColor, BorderWidth, Font, ...], a classe [Literal] não possui nenhum desses atributos. O leitor poderá reproduzir integralmente o exemplo anterior, substituindo o componente [Label] por um componente [Literal].
7.5. O componente Button
7.5.1. Utilização
A tag <asp:Button> permite inserir um botão do tipo [Submit] em um formulário, o que traz consigo um gerenciamento de eventos semelhante ao encontrado em aplicativos do Windows. É esse ponto que queremos aprofundar aqui. Criamos a seguinte página [form3.aspx]:

Essa página, criada com o WebMatrix, possui três componentes:
n.º | nome | tipo | propriedades | função |
1 | Botão | text=Botão1 | botão enviar | |
2 | Botão | text=Botão2 | botão enviar | |
3 | Rótulo | text= | mensagem informativa |
O código gerado pelo WebMatrix para esta parte é o seguinte:
<%@ Page Language="VB" %>
<script runat="server">
</script>
<html>
<head>
<title>asp:button</title>
</head>
<body>
<form runat="server">
<p>
<asp:Button id="Button1" runat="server" Text="Bouton1"></asp:Button>
<asp:Button id="Button2" runat="server" Text="Bouton2"></asp:Button>
</p>
<p>
<asp:Label id="lblInfo" runat="server"></asp:Label>
</p>
</form>
</body>
</html>
Encontramos os componentes [Button] e [Label], utilizados na concepção gráfica da página, dentro das tags <asp:>. Observe a tag <form runat="server">, que foi gerada automaticamente. Trata-se de uma tag de servidor HTML, c.a.d. Uma tag clássica HTML, representada, no entanto, por um objeto que pode ser manipulado pelo controlador. O código da tag <form> será gerado a partir do valor que o controlador atribuir a esse objeto.
Vamos adicionar, na parte do código do controlador, o procedimento [Page_Init] que gerencia o evento [Init] da página. Colocamos nele o código que salva a solicitação do cliente no arquivo [request.txt]. Precisaremos disso para entender o mecanismo dos botões.
<script runat="server">
Private Sub Page_Init(ByVal sender As Object, ByVal e As System.EventArgs)
' salva a solicitação atual em request.txt na pasta da página
Dim requestFileName As String = Me.MapPath(Me.TemplateSourceDirectory) + "\request.txt"
Me.Request.SaveAs(requestFileName, True)
End Sub
</script>
Observe-se acima que não colocamos a cláusula [Handles MyBase.Init] após a declaração do procedimento [Page_Init]. De fato, o evento [Init] do objeto [Page] possui um manipulador padrão chamado [Page_Init]. Se utilizarmos esse nome de manipulador, a cláusula [Handles Page.Init] torna-se desnecessária. No entanto, incluí-la não causa nenhum erro.
7.5.2. Testes
Executamos o aplicativo sob WebMatrix por meio de [F5]. Obtemos a seguinte página:

O código HTML recebido pelo navegador é o seguinte:
<html>
<head>
<title>asp:button</title>
</head>
<body>
<form name="_ctl0" method="post" action="form3.aspx" id="_ctl0">
<input type="hidden" name="__VIEWSTATE" value="dDwxNTY0NjIwMjUwOzs+2mcnJczeuvF2PEfvmtv7uiUhWUw=" />
<p>
<input type="submit" name="Button1" value="Bouton1" id="Button1" />
<input type="submit" name="Button2" value="Bouton2" id="Button2" />
</p>
<p>
<span id="lblInfo"></span>
</p>
</form>
</body>
</html>
Observe os seguintes pontos:
- a tag <form runat="server"> foi transformada na tag HTML
Dois atributos foram definidos: [method="post"] e [action="form3.aspx"]. É possível atribuir valores diferentes a esses atributos? Tentaremos esclarecer esse ponto mais adiante. Por enquanto, basta saber que o formulário será enviado para a URL [form3.aspx]. Outros dois atributos, [name, id], também foram definidos. Na maioria das vezes, eles são ignorados. No entanto, se a página contiver código JavaScript executado no navegador, o atributo [name] da tag <form> é útil.
- As tags <asp:button> passaram a ser tags HTML de botões [submit]. Um clique em qualquer um desses botões provocará, portanto, um “post” do formulário [_ctl10] para a URL [form3.aspx].
- A tag <asp:label> tornou-se uma tag HTML <span>
- Um campo oculto [__VIEWSTATE] foi gerado com um valor estranho:
Esse campo representa, de forma codificada, o estado do formulário enviado ao cliente. Esse estado representa o valor de todos os componentes do formulário. Como [__VIEWSTATE] faz parte do formulário, seu valor será enviado junto com o restante para o servidor. Isso permitirá que o servidor saiba qual componente do formulário mudou de valor e, eventualmente, tome decisões. Essas decisões assumirão a forma de eventos do tipo “o TextBox tal mudou de valor”.
7.5.3. As solicitações do cliente
Depois de obter a página [form3.aspx] no navegador, solicitemos-a novamente e analisemos a solicitação enviada pelo navegador para obtê-la. Lembramos que nosso aplicativo a armazena no arquivo [request.txt] na pasta do aplicativo:
GET /form3.aspx HTTP/1.1
Connection: keep-alive
Keep-Alive: 300
Accept: application/x-shockwave-flash,text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,image/jpeg,image/gif;q=0.2,*/*;q=0.1
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Accept-Encoding: gzip,deflate
Accept-Language: en-us,en;q=0.5
Host: localhost
User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.7b) Gecko/20040316
Temos aqui um GET clássico. Agora, vamos clicar no botão [Bouton1] da página no navegador. Aparentemente, nada acontece. No entanto, sabemos que o formulário foi enviado. É o código HTML da página que indica isso. Isso é confirmado pelo novo conteúdo de [request.txt]:
POST /form3.aspx HTTP/1.1
Connection: keep-alive
Keep-Alive: 300
Content-Length: 80
Content-Type: application/x-www-form-urlencoded
Accept: application/x-shockwave-flash,text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,image/jpeg,image/gif;q=0.2,*/*;q=0.1
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Accept-Encoding: gzip,deflate
Accept-Language: en-us,en;q=0.5
Host: localhost
Referer: http://localhost/form3.aspx
User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.7b) Gecko/20040316
__VIEWSTATE=dDwxNTY0NjIwMjUwOzs%2B2mcnJczeuvF2PEfvmtv7uiUhWUw%3D&Button1=Bouton1
O primeiro cabeçalho HTTP indica claramente que o cliente enviou um POST para a URL [/form3.aspx]. A última linha mostra os valores enviados:
- o valor do campo oculto __VIEWSTATE
- o valor do botão que foi clicado
Se clicarmos em [Bouton2], os valores enviados pelo navegador são os seguintes:
O valor do campo oculto continua o mesmo, mas foi o valor de [Button2] que foi enviado. O servidor pode, portanto, saber qual botão foi utilizado. Ele usará essa informação para acionar um evento que poderá ser processado pela página, assim que ela for carregada.
7.5.4. Gerenciando o evento Click de um objeto Button
Vamos relembrar como funciona uma página .aspx. Ela é um objeto derivado da classe [Page]. Vamos chamar a classe derivada de [unePage]. Quando o servidor recebe uma solicitação para essa página, um objeto do tipo [unePage] é instanciado por meio da operação new unePage(...). Em seguida, o servidor gera dois eventos chamados [Init] e [Load], nessa ordem. O objeto [unePage] pode lidar com eles fornecendo os manipuladores de eventos [Page_Init] e [Page_Load]. Em seguida, outros eventos serão gerados. Teremos a oportunidade de voltar a esse assunto. Se a solicitação do cliente for um POST, o servidor gerará o evento [Click] do botão que provocou esse POST. Se a classe [unePage] tiver definido um manipulador para esse evento, ele será chamado. Vamos examinar esse mecanismo com o WebMatrix. Na guia [Design] do [form3.aspx], clique duas vezes no botão [Bouton1]. Somos então direcionados automaticamente para a guia [Code], no corpo de um procedimento chamado [Button1_Click]. Para entender melhor, vamos para a guia [All] e observemos o código completo. As seguintes alterações foram feitas:
<%@ Page Language="VB" %>
<script runat="server">
...
Sub Button1_Click(sender As Object, e As EventArgs)
End Sub
</script>
<html>
...
<body>
<form runat="server">
...
<asp:Button id="Button1" onclick="Button1_Click" runat="server" Text="Bouton1"></asp:Button>
</form>
</body>
</html>
Um novo atributo [onclick="Button1_Click"] foi adicionado à tag <asp:Button> de [Button1]. Esse atributo indica a procedimento responsável por processar o evento [Click] no objeto [Button1], neste caso, a procedimento [Button1_Click]. Agora basta escrever essa procedimento:
Sub Button1_Click(sender As Object, e As EventArgs)
' clique no botão 1
lblInfo.Text="Vous avez cliqué sur [Bouton1]"
End Sub
O procedimento insere uma mensagem informativa no rótulo [lblInfo]. Procedemos da mesma forma com o botão [Bouton2] para obter a seguinte nova página [form3.aspx]:
<%@ Page Language="VB" %>
<script runat="server">
Private Sub Page_Init(ByVal sender As Object, ByVal e As System.EventArgs)
' salva a solicitação atual em request.txt na pasta da página
Dim requestFileName As String = Me.MapPath(Me.TemplateSourceDirectory) + "\request.txt"
Me.Request.SaveAs(requestFileName, True)
End Sub
Sub Button1_Click(sender As Object, e As EventArgs)
' clique no botão 1
lblInfo.Text="Vous avez cliqué sur [Bouton1]"
End Sub
Sub Button2_Click(sender As Object, e As EventArgs)
' clique no botão 2
lblInfo.Text="Vous avez cliqué sur [Bouton2]"
End Sub
</script>
<html>
<head>
<title>asp:button</title>
</head>
<body>
<form runat="server">
<p>
<asp:Button id="Button1" onclick="Button1_Click" runat="server" Text="Bouton1"></asp:Button>
<asp:Button id="Button2" onclick="Button2_Click" runat="server" Text="Bouton2" BorderStyle="None"></asp:Button>
</p>
<p>
<asp:Label id="lblInfo" runat="server"></asp:Label>
</p>
</form>
</body>
</html>
Executamos o programa [F5] para obter a seguinte página:

Se analisarmos o código HTML recebido, perceberemos que ele não sofreu alterações em relação ao da versão anterior da página:
<html>
<head>
<title>asp:button</title>
</head>
<body>
<form name="_ctl0" method="post" action="form3.aspx" id="_ctl0">
<input type="hidden" name="__VIEWSTATE" value="dDwxNTY0NjIwMjUwOzs+2mcnJczeuvF2PEfvmtv7uiUhWUw=" />
<p>
<input type="submit" name="Button1" value="Bouton1" id="Button1" />
<input type="submit" name="Button2" value="Bouton2" id="Button2" />
</p>
<p>
<span id="lblInfo"></span>
</p>
</form>
</body>
</html>
Se clicarmos em [Bouton1], obtemos a seguinte resposta:

O código HTML recebido para essa resposta é o seguinte:
<html>
<head>
<title>asp:button</title>
</head>
<body>
<form name="_ctl0" method="post" action="form3.aspx" id="_ctl0">
<input type="hidden" name="__VIEWSTATE" value="dDwxNTY0NjIwMjUwO3Q8O2w8aTwxPjs+O2w8dDw7bDxpPDU+Oz47bDx0PHA8cDxsPFRleHQ7PjtsPFZvdXMgYXZleiBjbGlxdcOpIHN1ciBbQm91dG9uMV07Pj47Pjs7Pjs+Pjs+Pjs+4oO98Vd244kj0lPMXReWOwJ1WW0=" />
<p>
<input type="submit" name="Button1" value="Bouton1" id="Button1" />
<input type="submit" name="Button2" value="Bouton2" id="Button2" />
</p>
<p>
<span id="lblInfo">Vous avez cliqué sur [Bouton1]</span>
</p>
</form>
</body>
</html>
Podemos observar que o campo oculto [__VIEWSTATE] mudou de valor. Isso reflete a mudança no valor do componente [lblInfo].
7.5.5. Os eventos ao longo do ciclo de vida de um aplicativo ASP.NET
A documentação ASP.NET apresenta a lista de eventos gerados pelo servidor ao longo do ciclo de vida de um aplicativo:
- na primeira solicitação recebida pela aplicação, o evento [Start] no objeto [HttpApplication] da aplicação será gerado. Esse evento pode ser tratado pelo procedimento [Application_Start] do arquivo [global.asax] da aplicação.
Em seguida, teremos uma série de eventos que se repetirão para cada solicitação recebida:
- se a solicitação não tiver enviado um token de sessão, uma nova sessão é iniciada e um evento [Start] no objeto [Session] associado à solicitação é gerado. Esse evento pode ser tratado pelo procedimento [Session_Start] do arquivo [global.asax] do aplicativo.
- O servidor gera o evento [BeginRequest] no objeto [HttpApplication]. Ele pode ser tratado pelo procedimento [Application_BeginRequest] do arquivo [global.asax] do aplicativo.
- O servidor carrega a página solicitada pela consulta. Ele instanciará um objeto [Page] e, em seguida, gerará dois eventos nesse objeto: [Init] e, depois, [Load]. Esses dois eventos podem ser gerenciados pelos procedimentos [Page_Init] e [Page_Load] da página.
- A partir dos valores postados recebidos, o servidor irá gerar outros eventos: [TextChanged] para um componente [TextBox] cujo valor foi alterado, [CheckedChanged] para um botão de opção cujo valor foi alterado, [SelectedIndexChanged] para uma lista cujo elemento selecionado foi alterado, ... Teremos a oportunidade de mencionar os principais eventos para cada um dos componentes de servidor que apresentaremos. Cada evento E em um objeto denominado O pode ser tratado por um procedimento com o nome O_E.
- A ordem de tratamento dos eventos anteriores não é garantida. Portanto, os manipuladores não devem fazer nenhuma suposição sobre essa ordem. No entanto, tem-se a certeza de que o evento [Click] do botão que provocou o POST é tratado por último.
- Assim que a página estiver pronta, o servidor a enviará ao cliente. Antes disso, ele gera o evento [PreRender], que pode ser tratado pela rotina [Page_PreRender] da página.
- Assim que a resposta HTML for enviada ao cliente, a página será liberada da memória. Nesse momento, serão gerados dois eventos: [Unload] e [Disposed]. A página pode utilizar esses eventos para liberar recursos.
O aplicativo também pode receber eventos fora de uma solicitação do cliente:
- o evento [End] em um objeto [Session] do aplicativo ocorre quando uma sessão é encerrada. Isso pode acontecer por solicitação explícita do código de uma página ou porque o tempo de vida atribuído à sessão foi excedido. O procedimento [Session_End] do arquivo [global.asax] gerencia esse evento. Nele, geralmente são liberados recursos obtidos no [Session_Start].
- O evento [End] no objeto [HttpApplication] do aplicativo ocorre quando o aplicativo é encerrado. Isso ocorre, principalmente, quando o servidor web é desligado. O procedimento [Application_End] do arquivo [global.asax] gerencia esse evento. Nele, geralmente são liberados recursos obtidos no [Application_Start].
Destacam-se os seguintes pontos:
- o modelo de eventos anterior baseia-se em trocas clássicas cliente-servidor HTTP. Isso fica perfeitamente claro ao examinar os cabeçalhos HTTP trocados.
- o processamento dos eventos anteriores ocorre sempre no lado do servidor. O clique em um botão pode, é claro, ser processado por um script JavaScript no lado do servidor. Mas, nesse caso, não se trata de um evento do servidor, e estamos diante de uma tecnologia independente de ASP.NET.
Em que momento os eventos são processados, seja no lado do servidor (eventos relacionados aos componentes do servidor) ou no lado do navegador por meio de scripts JavaScript?
Tomemos o exemplo de uma lista suspensa. Quando o usuário altera o item selecionado nessa lista, o evento (alteração do item selecionado) pode ou não ser processado e, se for processado, pode ocorrer em diferentes momentos.
- Se quisermos processá-lo imediatamente, temos duas soluções:
- ele pode ser processado pelo navegador por meio de um script JavaScript. Nesse caso, o servidor não intervém. Para que isso seja possível, é necessário que a página possa ser reconstruída com os valores presentes nela.
- ele pode ser processado pelo servidor. Para isso, há apenas uma solução: o formulário deve ser enviado ao servidor para processamento. Temos, portanto, uma operação [submit]. Veremos que, nesse caso, utilizamos um componente de servidor chamado [DropDownList] e definimos seu atributo [AutoPostBack] como [true]. Isso significa que, em caso de alteração do elemento selecionado na lista suspensa, o formulário deve ser enviado imediatamente ao servidor. Nesse caso, o servidor gera, para o objeto [DropDownList], um código HTML associado a uma função JavaScript encarregada de executar um [submit] assim que ocorrer o evento “alteração do elemento selecionado”. Esse [submit] enviará o formulário ao servidor e, nele, serão inseridos campos ocultos para indicar que o [post] decorre de uma alteração na seleção da lista suspensa. O servidor gerará então o evento [SelectedIndexChanged], que a página poderá processar.
- Se desejarmos processá-lo, mas não imediatamente, definimos o atributo [AutoPostBack] do componente de servidor [DropDownList] como [false]. Nesse caso, o servidor gera, para o objeto [DropDownList], o código HTML clássico de uma lista <select> sem função JavaScript associada. Nada acontece, portanto, quando o usuário altera a seleção na lista suspensa. No entanto, quando ele enviar o formulário por meio de um botão [submit], por exemplo, o servidor poderá reconhecer que houve uma alteração na seleção. Vimos, de fato, que o formulário enviado ao servidor possuía um campo oculto [__VIEWSTATE], representando, de forma codificada, o estado de todos os elementos do formulário enviado. Quando o servidor recebe o novo formulário enviado pelo cliente, ele poderá verificar se o elemento selecionado na lista suspensa mudou ou não. Se sim, ele gerará o evento [SelectedIndexChanged], que a página poderá então processar. Para distinguir esse mecanismo do anterior, alguns autores afirmam que o evento “mudança de seleção” foi “armazenado em cache” quando ocorreu no navegador. Ele só será processado pelo servidor quando o navegador enviar o formulário, geralmente após um clique no botão [submit].
- Por fim, se não se desejar processar o evento, define-se o atributo [AutoPostBack] do componente de servidor [DropDownList] como [false] e não se escreve o manipulador do seu evento [SelectedIndexChanged].
Uma vez compreendido o mecanismo de tratamento de eventos, o desenvolvedor não conceberá um aplicativo web como um aplicativo Windows. De fato, se uma alteração na seleção de uma caixa de combinação em um aplicativo Windows pode ser usada para alterar imediatamente a aparência do formulário no qual ela se encontra, haverá mais hesitação em processar esse evento imediatamente em um aplicativo web, caso isso implique um “post” do formulário para o servidor e, portanto, uma ida e volta pela rede entre o cliente e o servidor. É por isso que a propriedade [AutoPostBack] dos componentes de servidor é definida como [false] por padrão. Além disso, o mecanismo [AutoPostBack], que se baseia em scripts JavaScript gerados automaticamente pelo servidor web no formulário enviado ao cliente, só pode ser utilizado se houver certeza de que o navegador do cliente autorizou a execução de scripts JavaScript. Os formulários são, portanto, frequentemente construídos da seguinte maneira:
- os componentes de servidor do formulário têm suas propriedades definidas de [AutoPostBack] a [false]
- o formulário possui um ou mais botões responsáveis por executar o [POST] do formulário
- no código do controlador da página, escrevem-se os manipuladores apenas dos eventos que se deseja gerenciar, na maioria das vezes o evento [Click] em um dos botões.
7.6. O componente TextBox
7.6.1. Utilização
A tag <asp:TextBox> permite inserir um campo de entrada no código de apresentação de uma página. Criamos uma página [form4.aspx] para obter a seguinte apresentação:

4321Esta página, criada com o WebMatrix, possui os seguintes componentes:
n.º | nome | tipo | propriedades | função |
1 | TextBox | AutoPostback=true Text= | campo de entrada | |
2 | TextBox | AutoPostback=false Texto= | campo de entrada | |
3 | Rótulo | text= | mensagem informativa sobre o conteúdo de [TextBox1] | |
3 | Rótulo | text= | mensagem informativa sobre o conteúdo de [TextBox2] |
O código gerado pelo WebMatrix para esta parte é o seguinte:
<%@ Page Language="VB" %>
<script runat="server">
</script>
<html>
<head>
<title>asp:textbox</title>
</head>
<body>
<form runat="server">
<p>
Texte 1 :
<asp:TextBox id="TextBox1" runat="server" AutoPostBack="True"></asp:TextBox>
</p>
<p>
Texte 2 :
<asp:TextBox id="TextBox2" runat="server"></asp:TextBox>
</p>
<p>
<asp:Label id="lblInfo1" runat="server"></asp:Label>
</p>
<p>
<asp:Label id="lblInfo2" runat="server"></asp:Label>
</p>
</form>
</body>
</html>
Na guia [Design], clique duas vezes no componente [TextBox1]. A estrutura do gerenciador do evento [TextChanged] desse objeto é então gerada (aba [All]):
<%@ Page Language="VB" %>
<script runat="server">
Sub TextBox1_TextChanged(sender As Object, e As EventArgs)
End Sub
</script>
<html>
...
<body>
...
<asp:TextBox id="TextBox1" runat="server" AutoPostBack="True" OnTextChanged="TextBox1_TextChanged"></asp:TextBox>
</p>
....
</form>
</body>
</html>
O atributo [OnTextChanged="TextBox1_TextChanged"] foi adicionado à tag <asp:TextBox id="TextBox1"> para designar o manipulador do evento [TextChanged] em [TextBox1]. É o procedimento [TextBox1_Changed] que estamos escrevendo agora.
Sub TextBox1_TextChanged(sender As Object, e As EventArgs)
' alteração do texto
lblInfo1.text=Date.now.Tostring("T") + ": evt [TextChanged] sur [TextBox1]. Texte 1=["+textbox1.Text+"]"
End Sub
No procedimento, inserimos no rótulo [lblInfo1] uma mensagem sinalizando o evento e indicando o conteúdo de [TextBox1]. Fazemos o mesmo para [TextBox2]. Também indicamos a hora para acompanhar melhor o processamento dos eventos. O código final de [form4.aspx] é o seguinte:
<%@ Page Language="VB" %>
<script runat="server">
Private Sub Page_Init(ByVal sender As Object, ByVal e As System.EventArgs)
' salva a solicitação atual em request.txt na pasta da página
Dim requestFileName As String = Me.MapPath(Me.TemplateSourceDirectory) + "\request.txt"
Me.Request.SaveAs(requestFileName, True)
End Sub
Sub TextBox1_TextChanged(sender As Object, e As EventArgs)
' alteração de texto
lblInfo1.text=Date.now.Tostring("T") + ": evt [TextChanged] sur [TextBox1]. Texte 1=["+textbox1.Text+"]"
End Sub
Sub TextBox2_TextChanged(sender As Object, e As EventArgs)
' alteração de texto
lblInfo2.text=Date.now.Tostring("T") + ": evt [TextChanged] sur [TextBox2]. Texte 2=["+textbox2.Text+"]"
End Sub
</script>
<html>
<head>
<title>asp:textbox</title>
</head>
<body>
<form runat="server">
<p>
Texte 1 :
<asp:TextBox id="TextBox1" runat="server" AutoPostBack="True" OnTextChanged="TextBox1_TextChanged"></asp:TextBox>
</p>
<p>
Texte 2 :
<asp:TextBox id="TextBox2" runat="server" OnTextChanged="TextBox2_TextChanged"></asp:TextBox>
</p>
<p>
<asp:Label id="lblInfo1" runat="server"></asp:Label>
</p>
<p>
<asp:Label id="lblInfo2" runat="server"></asp:Label>
</p>
</form>
</body>
</html>
Adicionamos o procedimento [Page_Init] para armazenar a solicitação do cliente, conforme o exemplo anterior.
7.6.2. Testes
Executamos o aplicativo com o procedimento WebMatrix por meio do procedimento [F5]. Obtemos a seguinte página:

O código HTML recebido pelo navegador é o seguinte:
<html>
<head>
<title>asp:textbox</title>
</head>
<body>
<form name="_ctl0" method="post" action="form4.aspx" id="_ctl0">
<input type="hidden" name="__EVENTTARGET" value="" />
<input type="hidden" name="__EVENTARGUMENT" value="" />
<input type="hidden" name="__VIEWSTATE" value="dDwtMTY4MDc0MTUxOTs7PoqpeSYSCX7lCiWZvw5p7u+/OrTD" />
<script language="javascript">
<!--
function __doPostBack(eventTarget, eventArgument) {
var theform = document._ctl0;
theform.__EVENTTARGET.value = eventTarget;
theform.__EVENTARGUMENT.value = eventArgument;
theform.submit();
}
// -->
</script>
<p>
Texte 1 :
<input name="TextBox1" type="text" id="TextBox1" onchange="__doPostBack('TextBox1','')" language="javascript" />
</p>
<p>
Texte 2 :
<input name="TextBox2" type="text" id="TextBox2" />
</p>
<p>
<span id="lblInfo1"></span>
</p>
<p>
<span id="lblInfo2"></span>
</p>
</form>
</body>
</html>
Há muitos elementos neste código que foram gerados automaticamente pelo servidor. Destacamos os seguintes pontos:
- há três campos ocultos: [__VIEWSTATE], que já vimos anteriormente, [__EventTarget] e [__EventArgument]. Esses dois últimos campos são usados para gerenciar o evento “change” do navegador no campo de entrada [TextBox1]
- as tags de servidor <asp:textbox> geraram as tags HTML <input type="text" ...>, que correspondem aos campos de entrada
- a tag de servidor <asp:textbox id="TextBox1" AutoPostBack="true" ...> gerou uma tag <input type="text" ...> com um atributo [onchange="__doPostBack('TextBox1','')"]. Esse atributo indica que, em caso de alteração do conteúdo de [TextBox1], a função JavaScript [_doPostBack(...)] deve ser executada. Ela é a seguinte:
<script language="javascript">
<!--
function __doPostBack(eventTarget, eventArgument) {
var theform = document._ctl0;
theform.__EVENTTARGET.value = eventTarget;
theform.__EVENTARGUMENT.value = eventArgument;
theform.submit();
}
// -->
</script>
O que a função acima faz? Ela atribui um valor a cada um dos dois campos ocultos [__EventTarget] e [__EventArgument] e, em seguida, envia o formulário. Assim, o formulário é enviado ao servidor. Esse é o efeito de [AutoPostBack]. O evento “change” do navegador faz com que o código “__doPostBack('TextBox1','')” seja executado. Conclui-se, portanto, que no formulário enviado, o campo oculto [__EventTarget] terá o valor 'TextBox1' e o campo oculto [__EventArgument] terá o valor ''. Isso permitirá que o servidor identifique o componente que gerou o POST.
- A tag de servidor <asp:textbox id="TextBox2"...> gerou uma tag <input type="text" ...> clássica porque seu atributo [AutoPostBack] não estava definido como [true].
- A tag <form> indica que o formulário será enviado para [form4.aspx]:
Vamos fazer nosso primeiro teste. Digite um texto no primeiro campo de entrada:

e, em seguida, coloquemos o cursor no segundo campo de entrada. Imediatamente, obtemos uma nova página:

O que aconteceu? Quando o cursor saiu do primeiro campo de entrada, o navegador verificou se o conteúdo dele havia mudado. Era o caso. Assim, ele gerou, no lado do navegador, o evento [change] no campo HTML [TextBox1]. Vimos então que uma função JavaScript foi executada e enviou o formulário para [form4.aspx]. Assim, essa página foi recarregada pelo servidor. Os valores enviados pelo formulário permitiram que o servidor, por sua vez, percebesse que o conteúdo da tag de servidor [TextBox1] havia mudado. O procedimento [TextBox1_Changed] foi, portanto, executado no lado do servidor. Ele inseriu uma mensagem no rótulo [lblInfo1]. Assim que essa procedimento foi concluída, [form4.aspx] foi enviada ao navegador. É por isso que agora temos um texto em [lblInfo1]. Dito isso, pode ser surpreendente encontrar algo no campo de entrada [TextBox1]. De fato, nenhum procedimento executado no lado do servidor atribui um valor a esse campo. Trata-se de um mecanismo geral dos formulários da web ASP.NET: o servidor retorna o formulário no estado em que o recebeu. Para isso, ele reatribui aos componentes o valor que foi enviado para eles pelo cliente. Para alguns componentes, o cliente não envia nenhum valor. Esse é o caso, por exemplo, dos componentes <asp:label>, que são convertidos nas tags <span>. Vale lembrar que o formulário possui um campo oculto que representa o estado do formulário quando ele é enviado ao cliente. Esse estado é a soma dos estados de todos os componentes do formulário, incluindo os componentes <asp:label>, se houver. Como o campo oculto [__VIEWSTATE] é enviado pelo navegador do cliente, o servidor é capaz de restaurar o estado anterior de todos os componentes do formulário. Resta apenas modificar aqueles cujos valores foram alterados pelo POST.
Vejamos agora, no [request.txt], a solicitação que foi feita pelo navegador:
POST /form4.aspx HTTP/1.1
Connection: keep-alive
Keep-Alive: 300
Content-Length: 137
Content-Type: application/x-www-form-urlencoded
Accept: application/x-shockwave-flash,text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,image/jpeg,image/gif;q=0.2,*/*;q=0,1
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Accept-Encoding: gzip,deflate
Accept-Language: en-us,en;q=0.5
Host: localhost
Referer: http://localhost/form4.aspx
User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.7b) Gecko/20040316
__EVENTTARGET=TextBox1&__EVENTARGUMENT=&__VIEWSTATE=dDwtMTY4MDc0MTUxOTs7PoqpeSYSCX7lCiWZvw5p7u%2B%2FOrTD&TextBox1=premier+texte&TextBox2=
É possível ver claramente o POST, bem como os parâmetros enviados. Voltemos ao nosso navegador e digitemos um texto no segundo campo de entrada:

Voltemos ao campo de entrada nº 1 para digitar um novo texto: desta vez, nada acontece. Por quê? Porque o campo de entrada [TextBox2] não possui a propriedade [AutoPostBack] em [true]; portanto, a tag <input type="text"...> gerada para ele não suporta o evento [Change], conforme mostra seu código HTML:
Portanto, nenhum evento ocorre ao sair do campo de entrada nº 2. Agora, vamos inserir um novo texto no campo nº 1:

Saímos do campo de entrada nº 1. Imediatamente, o evento [Change] nesse campo é detectado e o formulário é enviado ao servidor, que retorna a seguinte página:

O que aconteceu? Em primeiro lugar, o navegador enviou o formulário. Esse fato é registrado na solicitação do cliente armazenada em [request.txt]:
POST /form4.aspx HTTP/1.1
....
User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.7b) Gecko/20040316
__EVENTTARGET=TextBox1&__EVENTARGUMENT=&__VIEWSTATE=dDwtMTY4MDc0MTUxOTt0PDtsPGk8MT47PjtsPHQ8O2w8aTwxPjtpPDU%2BOz47bDx0PHA8cDxsPFRleHQ7PjtsPHByZW1pZXIgdGV4dGU7Pj47Pjs7Pjt0PHA8cDxsPFRleHQ7PjtsPDE4OjQyOjI5OiBldnQgW1RleHRDaGFuZ2VkXSBzdXIgW1RleHRCb3gxXS4gVGV4dGUgMT1bcHJlbWllciB0ZXh0ZV07Pj47Pjs7Pjs%2BPjs%2BPjs%2BxLOermpUUUz5rTAa%2FFsjda6lVmo%3D&TextBox1=troisi%C3%A8me+texte&TextBox2=second+texte
O servidor começa por restaurar os valores anteriores dos componentes por meio do campo oculto [__VIEWSTATE] que o cliente lhe enviou. Por meio dos campos enviados [TextBox1] e [TextBox2], ele atribuirá aos componentes [TextBox1] e [TextBox2] os valores que lhe foram enviados. É por meio desse mecanismo que o cliente recuperará o formulário exatamente como foi enviado. Em seguida, ainda graças aos campos enviados [__VIEWSTATE], [TextBox1] e [TextBox2], o servidor detectará que os valores dos campos de entrada [TextBox1] e [TextBox2] foram alterados. Portanto, ele irá gerar os eventos [TextChanged] para esses dois objetos. Os procedimentos [TextBox1_TextChanged] e [TextBox2_TextChanged] serão executados, e os rótulos [labelInfo1] e [labelInfo2] receberão um novo valor. Em seguida, a página [form4.aspx], assim modificada, é enviada de volta ao cliente.
Agora, modificamos novamente o campo de entrada nº 1:

Quando retiramos o cursor de entrada do campo 1, o evento [Change] ocorre no navegador. Em seguida, ocorre a sequência de eventos já explicada (envio do navegador para o servidor, ..., envio da resposta do servidor). Obtemos a seguinte resposta:

Devido à hora exibida em cada uma das mensagens, percebemos que apenas o procedimento [TextBox1_Changed] foi executado no servidor. O procedimento [TextBox2_TextChanged] não foi executado justamente porque o valor de [TextBox2] não se alterou. Por fim, vamos inserir um novo texto no campo nº 2:

Em seguida, posicionemos o cursor no campo nº 1 e, depois, voltemos a colocá-lo no campo nº 2. A página não muda. Por quê? Como não alteramos o valor do campo nº 1, o evento do navegador [Change] não ocorre quando saímos desse campo. Consequentemente, o formulário não é enviado ao servidor. Portanto, nada muda na página. É o fato de o conteúdo de [lblInfo2] não mudar que nos mostra que não há POST. Se houvesse um, o servidor detectaria que o conteúdo de [TextBox2] mudou e deveria refletir esse fato em [lblInfo2].
Conclui-se deste exemplo que não há sentido em atribuir a propriedade [AutoPostBack] de um [TextBox] a um [true]. Isso provoca uma troca de dados cliente-servidor desnecessária na maioria das vezes.
7.6.3. A função do campo __VIEWSTATE
Vimos que o servidor inseria sistematicamente um campo oculto chamado __VIEWSTATE no formulário que gerava. Mencionamos que esse campo representava o estado do formulário e que, se esse campo oculto fosse devolvido ao servidor, ele seria capaz de reconstituir o valor anterior do formulário. O estado de um formulário é a soma dos estados de seus componentes. Cada um deles possui uma propriedade [EnableViewState] com valor booleano que indica se o estado do componente deve ou não ser inserido no campo oculto [__VIEWSATE]. Por padrão, essa propriedade tem o valor [true], fazendo com que o estado de todos os componentes de um formulário seja colocado em [__VIEWSTATE]. Às vezes, isso não é desejável.
Vamos fazer alguns testes para entender melhor a função da propriedade [EnableViewState]. Vamos definir essa propriedade como [false] para os dois campos de entrada:
...
<asp:TextBox id="TextBox1" runat="server" OnTextChanged="TextBox1_TextChanged" AutoPostBack="True" EnableViewState="False"></asp:TextBox>
...
<asp:TextBox id="TextBox2" runat="server" OnTextChanged="TextBox2_TextChanged" EnableViewState="False"></asp:TextBox>
...
Vamos agora executar o aplicativo e digitar um primeiro texto no campo nº 1 antes de passar para o campo nº 2. Em seguida, é enviada uma solicitação POST ao servidor e obtemos a seguinte resposta:

Digite um texto no campo nº 2, altere o do campo nº 1 e, em seguida, volte ao campo nº 2 (nessa ordem). Uma nova chamada POST é enviada ao servidor e obtemos a seguinte resposta:

Por enquanto, tudo continua como antes. Agora, vamos alterar o conteúdo do campo nº 1 e, em seguida, passar para o campo nº 2. É enviada uma nova solicitação POST. A resposta do servidor é a seguinte:

Desta vez, houve uma alteração. O servidor detectou um evento [TextChanged] no campo nº 2, uma vez que a hora do [lblInfo2] foi alterada. No entanto, não houve nenhuma alteração. Isso se explica pela propriedade [EnableViewState=false] de [TextBox2]. Ela faz com que o servidor não tenha inserido no campo [__VIEWSTATE] do formulário o estado anterior de [TextBox2]. Isso equivale a atribuir a ele a string vazia como estado anterior. Quando ocorreu o POST devido à alteração do conteúdo de [TextBox1], o servidor comparou o valor atual de [TextBox2] — que era [deux] — com seu valor anterior (a string vazia). Concluiu-se, então, que o valor de [TextBox2] havia mudado e gerou o evento [TextChanged] para [TextBox2]. É possível validar esse funcionamento inserindo a string vazia em [TextBox2]. De acordo com o que acabou de ser explicado, o servidor não deveria gerar o evento [TextChanged] para [TextBox2]. Vamos tentar:

Foi exatamente isso que aconteceu. A hora e o conteúdo de [lblInfo2] mostram que o procedimento [TextBox2_TextChanged] não foi executado. Tendo isso em mente, vamos examinar a propriedade [EnableViewState] dos quatro componentes do formulário:
desejamos manter o estado desse componente para que o servidor saiba se ele foi alterado ou não | |
idem | |
Não queremos manter o estado desse componente. Queremos que o texto seja recalculado a cada novo POST. Se não for recalculado, ele deve ficar vazio. Tudo isso é obtido corretamente com [EnableViewState=false] | |
idem |
Nossa página de apresentação fica da seguinte forma:
...
<asp:TextBox id="TextBox1" runat="server" OnTextChanged="TextBox1_TextChanged" AutoPostBack="True"></asp:TextBox>
...
<asp:TextBox id="TextBox2" runat="server" OnTextChanged="TextBox2_TextChanged"></asp:TextBox>
....
<asp:Label id="lblInfo1" runat="server" enableviewstate="False"></asp:Label>
...
<asp:Label id="lblInfo2" runat="server" enableviewstate="False"></asp:Label>
...
Vamos repetir a mesma série de testes de antes. No ponto em que obtivemos a tela
version 1

, agora obtemos:
version 2

Nesta etapa, alterávamos o conteúdo do campo 1 sem alterar o do campo 2, fazendo com que o procedimento [TextBox2_TextChanged] não fosse executado no servidor, o que implicava que o campo [lblInfo2] não recebia um novo valor. Portanto, ele é exibido com seu valor anterior. Na versão 1, [EnableViewState=true], esse valor anterior era o valor inserido. Na versão 2, [EnableViewState=false], esse valor anterior é a string vazia.
Às vezes, não faz sentido manter o estado anterior dos componentes. Em vez de definir [EnableViewState=false] para cada um deles, é possível declarar que a página não deve manter seu estado. Isso é feito na diretiva [Page] do código de apresentação:
Nesse caso, independentemente do valor de sua propriedade [EnableViewState], o estado de um componente não é inserido no campo oculto [__VIEWSTATE]. Tudo ocorre, então, como se seu estado anterior fosse a string vazia.
Vamos agora utilizar o cliente [curl] para destacar outros mecanismos. Primeiro, solicitamos a URL [http://localhost/form4.aspx]:
dos>curl --include --url http://localhost/form4.aspx
HTTP/1.1 200 OK
Server: Microsoft ASP.NET Web Matrix Server/0.6.0.0
Date: Sun, 04 Apr 2004 17:51:14 GMT
Cache-Control: private
Content-Type: text/html; charset=utf-8
Content-Length: 1077
Connection: Close
<html>
<head>
<title>asp:textbox</title>
</head>
<body>
<form name="_ctl0" method="post" action="form4.aspx" id="_ctl0">
<input type="hidden" name="__EVENTTARGET" value="" />
<input type="hidden" name="__EVENTARGUMENT" value="" />
<input type="hidden" name="__VIEWSTATE" value="dDwtMTY4MDc0MTUxOTs7PoqpeSYSCX7lCiWZvw5p7u+/OrTD" />
<script language="javascript">
<!--
function __doPostBack(eventTarget, eventArgument) {
var theform = document._ctl0;
theform.__EVENTTARGET.value = eventTarget;
theform.__EVENTARGUMENT.value = eventArgument;
theform.submit();
}
// -->
</script>
<p>
Texte 1 :
<input name="TextBox1" type="text" id="TextBox1" onchange="__doPostBack('TextBox1','')" language="javascript" />
</p>
<p>
Texte 2 :
<input name="TextBox2" type="text" id="TextBox2" />
</p>
<p>
<span id="lblInfo1"></span>
</p>
<p>
<span id="lblInfo2"></span>
</p>
</form>
</body>
</html>
Recebemos do servidor o código HTML, proveniente de [form4.aspx]. Ele não difere daquele que o navegador havia recebido. Vale lembrar aqui a solicitação feita pelo navegador ao enviar o formulário:
POST /form4.aspx HTTP/1.1
Connection: keep-alive
...
User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.7b) Gecko/20040316
__EVENTTARGET=TextBox1&__EVENTARGUMENT=&__VIEWSTATE=dDwtMTY4MDc0MTUxOTs7PoqpeSYSCX7lCiWZvw5p7u%2B%2FOrTD&TextBox1=premier+texte&TextBox2=
Vamos fazer o mesmo POST, mas sem enviar o campo [__VIEWSTATE]:
dos>curl --include --url http://localhost/form4.aspx --data __EVENTTARGET=TextBox1 --data __EVENTARGUMENT= --data TextBox1=primeiro+texto --data TextBox2=
...................
<p>
Texte 1 :
<input name="TextBox1" type="text" value="premier texte" id="TextBox1" onchange="__doPostBack('TextBox1','')" language="javascript" />
</p>
<p>
Texte 2 :
<input name="TextBox2" type="text" id="TextBox2" />
</p>
<p>
<span id="lblInfo1">19:57:48: evt [TextChanged] sur [TextBox1]. Texte 1=[premier texte]</span>
</p>
<p>
<span id="lblInfo2"></span>
</p>
..............
Observe os seguintes pontos:
- o servidor detectou um evento [TextChanged] no [TextBox1], uma vez que gerou o texto [lblInfo1]. A ausência do [__VIEWSTATE] não o impediu. Na ausência desse valor, ele presume que o valor anterior de um campo de entrada é a string vazia.
- Ele conseguiu transferir o texto inserido para [TextBox1] para o atributo [value] da tag [TextBox1], de modo que o campo [TextBox1] reaparecesse com o valor inserido. Para isso, ele não precisa do [__VIEWSTATE], mas apenas do valor enviado para o [TextBox1]
Agora, vamos repetir a mesma consulta sem alterar nada. Obtemos a seguinte nova resposta:
dos>curl --include --url http://localhost/form4.aspx --data __EVENTTARGET=TextBox1 --data __EVENTARGUMENT= --data TextBox1=primeiro+texto --data TextBox2=
<p>
Texte 1 :
<input name="TextBox1" type="text" value="premier texte" id="TextBox1" onchange="__doPostBack('TextBox1','')" language="javascript" />
</p>
<p>
Texte 2 :
<input name="TextBox2" type="text" id="TextBox2" />
</p>
<p>
<span id="lblInfo1">20:05:47: evt [TextChanged] sur [TextBox1]. Texte 1=[premier texte]</span>
</p>
<p>
<span id="lblInfo2"></span>
</p>
Na ausência de [__VIEWSTATE], o servidor não conseguiu perceber que o campo [TextBox1] não havia mudado de valor. Ele, então, age como se o valor anterior fosse a string vazia. Por isso, ele gerou aqui o evento [TextChanged] em [TextBox1]. Vamos repetir a mesma consulta, mas desta vez com o campo [TextBox1] vazio e [TextBox2] preenchido:
dos>curl --include --url http://localhost/form4.aspx --data __EVENTTARGET=TextBox1 --data __EVENTARGUMENT= --data TextBox2=segundo+texto --data TextBox1=
......
<p>
Texte 1 :
<input name="TextBox1" type="text" id="TextBox1" onchange="__doPostBack('TextBox1','')" language="javascript" />
</p>
<p>
Texte 2 :
<input name="TextBox2" type="text" value="second texte" id="TextBox2" />
</p>
<p>
<span id="lblInfo1"></span>
</p>
<p>
<span id="lblInfo2">20:11:54: evt [TextChanged] sur [TextBox2]. Texte 2=[second texte]</span>
</p>
......
Na ausência de [__VIEWSTATE], o valor anterior de [TextBox1] foi considerado como a string vazia. Como o valor lançado de [TextBox1] também era a string vazia, o evento [TextChanged] em [TextBox1] não foi gerado. O procedimento [TextBox1_TextChanged] não foi executado e, portanto, o campo [lblInfo1] não recebeu um novo valor. Sabe-se que, nesse caso, o componente mantém seu valor anterior. No entanto, neste caso, isso não ocorreu: [lblInfo1] perdeu seu valor anterior. Isso porque esse valor é buscado em [__VIEWSTATE]. Como esse campo está ausente, a string vazia foi atribuída a [lblInfo1]. Para [TextBox2], o servidor comparou seu valor postado, [second texte], com seu valor anterior. Na ausência de [__VIEWSTATE], esse valor anterior é igual à string vazia. Como o valor postado de [TextBox2] é diferente da string vazia, o evento [TextChanged] em [TextBox2] foi gerado. O procedimento [TextBox2_TextChanged] foi executado e o campo [lblInfo2] recebeu um novo valor.
Pode-se questionar se os parâmetros [__EVENTTARGET] e [__EVENTARGUMENT] são realmente úteis. Ao não enviar esses parâmetros, o servidor não saberá por qual evento o [submit] foi acionado. Vamos tentar:
dos>curl --include --url http://localhost/form4.aspx --data TextBox2=segundo+texto --data TextBox1=primeiro+texto
..............................
<p>
Texte 1 :
<input name="TextBox1" type="text" id="TextBox1" onchange="__doPostBack('TextBox1','')" language="javascript" />
</p>
<p>
Texte 2 :
<input name="TextBox2" type="text" id="TextBox2" />
</p>
<p>
<span id="lblInfo1"></span>
</p>
<p>
<span id="lblInfo2"></span>
</p>
</form>
.....................
Percebe-se que nenhum evento [TextChanged] foi processado. Além disso, os campos lançados [TextBox1] e [TextBox2] não apresentam os valores lançados. Na verdade, tudo ocorre como se tivéssemos executado um GET. Tudo volta ao normal se o campo [__EVENTTARGET] estiver entre os campos postados, mesmo que não tenha nenhum valor:
dos>curl --include --url http://localhost/form4.aspx --data __EVENTTARGET= --data TextBox2=segundo+texto --data TextBox1=primeiro+texto
.......
<p>
Texte 1 :
<input name="TextBox1" type="text" value="premier texte" id="TextBox1" onchange="__doPostBack('TextBox1','')" language="javascript" />
</p>
<p>
Texte 2 :
<input name="TextBox2" type="text" value="second texte" id="TextBox2" />
</p>
<p>
<span id="lblInfo1">20:34:14: evt [TextChanged] sur [TextBox1]. Texte 1=[premier texte]</span>
</p>
<p>
<span id="lblInfo2">20:34:14: evt [TextChanged] sur [TextBox2]. Texte 2=[second texte]</span>
</p>
........
7.6.4. Outras propriedades do componente TextBox
O componente de servidor [TextBox] também permite gerar as tags HTML <input type="password"..> e <textarea>..</textarea>, c.a.d. Essas tags correspondem, respectivamente, a um campo de entrada protegido e a um campo de entrada multilinha. Essa geração é controlada pela propriedade [TextMode] do componente [TextBox]. Ela possui três valores possíveis:
valor | tag HTML gerada |
<input type="text" ...> | |
<textarea>...</textarea> | |
<input type="password ...> |
Analisamos o uso dessas propriedades com o seguinte exemplo [form5.aspx]:

n.º | nome | tipo | propriedades | função |
1 | Botão | botão [submit] — serve para adicionar o conteúdo de [TextBox1] ao de [TextBox2] | ||
2 | TextBox | TextMode=Password Text= | campo de entrada protegido | |
3 | TextBox | TextMode=Multilinhas Text= | agrupa as entradas feitas em [TextBox1] |
A propriedade [EnableViewState] da página é definida como [false]. No lado do servidor, gerenciamos o evento de clique no botão [btnAjouter]:
Sub btnAjouter_Click(sender As Object, e As EventArgs)
' junta-se o conteúdo de [textBox1] ao de [TextBox2]
textbox2.text=textbox2.text + textbox1.text+controlchars.crlf
End Sub
Para entender esse código, é preciso lembrar como o POST de um formulário é processado. As rotinas [Page_Init] e [Page_Load] são executadas primeiro. Em seguida, vêm todas as rotinas de eventos armazenadas em cache. Por fim, é executado o procedimento que gerencia o evento que provocou o [POST], neste caso, o procedimento [btnAjouter_Click]. Quando os gerenciadores de eventos são executados, todos os componentes da página que possuem um valor no POST assumem esse valor. Os demais recuperaram seu valor anterior, caso sua propriedade [EnableViewState] estivesse definida como [true], ou seu valor de design, caso sua propriedade [EnableViewState] estivesse definida como [false]. Aqui, os valores dos campos [TextBox1] e [TextBox2] farão parte do POST inserido pelo cliente. Da mesma forma, no código anterior, [textbox1.text] terá como valor o valor lançado pelo cliente, assim como [textbox2.text]. O procedimento [btnAjouter_Click] insere no campo [TextBox2] o valor enviado para [TextBox2] somado ao valor enviado para [TextBox1], somado ao caractere de fim de linha [ControlChars.CrLf], definido no espaço de nomes [Microsoft.VisualBasic]. Não é necessário importar esse espaço, pois o servidor web o importa por padrão.
O código final de [form5.aspx] é o seguinte:
<%@ Page Language="VB" EnableViewState="False" %>
<script runat="server">
Sub btnAjouter_Click(sender As Object, e As EventArgs)
' adiciona-se o conteúdo de [textBox1] ao de [TextBox2]
textbox2.text+=textbox1.text+controlchars.crlf
End Sub
</script>
<html>
<head>
</head>
<body>
<form runat="server">
<p>
<asp:Button id="btnAjouter" onclick="btnAjouter_Click" runat="server" Text="Ajouter" EnableViewState="False"></asp:Button>
<asp:TextBox id="TextBox1" runat="server" TextMode="Password" Width="353px" EnableViewState="False"></asp:TextBox>
</p>
<p>
<asp:TextBox id="TextBox2" runat="server" TextMode="MultiLine" Width="419px" Height="121px" EnableViewState="False"></asp:TextBox>
</p>
</form>
</body>
</html>
Um pouco acima, apresentamos a captura de tela de uma execução.
7.7. O componente DropDownList
A tag <asp:DropDownList> permite inserir uma lista suspensa no código de apresentação de uma página. Criamos uma página [form6.aspx] para obter a seguinte apresentação:

n.º | nome | tipo | propriedades | função |
1 | DropDownList | AutoPostback=true EnableViewState=true | lista suspensa | |
2 | Rótulo | EnableViewState=false | mensagem informativa |
O código de apresentação gerado é o seguinte:
Page Language="VB" %>
<script runat="server">
</script>
<html>
<head>
</head>
<body>
<form runat="server">
<p>
<asp:DropDownList id="DropDownList1" runat="server" OnSelectedIndexChanged="DropDownList1_SelectedIndexChanged" AutoPostBack="True"></asp:DropDownList>
</p>
<p>
<asp:Label id="lblInfo" runat="server" enableviewstate="False"></asp:Label>
</p>
</form>
</body>
</html>
No momento, a lista suspensa não contém nenhum item. Vamos preenchê-la no procedimento [Page_Load]. Para isso, precisamos conhecer algumas das propriedades e métodos da classe [DropDownList]:
coleção do tipo [ListItemCollection] dos elementos da lista suspensa. Os membros dessa coleção são do tipo [ListItem]. | |
número de elementos da coleção [Items] | |
elemento nº i da lista — do tipo [ListItem] | |
para adicionar um novo elemento [ListItem] à coleção [Items] | |
para excluir todos os elementos da coleção [Items] | |
para excluir o elemento nº i da coleção [Items] | |
1º elemento [ListItem] da coleção [Items] cuja propriedade [Selected] está definida como verdadeiro | |
número do elemento [SelectedItem] na coleção [Items] |
Os elementos da coleção [Items] da classe [DropDownList] são do tipo [ListItem]. Cada elemento [ListItem] gera uma tag HTML <option>:
Descrevemos algumas propriedades e métodos da classe [ListItem]:
construtor — cria um elemento [ListItem] com as propriedades [texte] e [value]. Um elemento ListItem(T,V) dará origem à tag HTML <option value="V">T</option>. A classe [ListITem] permite, portanto, descrever os elementos de uma lista HTML | |
booleano. Se for verdadeiro, a opção correspondente da lista HTML terá o atributo [selected="selected"]. Esse atributo indica ao navegador que o elemento correspondente deve aparecer selecionado na lista HTML | |
o texto T da opção HTML <option value="V" [selected="selected"]>T</option> | |
o valor V do atributo [Value] da opção HTML <option value="V" [selected="selected"]>T</option> |
Temos informações suficientes para inserir, no procedimento [Page_Load] da página, o código de preenchimento da lista suspensa [DropDownList1]:
Sub Page_Load(sender As Object, e As EventArgs)
' preenche-se o combo se for a primeira vez que for chamado
if not IsPostBack then
dim valeurs() as String = {"1","2","3","4"}
dim textes() as String = {"un","deux","trois","quatre"}
dim i as integer
for i=0 to valeurs.length-1
DropDownList1.Items.Add(new ListItem(textes(i),valeurs(i)))
next
end if
end sub
Com o componente [DropDownList1] assim inicializado, sua tradução HTML será a seguinte:
<select name="DropDownList1" id="DropDownList1" onchange="__doPostBack('DropDownList1','')" language="javascript">
<option value="1">un</option>
<option value="2">deux</option>
<option value="3">trois</option>
<option value="4">quatre</option>
</select>
Sabemos que o procedimento [Page_Load] é executado sempre que a página [form6.aspx] é exibida. Essa página é chamada pela primeira vez por um GET e, em seguida, por um POST sempre que o usuário seleciona um novo item na lista suspensa. É necessário executar o código de preenchimento dessa lista na [Page_Load] todas as vezes? A resposta depende do atributo [EnableViewState] do componente [DropDownList1]. Se esse atributo estiver definido como verdadeiro, sabe-se que o estado do componente [DropDownList1] será mantido ao longo das consultas no campo oculto [__VIEWSTATE]. Esse estado inclui duas coisas:
- a lista de todos os valores da lista suspensa
- o valor do elemento selecionado nessa lista
Parece, então, tentador definir a propriedade [EnableViewState] do componente [DropDownList1] como [true], para não precisar recalcular os valores a serem inseridos na lista. O problema, porém, é que, como o procedimento [Page_Load] é executado sempre que a página [form6.aspx] é solicitada, esses valores serão calculados de qualquer maneira. O objeto [Page], do qual [form6.aspx] é uma instância, possui um atributo [IsPostBack] com valor booleano. Se esse atributo estiver definido como verdadeiro, isso significa que a página foi chamada por um POST. Se for falso, significa que a página foi chamada por um GET. Em nosso sistema de comunicação cliente-servidor, o cliente sempre solicita a mesma página [form6.aspx] ao servidor. Na primeira vez, ele a solicita com um GET; nas vezes seguintes, com um POST. Conclui-se, portanto, que a propriedade [IsPostBack] pode nos servir para detectar a primeira chamada GET do cliente. Os valores da lista suspensa só são gerados nessa primeira chamada. Nas solicitações seguintes, esses valores serão gerados pelo mecanismo do [VIEWSTATE]. Em outras situações, o conteúdo de uma lista pode variar de uma solicitação para outra e, portanto, deve ser recalculado a cada uma delas. Nesse caso, definirá-se o atributo [EnableViewState] da lista como [false] para evitar um cálculo duplo desnecessário do conteúdo da lista, a menos que seja necessário saber quais elementos foram selecionados anteriormente na lista, pois essa informação é mantida no [VIEWSTATE].
O atributo [AutoPostBack] da lista [DropDownList1] foi definido como verdadeiro. Isso significa que o navegador enviará o formulário assim que detectar o evento “alteração do elemento selecionado” na lista suspensa. O servidor, por sua vez, detectará, por meio do [VIEWSTATE] e dos valores enviados, que o elemento selecionado no componente [DropDownList1] foi alterado. Ele então acionará o evento [SelectedIndexChanged] nesse componente. Trataremos isso com o seguinte procedimento:
Sub DropDownList1_SelectedIndexChanged(sender As Object, e As EventArgs)
' alteração da seleção
lblInfo.text="Elément sélectionné : texte="+dropdownlist1.selecteditem.text+ _
" valeur=" + dropdownlist1.selecteditem.value + _
" index="+ dropdownlist1.selectedindex.tostring
End Sub
Quando esse procedimento é executado, o objeto [DropDownList1] recuperou seus elementos do tipo [ListItem] por meio do [VIEWSTATE]. Além disso, um deles, do tipo [ListItem], tem seu atributo [Selected] definido como verdadeiro — aquele cujo valor foi enviado pelo navegador. É possível acessar esse elemento de várias maneiras:
é o primeiro elemento [ListItem] da lista cujo atributo [Selected] está definido como verdadeiro | |
corresponde à parte [texte] da tag HTML do elemento <option value="...">texto</option> selecionado pelo usuário | |
corresponde à parte [value] da tag HTML do elemento <option value="...">texto</option> selecionado pelo usuário | |
número na coleção [DropDownList1.Items] do primeiro elemento [ListItem] cujo atributo [Selected] está definido como verdadeiro |
O código final de [form6.aspx] é o seguinte:
<%@ Page Language="VB" %>
<script runat="server">
Sub Page_Load(sender As Object, e As EventArgs)
' preenche-se o campo combinado se for a primeira vez que se é chamado
if not IsPostBack then
dim valeurs() as String = {"1","2","3","4"}
dim textes() as String = {"un","deux","trois","quatre"}
dim i as integer
for i=0 to valeurs.length-1
DropDownList1.Items.Add(new ListItem(textes(i),valeurs(i)))
next
end if
end sub
Sub DropDownList1_SelectedIndexChanged(sender As Object, e As EventArgs)
' alteração na seleção
lblInfo.text="Elément sélectionné : texte="+dropdownlist1.selecteditem.text+ _
" valeur=" + dropdownlist1.selecteditem.value + _
" index="+ dropdownlist1.selectedindex.tostring
End Sub
</script>
<html>
<head>
</head>
<body>
<form runat="server">
<p>
<asp:DropDownList id="DropDownList1" runat="server" OnSelectedIndexChanged="DropDownList1_SelectedIndexChanged" AutoPostBack="True"></asp:DropDownList>
</p>
<p>
<asp:Label id="lblInfo" runat="server" enableviewstate="False"></asp:Label>
</p>
</form>
</body>
</html>
7.8. O componente ListBox
A tag <asp:ListBox> permite inserir uma lista no código de apresentação de uma página. Criamos [form7.aspx] para obter a seguinte apresentação:

n.º | nome | tipo | propriedades | função |
1 | TextBox | EnableViewState=false | campo de entrada | |
2 | Botão | botão [submit] que transfere para a Lista 1 o conteúdo de txtSaisie, caso este não esteja vazio. | ||
3 | ListBox | EnableViewState=true SelectionMode=Single | lista de valores com seleção única | |
4 | ListBox | EnableViewState=true SelectionMode=Multiple | lista de valores para seleção múltipla | |
5 | Botão | botão [submit] que transfere para [liste 2] o elemento selecionado de [liste 1] | ||
6 | Botão | botão [submit] que transfere para [liste 1] os elementos selecionados de [liste 2] |
O código de apresentação gerado é o seguinte:
<%@ Page Language="VB" %>
<script runat="server">
</script>
<html>
<head>
</head>
<body>
<form runat="server">
<p>
Tapez un texte pour l'inclure dans Liste 1 :
<asp:TextBox id="txtSaisie" runat="server" EnableViewState="False"></asp:TextBox>
</p>
<p>
<asp:Button id="btnAjouter" onclick="btnAjouter_Click" runat="server" Text="Ajouter"></asp:Button>
</p>
<p>
<table>
<tbody>
<tr>
<td>
<p align="center">
Liste 1
</p>
</td>
<td>
</td>
<td>
<p align="center">
Liste 2
</p>
</td>
</tr>
<tr>
<td>
<asp:ListBox id="ListBox1" runat="server"></asp:ListBox>
</td>
<td>
<p>
<asp:Button id="btn1vers2" onclick="btn1vers2_Click" runat="server" Text="-->"></asp:Button>
</p>
<p>
<asp:Button id="btn2vers1" onclick="btn2vers1_Click" runat="server" Text="<--"></asp:Button>
</p>
</td>
<td>
<p>
<asp:ListBox id="ListBox2" runat="server" SelectionMode="Multiple"></asp:ListBox>
</p>
</td>
</tr>
<tr>
<td>
<p align="center">
<asp:Button id="btnRaz1" onclick="btnRaz1_Click" runat="server" Text="Effacer"></asp:Button>
</p>
</td>
<td>
</td>
<td>
<p align="center">
<asp:Button id="btnRaz2" onclick="btnRaz2_Click" runat="server" Text="Effacer"></asp:Button>
</p>
</td>
</tr>
</tbody>
</table>
</p>
</form>
</body>
</html>
A classe [ListBox] é derivada da mesma classe [ListControl] que a classe [DropDownList] analisada anteriormente. Nela encontramos todas as propriedades e métodos vistos para [DropDownList], pois, na verdade, eles pertenciam à classe [ListControl]. Surge uma nova propriedade:
define o modo de seleção da lista HTML <select> que será gerada a partir do componente. Se SelectionMode=Single, então apenas um elemento poderá ser selecionado. Se SelectionMode=Multiple, vários elementos poderão ser selecionados. Para isso, o atributo [multiple="multiple"] será gerado na tag <select> da lista HTML. |
Vamos tratar os eventos. O clique no botão [Ajouter] será tratado pela seguinte rotina [btnAjouter_Click]:
Sub btnAjouter_Click(sender As Object, e As EventArgs)
' adição à lista 1
dim texte as string=txtSaisie.text.trim
if texte<> "" then ListBox1.Items.Add(New ListItem(texte))
' zerar txtSaisie
txtSaisie.text=""
End Sub
Se o texto inserido em [txtSaisie] não for uma string vazia ou em branco, um novo elemento é adicionado à lista [ListBox1]. Sabemos que precisamos adicionar um elemento do tipo [ListItem]. Anteriormente, havíamos usado o construtor [ListItem(T as String, V as String)] para realizar uma tarefa semelhante. Esse elemento gera a tag HTML [<option value="V">T</option>]. Aqui, usamos o construtor [ListItem(T as String)], que gera as tags HTML, [<option value="T">T</option>] e c.a.d. o texto [T] da opção é utilizado para formar o valor da opção. Assim que o conteúdo de [txtSaisie] for adicionado à lista [ListBox1], o campo [txTSaisie] é esvaziado.
Os cliques nos botões [Effacer] serão processados pelos seguintes procedimentos:
Sub btnRaz1_Click(sender As Object, e As EventArgs)
' limpar lista 1
ListBox1.Items.Clear
End Sub
Sub btnRaz2_Click(sender As Object, e As EventArgs)
' limpar a lista 2
ListBox2.Items.Clear
End Sub
Os cliques nos botões de transferência entre listas são processados pelos seguintes procedimentos:
Sub btn1vers2_Click(sender As Object, e As EventArgs)
' transferência do elemento selecionado da lista 1 para a lista 2
transfert(ListBox1,ListBox2)
End Sub
Sub btn2vers1_Click(sender As Object, e As EventArgs)
' transferência do elemento selecionado da lista 2 para a lista 1
transfert(ListBox2,ListBox1)
End Sub
sub transfert(l1 as listbox, l2 as listbox)
' transferência dos elementos selecionados da lista 1 para a lista 2
' algo a ser feito?
if l1.selectedindex=-1 then return
dim i as integer
' começamos pelo fim
for i=l1.items.count-1 to 0 step -1
' selecionado?
if l1.items(i).selected then
' não está mais selecionado
l1.items(i).selected=false
' transferência para l2
l2.items.add(l1.items(i))
' exclusão em l1
l1.items.removeAt(i)
end if
next
end sub
Como os dois botões realizam a mesma função — transferir itens de uma lista para outra —, é possível simplificar para um único procedimento de transferência com dois parâmetros:
- l1 do tipo [ListBox], que é a lista de origem
- l2 do tipo [ListBox], que é a lista de destino
Primeiramente, verifica-se se há pelo menos um elemento selecionado na lista l1; caso contrário, não há nada a fazer. Para isso, examina-se a propriedade [l1.selectedindex], que representa o número do primeiro elemento selecionado na lista. Se não houver nenhum, seu valor é -1. Se houver pelo menos um elemento selecionado na l1, realiza-se a transferência para a l2. Para isso, percorre-se toda a lista de elementos da l1 e verifica-se, para cada um deles, se seu atributo [selected] está definido como verdadeiro. Se for o caso, seu atributo [selected] é alterado para [false], em seguida é copiado para a lista l2 e, por fim, é removido da lista l1. Essa remoção provoca uma renumeração dos elementos da lista l1. É por isso que a lista de elementos de l1 é percorrida de trás para frente. Se for percorrida na ordem normal e o elemento nº 10 for removido, o elemento nº 11 passa a ser o nº 10 e o nº 12, o elemento nº 11. Depois de processar o elemento nº 10, nosso loop no sentido normal passará a processar o elemento nº 11, que, conforme acabamos de explicar, é o antigo nº 12. O elemento que tinha o nº 11 e agora tem o nº 10 fica de fora. Ao percorrer os elementos da lista l1 no sentido inverso, evitamos esse problema.
7.9. Os componentes CheckBox, RadioButton
As tags <asp:RadioButton> e <asp:CheckBox> permitem inserir, respectivamente, um botão de opção e uma caixa de seleção no código de apresentação de uma página. Criamos uma página [form8.aspx] para obter a seguinte apresentação:

n.º | nome | tipo | propriedades | função |
1 | RadioButton | RadioButton1.Checked=true RadioButton1.Text=1 RadioButton2.Checked=false RadioButton2.Text=2 RadioButton3.Checked=false RadioButton3.Text=3 para os 3 botões: GroupName=radio | botões de opção | |
2 | CheckBox | Checked=false para todos CheckBoxA.Text=A CheckBoxB.Text=B CheckBoxC.Text=C | caixas de seleção | |
3 | Botão | botão [submit] | ||
4 | ListBox | lista de informações |
Para que o navegador trate os três botões de opção como mutuamente exclusivos, é necessário agrupá-los em um conjunto de botões de opção. Isso é feito por meio do atributo [GroupName] da classe [RadioButton]. O estado da página não precisa ser mantido nesta aplicação. Por isso, definimos o atributo [EnableViewState="false"] na página. O código de apresentação é o seguinte:
<html>
<head>
</head>
<body>
<form id="frmControls" runat="server">
<h3>Cases à cocher
</h3>
<p>
<asp:RadioButton id="RadioButton1" runat="server" Checked="True" EnableViewState="False" GroupName="radio" Text="1"></asp:RadioButton>
<asp:RadioButton id="RadioButton2" runat="server" EnableViewState="False" GroupName="radio" Text="2"></asp:RadioButton>
<asp:RadioButton id="RadioButton3" runat="server" EnableViewState="False" GroupName="radio" Text="3"></asp:RadioButton>
</p>
<p>
<asp:CheckBox id="CheckBoxA" runat="server" EnableViewState="False" Text="A"></asp:CheckBox>
<asp:CheckBox id="CheckBoxB" runat="server" EnableViewState="False" Text="B"></asp:CheckBox>
<asp:CheckBox id="CheckBoxC" runat="server" EnableViewState="False" Text="C"></asp:CheckBox>
</p>
<p>
<asp:Button id="btnEnvoyer" onclick="btnEnvoyer_Click" runat="server" Text="Envoyer"></asp:Button>
<asp:Button id="btnTree" onclick="btnTree_Click" runat="server" Text="Contrôles"></asp:Button>
</p>
<p>
<asp:ListBox id="lstInfos" runat="server" EnableViewState="False" Rows="6" Height="131px"></asp:ListBox>
</p>
</form>
</body>
</html>
Precisamos escrever a rotina [btnEnvoyer_Click] para processar o evento [Click] nesse botão. O estado de um botão de opção ou de uma caixa de seleção é determinado pelo seu atributo [Checked], um valor booleano que assume o valor “verdadeiro” se a caixa estiver marcada e “falso” caso contrário. Portanto, basta inserir na lista [lstInfos] o valor do atributo [Checked] dos seis botões de opção e caixas de seleção. Como não há nenhuma dificuldade específica nisso, vamos inovar um pouco:
<script runat="server">
Sub btnEnvoyer_Click(sender As Object, e As EventArgs)
' colocamos informações na caixa de lista
for each c as control in FindControl("frmControls").controls
' o controle é derivado de CheckBox
if TypeOf(c) is CheckBox then
lstInfos.Items.Add(c.ID + " : " + Ctype(c,CheckBox).Checked.ToString)
end if
next
End Sub
</script>
A página pode ser vista como uma estrutura em árvore de controles. Em nosso exemplo, nossa página contém textos e controles de servidor. Os textos são considerados um controle específico chamado [LiteralControl]. Qualquer texto gera esse controle, mesmo uma sequência de espaços entre dois controles. Cada controle possui um atributo ID que o identifica. É o atributo ID que aparece nas tags:
Se ignorarmos os controles [LiteralControl], a página em análise possui os seguintes controles:
- [HtmlForm], que é o formulário [ID=frmControls]. Este, por sua vez, é um contêiner de controles. Ele contém os seguintes controles:
-- [ID=RadioButton1] do tipo [RadioButton]
-- [ID=RadioButton2] do tipo [RadioButton]
-- [ID=RadioButton3] do tipo [RadioButton]
-- [ID=CheckBoxA] do tipo [CheckBox]
-- [ID=CheckBoxA] do tipo [CheckBox]
-- [ID=CheckBoxA] do tipo [CheckBox]
-- [ID=btnEnvoyer] do tipo [Button]
Um controle possui as seguintes propriedades:
retorna a coleção de controles filhos de [Control], caso haja algum | |
retorna o controle identificado por ID, localizado na raiz da árvore de controles filhos de [Control]. No exemplo acima: Page.FindControl("frmControls") designa o contêiner [HtmlForm]. Para acessar o botão de opção [RadioButton1], será necessário escrever Page.FindControl("frmControls").FindControl("RadioButton1") | |
identificador de [Control] |
Voltemos ao código do procedimento [btnEnvoyer_Click]:
Sub btnEnvoyer_Click(sender As Object, e As EventArgs)
' informações são inseridas na caixa de lista
for each c as control in FindControl("frmControls").controls
' o controle é derivado de CheckBox?
if TypeOf(c) is CheckBox then
lstInfos.Items.Add(c.ID + " : " + Ctype(c,CheckBox).Checked.ToString)
end if
next
End Sub
Queremos exibir o estado dos botões de opção e das caixas de seleção presentes no formulário. Percorremos todos os controles do formulário. Se o controle atual for de um tipo derivado de [CheckBox], exibimos sua propriedade [Checked]. Como a classe [RadioButton] é derivada da classe [CheckBox], o teste é válido para os dois tipos de controles. A captura de tela apresentada acima mostra um exemplo de execução.
7.10. Os componentes CheckBoxList, RadioButtonList
Às vezes, é necessário permitir que o usuário escolha entre valores que não são conhecidos no momento da criação da página. Essas opções provêm de um arquivo de configuração, de um banco de dados, etc., e só são conhecidas no momento da execução. Existem soluções para esse problema, e nós já as encontramos. A lista de seleção única é adequada quando o usuário pode fazer apenas uma escolha, e a lista de seleção múltipla, quando ele pode fazer várias. Do ponto de vista estético e se o número de opções não for grande, pode-se preferir usar botões de rádio em vez da lista de seleção simples ou caixas de seleção em vez da lista de seleção múltipla. Isso é possível com os componentes [CheckBoxList] e [RadioButtonList].
As classes [CheckBoxList] e [RadioButtonList] são derivadas da mesma classe [ListControl] que as classes [DropDownList] e [ListBox] estudadas anteriormente. Portanto, encontraremos algumas das propriedades e métodos observados nessas classes, aqueles que, na verdade, pertenciam à classe [ListControl].
coleção do tipo [ListItemCollection] dos elementos da lista suspensa. Os membros dessa coleção são do tipo [ListItem]. | |
número de elementos da coleção [Items] | |
elemento nº i da lista — do tipo [ListItem] | |
para adicionar um novo elemento [ListItem] à coleção [Items] | |
para excluir todos os elementos da coleção [Items] | |
para excluir o elemento nº i da coleção [Items] | |
1º elemento [ListItem] da coleção [Items] cuja propriedade [Selected] está definida como verdadeiro | |
número do elemento anterior na coleção [Items] |
Algumas propriedades são específicas das classes [CheckBoxList] e [RadioButtonList]:
[horizontal] ou [vertical] para listas horizontais ou verticais. |
Os elementos da coleção [Items] são do tipo [ListItem]. Cada elemento [ListItem] gerará uma tag diferente, dependendo se se trata de um objeto [CheckBoxList] ou [RadioButtonList]:
Ou
Descrevemos algumas propriedades e métodos da classe [ListItem]:
construtor — cria um elemento [ListItem] com as propriedades texto e valor. Um elemento ListItem(T,V) gerará a tag HTML <input type="checkbox" value="V">T ou <input type="radio" value="V">T, conforme o caso. | |
booleano. Se for verdadeiro, a opção correspondente da lista HTML terá o atributo [selected="selected"]. Esse atributo indica ao navegador que o elemento correspondente deve aparecer selecionado na lista HTML | |
o texto T da opção HTML <input type=".." value="V" [selected="selected"]>T | |
o valor do atributo Value da opção HTML <input type=".." value="V" [selected="selected"]>T |
Propomos construir a seguinte página [form8b.aspx]:

n.º | nome | tipo | propriedades | função |
1 | RadioButtonList | EnableViewState=true RepeatDirection=horizontal | lista de botões de opção | |
2 | CheckBoxList | EnableViewState=true RepeatDirection=horizontal | lista de caixas de seleção | |
3 | Botão | botão [submit] que exibe em [4] a lista dos elementos selecionados nas duas listas | ||
4 | ListBox | EnableViewState=false | lista de valores |
O código de apresentação da página é o seguinte:
<%@ Page Language="VB" autoeventwireup="false" %>
<script runat="server">
...
</script>
<html>
<head>
</head>
<body>
<form id="frmControls" runat="server">
<h3>Listes de cases à cocher
</h3>
<p>
<asp:RadioButtonList id="RadioButtonList1" runat="server" RepeatDirection="Horizontal"></asp:RadioButtonList>
</p>
<p>
<asp:CheckBoxList id="CheckBoxList1" runat="server" RepeatDirection="Horizontal"></asp:CheckBoxList>
</p>
<p>
<asp:Button id="btnEnvoyer" onclick="btnEnvoyer_Click" runat="server" Text="Envoyer"></asp:Button>
</p>
<p>
<asp:ListBox id="lstInfos" runat="server" EnableViewState="False" Rows="6"></asp:ListBox>
</p>
</form>
</body>
</html>
O código de verificação é o seguinte:
<script runat="server">
Sub Page_Load(sender As Object, e As EventArgs) handles MyBase.Load
' preenche-se as listas se for a primeira vez que a função é chamada
if not IsPostBack then
' textos para a lista RadioButton
dim textesRadio() as String = {"1","2","3","4"}
' textos para a lista CheckBox
dim textesCheckBox() as String = {"un","deux","trois","quatre"}
' preenchimento da lista de opções de rádio
dim i as integer
for i=0 to textesRadio.length-1
RadioButtonList1.Items.Add(new ListItem(textesRadio(i)))
next
' seleção do elemento nº 1
RadioButtonList1.SelectedIndex=1
' preenchimento da lista de caixas de seleção
for i=0 to textesCheckBox.length-1
CheckBoxList1.Items.Add(new ListItem(textesCheckBox(i)))
next
end if
end sub
Sub btnEnvoyer_Click(sender As Object, e As EventArgs)
' insere informações na caixa de lista lstinfos
affiche(RadioButtonList1)
affiche(CheckBoxList1)
End Sub
sub affiche(l1 as ListControl)
' exibe os valores dos elementos selecionados da l1
' algo a ser feito?
if l1.selectedindex=-1 then return
dim i as integer
' começamos pelo fim
for i= 0 to l1.items.count-1
' selecionado?
if l1.items(i).selected then
lstInfos.Items.Add("["+TypeName(l1)+"] ["+l1.items(i).text+"] sélectionné")
end if
next
end sub
</script>
No procedimento [Page_Load], que é executado a cada vez que a página é carregada, as duas listas são inicializadas. Para evitar que isso ocorra todas as vezes, utiliza-se a propriedade [IsPostBack] para que a inicialização ocorra apenas na primeira vez. Nas vezes seguintes, as listas serão regeneradas automaticamente pelo mecanismo do [VIEWSTATE]. Assim que a página é exibida, o usuário marca algumas caixas de seleção e utiliza o botão [Envoyer]. Os valores do formulário são então enviados para o próprio formulário. Após a execução do [Page_Load], é executado o procedimento [btnEnvoyer_Click]. Este chama o procedimento [affiche] para preencher a lista [lstInfos]. Este recebe como parâmetro um objeto do tipo [ListControl], o que permite enviar a ele, indistintamente, um objeto [RadioButtonList] ou um objeto [CheckBoxList], classes derivadas de [ListControl]. A lista [lstInfos] pode ter seu atributo alterado de [EnableViewState] para [false], uma vez que seu estado não precisa ser mantido entre as diferentes consultas.
7.11. Os componentes Panel, LinkButton
A tag <asp:panel> permite inserir um contêiner de controles em uma página. A vantagem do contêiner é que algumas de suas propriedades se aplicam a todos os controles que ele contém. É o caso de sua propriedade [Visible]. Essa propriedade existe para todos os controles de servidor. Se um contêiner tiver a propriedade [Visible=false], cada um de seus controles será controlado por sua própria propriedade [Visible]. Se ele tiver a propriedade [Visible=false], então o contêiner e tudo o que ele contém não serão exibidos. Isso pode ser mais simples do que gerenciar a propriedade [Visible] de cada um dos controles do contêiner.
A tag <asp:LinkButton> permite inserir um link no código de apresentação de uma página. Ela tem uma função semelhante à do botão [Button]. Na verdade, ela aciona um POST no lado do cliente por meio de uma função JavaScript associada a ela. Criamos uma página [form9.aspx] para obter a seguinte apresentação:

n.º | nome | tipo | propriedades | função |
1 | Painel | EnableViewState=true | contêiner de controles | |
2 | ListBox | EnableViewState=true | uma lista com três valores | |
3 | LinkButton | EnableViewState=false | link para ocultar o contêiner |
Quando o contêiner está oculto, um novo link aparece:

n.º | nome | tipo | propriedades | função |
4 | LinkButton | EnableViewState=false | link para exibir o contêiner |
O código de apresentação da página é o seguinte:
<html>
<head>
</head>
<body>
<form runat="server">
<p>
<asp:Panel id="Panel1" runat="server" BorderStyle="Ridge" BorderWidth="1px">
<p>
Conteneur
</p>
<p>
<asp:ListBox id="ListBox1" runat="server">
<asp:ListItem Value="1">un</asp:ListItem>
<asp:ListItem Value="2">deux</asp:ListItem>
<asp:ListItem Value="3" Selected="True">trois</asp:ListItem>
</asp:ListBox>
</p>
</asp:Panel>
</p>
<p>
<asp:LinkButton id="lnkVoir" onclick="lnkVoir_Click" runat="server">Voir le conteneur</asp:LinkButton>
</p>
<p>
<asp:LinkButton id="lnkCacher" onclick="lnkCacher_Click" runat="server">Cacher le conteneur</asp:LinkButton>
</p>
</form>
</body>
</html>
Observe que esse código inicializa a lista [ListBox1] com três valores. Os gerenciadores de eventos [Clic] nos dois links são os seguintes:
<%@ Page Language="VB" %>
<script runat="server">
Sub Page_Load(sender As Object, e As EventArgs)
...
end sub
Sub lnkVoir_Click(sender As Object, e As EventArgs)
' exibe o contêiner 1
panel1.Visible=true
' alterar os links
lnkVoir.visible=false
lnkCacher.visible=true
End Sub
Sub lnkCacher_Click(sender As Object, e As EventArgs)
' oculta o contêiner 1
panel1.Visible=false
' altera os links
lnkVoir.visible=true
lnkCacher.visible=false
End Sub
</script>
Utilizaremos o procedimento [Page_Load] para inicializar o formulário. Faremos isso na primeira consulta (IsPostBack=false):
<%@ Page Language="VB" %>
<script runat="server">
Sub Page_Load(sender As Object, e As EventArgs)
' pela primeira vez
if not IsPostBack then
' mostra o contêiner
lnkVoir_Click(nothing,nothing)
end if
end sub
.....
</script>
7.12. Para continuar...
Os parágrafos anteriores apresentaram vários componentes de servidor. Em cada um deles, foram apresentadas apenas algumas de suas propriedades. Para aprofundar o estudo desses componentes, o leitor poderá proceder de diversas maneiras:
- descobrir as propriedades de um componente com um IDE, como o WebMatrix. Esse documento, de fato, apresenta as principais propriedades dos componentes utilizados em um formulário
- consultar a documentação de .NET para conhecer todas as classes correspondentes a cada um dos componentes de servidor. Esse é o método preferível para um domínio total do componente. Nela, você encontrará a árvore de classes que conduz aos componentes, bem como as propriedades, métodos, construtores e eventos de cada uma delas. Além disso, a documentação às vezes fornece exemplos.
Neste capítulo, utilizamos a técnica “tudo em um” ([WebMatrix]), c.a.d, na qual colocamos o código de apresentação e o código de controle de uma página no mesmo arquivo. De modo geral, não recomendamos esse método, mas sim o chamado [codebehind], utilizado anteriormente, que coloca esses dois códigos em dois arquivos separados. Vale lembrar que a vantagem dessa separação reside no fato de que o código de controle pode ser compilado sem a necessidade de executar a aplicação web. Além disso, nossos exemplos — como explicamos desde o início do capítulo — tinham um perfil bem específico: consistiam em uma única página que era um formulário trocado entre o cliente e o servidor em ciclos sucessivos de solicitação-resposta, sendo a primeira solicitação do cliente um GET e as seguintes, POST.
7.13. Componentes do servidor e controlador de aplicativo
Nos capítulos anteriores, criamos várias aplicações web. Todas elas foram construídas de acordo com a arquitetura MVC (Modelo-Visão-Controlador), que divide a aplicação em blocos bem distintos e facilita sua manutenção. Na época, construíamos nossas interfaces de usuário com tags HTML padrão. Com o que acabamos de ver, é natural querer agora utilizar componentes de servidor. Retomemos um problema já estudado em detalhes, que era o cálculo de um imposto. Sua arquitetura MVC era a seguinte:

A aplicação possui duas visualizações: [formulaire.aspx] e [erreurs.aspx]. A visualização [formulaire.aspx] é exibida quando a URL [main.aspx] é solicitada pela primeira vez:

O usuário preenche o formulário:

e clica no botão [Calculer] para obter a seguinte resposta:

Em um aplicativo MVC, toda solicitação deve passar pelo controlador, neste caso, o [main.aspx]. Isso significa que, quando o formulário [formulaire.aspx] for preenchido pelo usuário, ele deve ser enviado para [main.aspx] e não para [formulaire.aspx]. Isso simplesmente não é possível se construirmos a interface de usuário [formulaire.aspx] com componentes de servidor ASP. Para perceber isso, vamos construir um formulário [formtest.aspx] com um componente <asp:button>:
<%@ Page Language="VB" EnableViewState="false"%>
<html>
<head>
<title>test</title>
</head>
<body>
<form action="main.aspx" runat="server">
<p>
<asp:Button id="btnTest" runat="server" EnableViewState="false" Text="Test"></asp:Button>
</p>
</form>
</body>
</html>
Observe-se o atributo [action="main.aspx"] da tag <form...>. Vamos executar este aplicativo. A página de apresentação exibe apenas um botão:

Vejamos o código HTML enviado pelo servidor:
<html>
<head>
<title>test</title>
</head>
<body>
<form name="_ctl0" method="post" action="formtest.aspx" id="_ctl0">
<input type="hidden" name="__VIEWSTATE" value="dDwtNTMwNzcxMzI0Ozs+" />
<p>
<input type="submit" name="btnTest" value="Test" id="btnTest" />
</p>
</form>
</body>
</html>
Percebe-se que o POST do formulário tem como destino o próprio formulário [action="formtest.aspx"], enquanto havíamos escrito em [formtest.aspx] a tag HTML servidor:
O atributo [runat="server"] da tag <form> é imposto pelo uso dos componentes do servidor. Ocorre um erro de compilação se não inserirmos esse atributo. Quando ele é inserido, o atributo [action] da tag <form> é ignorado. O servidor sempre gera um atributo [action] que aponta para o próprio formulário. Conclui-se, portanto, que em um aplicativo MVC não é possível utilizar formulários criados com a tag <form ... runat="server">. No entanto, essa tag é indispensável para todos os componentes de servidor ASP que recuperam entradas do usuário. É o mesmo que dizer que não é possível utilizar formulários do servidor ASP em uma aplicação MVC. Essa é uma grande descoberta. De fato, um dos pontos fortes do marketing do ASP.NET é que é possível construir uma aplicação web da mesma forma que uma aplicação do Windows. Isso é verdade se nossa aplicação não seguir a arquitetura MVC, mas é ainda mais verdadeiro caso contrário. Ora, a arquitetura MVC parece ser um conceito fundamental do desenvolvimento web atual, difícil de ignorar.
É possível utilizar a arquitetura MVC em conjunto com formulários com componentes ASP para aplicativos com poucas visualizações diferentes, graças ao seguinte recurso:
- a aplicação consiste em uma única página que funciona como controlador
- as visualizações são representadas nessa página por diferentes contêineres, um contêiner por visualização. Para exibir uma visualização, torna-se visível o respectivo contêiner e ocultam-se os demais
Essa é uma solução elegante que vamos implementar agora em alguns exemplos
7.14. Exemplos de aplicativos MVC com componentes de servidor ASP
7.14.1. Exemplo 1
Neste primeiro exemplo, implementamos os componentes de servidor que apresentamos. A página [form10.aspx] terá a seguinte aparência:
![]() | ![]() |
A captura de tela à esquerda acima mostra o formulário tal como é apresentado ao cliente. Ele o preenche e o valida por meio do código [Envoyer]. O servidor retorna uma visualização com uma lista dos valores inseridos (captura de tela à direita). Um link permite que o usuário retorne ao formulário. Ele o encontra exatamente como o validou. O código de apresentação de [form10.aspx] é o seguinte:
<html>
<head>
<title>Exemple</title> <script language="javascript">
function effacer(){
alert("Vous avez cliqué sur [Effacer]")
}
</script>
</head>
<body>
<p>
Gestion d'un formulaire
</p>
<p>
<hr />
</p>
<form runat="server">
<p>
<asp:Panel id="panelinfo" runat="server" EnableViewState="False">
<p>
Liste des valeurs obtenues
</p>
<p>
<asp:ListBox id="lstInfos" runat="server" EnableViewState="False"></asp:ListBox>
</p>
<p>
<asp:LinkButton id="LinkButton1" onclick="LinkButton1_Click" runat="server">Retour au formulaire</asp:LinkButton>
</p>
<p>
<hr />
</p>
</asp:Panel>
</p>
<p>
<asp:Panel id="panelform" runat="server" >
<table>
<tbody>
<tr>
<td>
Etes-vous marié(e)</td>
<td>
<asp:RadioButton id="rdOui" runat="server" GroupName="rdmarie"></asp:RadioButton>
Oui<asp:RadioButton id="rdNon" runat="server" GroupName="rdmarie" Checked="True"></asp:RadioButton>
Non</td>
</tr>
<tr>
<td>
Cases à cocher</td>
<td>
<asp:CheckBox id="chk1" runat="server"></asp:CheckBox>
1<asp:CheckBox id="chk2" runat="server"></asp:CheckBox>
2<asp:CheckBox id="chk3" runat="server"></asp:CheckBox>
3</td>
</tr>
<tr>
<td>
Champ de saisie</td>
<td>
<asp:TextBox id="txtSaisie" runat="server" MaxLength="20" Columns="20"></asp:TextBox>
</td>
</tr>
<tr>
<td>
Mot de passe</td>
<td>
<asp:TextBox id="txtmdp" runat="server" MaxLength="10" Columns="10" TextMode="Password"></asp:TextBox>
</td>
</tr>
<tr>
<td>
Boîte de saisie</td>
<td>
<asp:TextBox id="txtArea" runat="server" Columns="20" TextMode="MultiLine" Rows="3"></asp:TextBox>
</td>
</tr>
<tr>
<td>
Liste déroulante</td>
<td>
<asp:DropDownList id="cmbValeurs" runat="server"></asp:DropDownList>
</td>
</tr>
<tr>
<td>
Liste à choix unique</td>
<td>
<asp:ListBox id="lstSimple" runat="server"></asp:ListBox>
<asp:Button id="btnRazSimple" onclick="btnRazSimple_Click" runat="server" EnableViewState="False" Text="Raz"></asp:Button>
</td>
</tr>
<tr>
<td>
Liste à choix multiple</td>
<td>
<asp:ListBox id="lstMultiple" runat="server" SelectionMode="Multiple"></asp:ListBox>
<asp:Button id="razMultiple" onclick="razMultiple_Click" runat="server" EnableViewState="False" Text="Raz"></asp:Button>
</td>
</tr>
<tr>
<td>
Champ caché</td>
<td>
<asp:Label id="lblSecret" runat="server" visible="False"></asp:Label></td>
</tr>
<tr>
<td>
Bouton simple</td>
<td>
<input id="btnEffacer" onclick="effacer()" type="button" value="Effacer" /></td>
</tr>
<tr>
<td>
Bouton [reset]</td>
<td>
<input id="btnReset" type="reset" value="Rétablir" /></td>
</tr>
<tr>
<td>
Bouton [submit]</td>
<td>
<asp:Button id="btnEnvoyer" onclick="btnEnvoyer_Click" runat="server" EnableViewState="False" Text="Envoyer"></asp:Button>
</td>
</tr>
</tbody>
</table>
</asp:Panel>
</p>
</form>
</body>
</html>
A página possui dois contêineres, um para cada visualização: [panelform] para a visualização do formulário e [panelinfo] para a visualização de informações. A lista de componentes do contêiner [panelForm] é a seguinte:
nome | tipo | propriedades | função |
Painel | EnableViewState=true | visualização do formulário | |
RadioButton | EnableViewState=true GroupName=rdmarie | botões de opção | |
CheckBox | EnableViewState=true | caixas de seleção | |
TextBox | EnableViewState=true | campo de entrada | |
TextBox | EnableViewState=true | campo de entrada protegido | |
TextBox | EnableViewState=true | campo de entrada com várias linhas | |
DropDownList | EnableViewState=true | lista suspensa | |
ListBox | EnableViewState=true SelectionMode=Single | lista de seleção única | |
Botão | EnableViewState=false | desmarca todos os elementos de lstSimple | |
ListBox | EnableViewState=true SelectionMode=Multiple | lista de seleção múltipla | |
Botão | EnableViewState=false | desmarca todos os itens de lstMultiple | |
Rótulo | EnableViewState=true Visível=false | campo oculto | |
HTML padrão | exibe um alerta | ||
Botão | EnableViewState=false | botão [submit] do formulário | |
HTML padrão | botão [reset] do formulário |
O papel do [VIEWSTATE] para os componentes é importante aqui. Todos os componentes, exceto os botões, devem ter a propriedade [EnableViewState=true]. Para entender o motivo, é preciso lembrar como a aplicação funciona. Suponhamos que o campo [txtSaisie] tenha a propriedade [EnableViewState=false]:
- o cliente solicita pela primeira vez a página [form10.aspx]. Ele obtém a visualização do formulário
- preenche-o e o envia usando o botão [Envoyer]. Os campos de entrada são então enviados, e o servidor atribui aos componentes do servidor o valor enviado ou seu estado [VIEWSTATE], caso tivessem um. Assim, ao campo [txtSaisie] é atribuído o valor inserido pelo usuário. Portanto, também nesta etapa, seu estado [VIEWSTATE] não tem utilidade. Como resultado da operação, a visualização [informations] é enviada — na verdade, ainda é a página [form10.aspx], mas com um contêiner exibido diferente.
- O usuário consulta essa nova visualização e usa o link [Retour au formulaire] para retornar a ela. Em seguida, é feita uma transição de POST para [form10.aspx]. Nesse momento, há, no máximo, um valor enviado: o valor selecionado pelo usuário na lista de informações, informação que não é utilizada posteriormente. De qualquer forma, não há nenhum campo [txtSaisie] enviado.
- O servidor recebe o POST e atribui aos componentes do servidor o valor enviado ou seu estado [VIEWSTATE], caso tivessem um. Aqui, o [txtNom] não tem nenhum valor enviado. Se seu atributo [EnableViewState] for [false], a string vazia será atribuída a ele. Como queremos que ele tenha o valor inserido pelo usuário, é necessário que ele tenha a propriedade [EnableViewState=true].
O contêiner [panelinfo] possui os seguintes controles:
nome | tipo | propriedades | função |
Painel | EnableViewState=false | visualização de informações | |
ListBox | EnableViewState=false | lista de informações que resume os valores inseridos pelo usuário | |
LinkButton | EnableViewState=false | link de retorno ao formulário |
Durante os testes, ao analisar o código HTML gerado pelo código de apresentação acima, é possível ficar surpreso com o código gerado para o campo oculto [lblSecret]:
O componente [lblSecret] não é convertido para o código HTML, pois possui a propriedade [Visible=false]. No entanto, como ele possui a propriedade [EnableViewState=true], seu valor será, mesmo assim, mantido no campo oculto [__VIEWSATE]. Assim, será possível recuperá-lo, conforme demonstrarão os testes.
Resta-nos escrever os manipuladores de eventos. Em [Page_Load], inicializaremos o formulário:
Sub page_Load(sender As Object, e As EventArgs)
' na primeira vez, os elementos são inicializados
' nas vezes seguintes, esses elementos recuperam seus valores por meio do VIEWSTATE
if IsPostBack then return
' inicialização do formulário
' o painel de informações não é exibido
panelinfo.visible=false
' painel de formulário exibido
panelform.visible=true
' botões de opção
rdNon.Checked=true
' caixas de seleção
chk2.Checked=true
' campo de entrada
txtSaisie.Text="qqs mots"
' campo de senha
txtMdp.Text="ceciestsecret"
' campo de entrada
txtArea.Text="ligne"+ControlChars.CrLf+"ligne2"+ControlChars.CrLf
' menu suspenso
dim i as integer
for i=1 to 4
cmbValeurs.Items.Add(new ListItem("choix"+i.ToString,i.ToString))
next
cmbValeurs.SelectedIndex=1
' lista de seleção única
for i=1 to 7
lstSimple.Items.Add(new ListItem("simple"+i.ToString,i.ToString))
next
lstSimple.SelectedIndex=0
' lista de seleção múltipla
for i=1 to 10
lstMultiple.Items.Add(new ListItem("multiple"+i.ToString,i.ToString))
next
lstMultiple.Items(0).Selected=true
lstMultiple.Items(2).Selected=true
' campo oculto
lblSecret.Text="secret"
End Sub
Ao clicar nos botões [lstRazSimple] e [lstMultiple]:
Sub btnRazSimple_Click(sender As Object, e As EventArgs)
' limpar lista simples
lstSimple.SelectedIndex=-1
End Sub
Sub razMultiple_Click(sender As Object, e As EventArgs)
' limpar lista de seleção múltipla
lstMultiple.SelectedIndex=-1
End Sub
Ao clicar no botão [Envoyer]:
Sub btnEnvoyer_Click(sender As Object, e As EventArgs)
' o painel de informações é exibido e o painel do formulário é ocultado
panelinfo.Visible=true
panelform.visible=false
' os valores enviados são recuperados e inseridos em lstInfos
' botões de opção
dim info as string="état marital : "+iif(rdoui.checked,"marié"," non marié")
affiche(info)
' caixas de seleção
info=" cases cochées : "+iif(chk1.checked,"1 oui","1 non")+","+ _
iif(chk2.checked,"2 oui","2 non")+","+iif(chk3.checked,"3 oui","3 non")
affiche(info)
' campo de entrada
affiche("champ de saisie : " + txtSaisie.Text.Trim)
' senha
affiche("mot de passe : " + txtMdp.Text.Trim)
' campo de preenchimento
dim lignes() as String
lignes=new Regex("\r\n").Split(txtArea.Text.Trim)
dim i as integer
for i=0 to lignes.length-1
lignes(i)="["+lignes(i).Trim+"]"
next
affiche("Boîte de saisie : " + String.Join(",",lignes))
' menu suspenso
affiche("éléments sélectionnés dans combo : "+selection(cmbValeurs))
' lista simples
affiche("éléments sélectionnés dans liste simple : "+selection(lstSimple))
' lista múltipla
affiche("éléments sélectionnés dans liste multiple : "+selection(lstMultiple))
' campo oculto
affiche ("Champ caché : " + lblSecret.Text)
End Sub
sub affiche(msg as String)
' exibe mensagem em lstInfos
lstInfos.Items.Add(msg)
end sub
function selection(liste as ListControl) as string
' percorre os itens da lista
' para encontrar os que estão selecionados
dim i as integer
dim info as string=""
for i=0 to liste.Items.Count-1
if liste.Items(i).Selected then info+="[" + liste.Items(i).Text + "]"
next
return info
end function
Por fim, ao clicar no link [Retour vers le formulaire ]:
Sub LinkButton1_Click(sender As Object, e As EventArgs)
' exibe-se o formulário e oculta-se o painel de informações
panelform.visible=true
panelinfo.visible=false
End Sub
7.14.2. Exemplo 2
Retomamos aqui uma aplicação já abordada com formulários padrão HTML. A aplicação permite realizar simulações de cálculos de impostos. Ela se baseia em uma classe [impot], que não será abordada novamente aqui. Essa classe necessita de dados que encontra em uma fonte de dados OLEDB. Para o exemplo, será uma fonte ACCESS.
7.14.2.1. A estrutura MVC do aplicativo
A estrutura MVC do aplicativo é a seguinte:

As três visualizações serão incorporadas ao código de apresentação do controlador [main.aspx] na forma de contêineres. Portanto, essa aplicação possui uma única página: [main.aspx].
7.14.2.2. As visualizações do aplicativo web
A visualização [formulaire] é o formulário de preenchimento de informações que permite o cálculo do imposto de um usuário:

O usuário preenche o formulário:

Ele utiliza o botão [Envoyer] para solicitar o cálculo do seu imposto. Ele obtém a seguinte visualização [simulations]:

Ele retorna ao formulário pelo link acima. Ele o encontra exatamente como o preencheu. Ele pode cometer erros de preenchimento:

Esses erros são sinalizados pela tela [erreurs]:

Ele retorna ao formulário pelo link acima. Ele o encontra no estado em que o preencheu. Ele pode realizar novas simulações:

Ele obtém então a visualização [simulations] com mais uma simulação:

Por fim, se a fonte de dados não estiver disponível, isso é sinalizado ao usuário na visualização [erreurs]:

7.14.2.3. O código de apresentação do aplicativo
Vale lembrar que a página [main.aspx] reúne todas as visualizações. Trata-se de um único formulário com três contêineres:
- [panelform] para a visualização [formulaire]
- [panelerreurs] para a visualização [erreurs]
- [panelsimulations] para a visualização [simulations]
Voltamos a separar o código de apresentação e o código de controle em dois arquivos distintos. O primeiro estará em [main.aspx] e o segundo em [main.aspx.vb]. O código de [main.aspx] é o seguinte:
<%@ page codebehind="main.aspx.vb" inherits="vs.main" AutoEventWireUp="false" %>
<HTML>
<HEAD>
<title>Calcul d'impôt </title>
</HEAD>
<body>
<P>Calcul de 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</TD>
</TR>
<TR>
<TD>Nombre d'enfants</TD>
<TD>
<asp:TextBox id="txtEnfants" runat="server" MaxLength="3" Columns="3"></asp:TextBox></TD>
</TR>
<TR>
<TD>Salaire annuel (euro)</TD>
<TD>
<asp:TextBox id="txtSalaire" runat="server" MaxLength="10" Columns="10"></asp:TextBox></TD>
</TR>
</TABLE>
<P>
<asp:Button id="btnCalculer" runat="server" Text="Calculer"></asp:Button>
<asp:Button id="btnEffacer" runat="server" Text="Effacer"></asp:Button></P>
</asp:panel>
<asp:panel id="panelerreurs" runat="server">
<P>Les erreurs suivantes se sont produites :</P>
<P>
<asp:Literal id="erreursHTML" runat="server"></asp:Literal></P>
<P></P>
<asp:LinkButton id="lnkForm1" runat="server">Retour au formulaire</asp:LinkButton>
</asp:panel>
<asp:panel id="panelsimulations" runat="server">
<P>
<TABLE>
<TR>
<TH>
Marié</TH>
<TH>
Enfants</TH>
<TH>
Salaire annuel</TH>
<TH>
Impôt à payer (euro)</TH></TR>
<asp:Literal id="simulationsHTML" runat="server"></asp:Literal></TABLE>
<asp:LinkButton id="lnkForm2" runat="server">Retour au formulaire</asp:LinkButton></P>
</asp:panel>
</FORM>
</body>
</HTML>
Definimos os três contêineres. Observe que todos eles estão dentro da tag <form runat="server">. Isso é obrigatório, pois, para aproveitar as vantagens dos componentes de servidor, estes devem ser colocados dentro dessa tag. O ponto importante a ser compreendido é que temos aqui um único formulário que será trocado entre o cliente e o servidor web. Estamos, portanto, na configuração utilizada em todo este capítulo sobre os componentes de servidor. Vamos detalhar os componentes de cada contêiner:
Contêiner [panelform]:
nome | tipo | propriedades | função |
Painel | EnableViewState=true | visualização do formulário | |
RadioButton | EnableViewState=true GroupName=rdmarie | botões de opção | |
TextBox | EnableViewState=true | número de filhos | |
TextBox | EnableViewState=true | salário anual | |
Botão | botão [submit] do formulário — inicia o cálculo do imposto | ||
Botão | Botão [submit] do formulário — limpa o formulário |
Contêiner [panelerreurs]:
nome | tipo | propriedades | função |
Painel | EnableViewState=true | visualização de erros | |
LinkButton | EnableViewState=true | link para o formulário | |
Literal | código HTML da lista de erros |
Contêiner [panelsimulations]:
nome | tipo | propriedades | função |
Painel | EnableViewState=true | visualização de simulações | |
LinkButton | EnableViewState=true | link para o formulário | |
Literal | código HTML da lista de simulações em uma tabela HTML |
7.14.2.4. O código de controle do aplicativo
O código de controle do aplicativo está distribuído nos arquivos [global.asax.vb] e [main.aspx.vb]. O arquivo [global.asax] está definido da seguinte forma:
O arquivo [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
Public Class Global
Inherits System.Web.HttpApplication
Sub Application_Start(ByVal sender As Object, ByVal e As EventArgs)
' cria-se um objeto de importação
Dim objImpot As impot
Try
objImpot = New impot(New impotsOLEDB(ConfigurationSettings.AppSettings("chaineConnexion")))
' coloca-se o objeto no aplicativo
Application("objImpot") = objImpot
' sem erros
Application("erreur") = False
Catch ex As Exception
'ocorreu um erro; registra-se o erro no aplicativo
Application("erreur") = True
Application("message") = ex.Message
End Try
End Sub
Sub Session_Start(ByVal sender As Object, ByVal e As EventArgs)
' início da sessão — cria-se uma lista de simulações vazia
Session.Item("simulations") = New ArrayList
End Sub
End Class
Quando o aplicativo é iniciado (primeira solicitação feita ao aplicativo), o procedimento [Application_Start] é executado. Ele procura criar um objeto do tipo [impot], obtendo seus dados de uma fonte OLEDB. Recomenda-se ao leitor que consulte o capítulo 5, onde essa classe foi definida, caso tenha esquecido. A criação do objeto [impot] pode falhar se a fonte de dados não estiver disponível. Nesse caso, o erro é armazenado na aplicação para que todas as consultas posteriores saibam que ela não pôde ser inicializada corretamente. Se a criação ocorrer sem problemas, o objeto [impot] criado também é armazenado na aplicação. Ele será utilizado por todas as consultas de cálculo de imposto. Quando um cliente faz sua primeira consulta, uma sessão é criada para ele pelo procedimento [Application_Start]. Essa sessão destina-se a armazenar as diferentes simulações de cálculo de imposto que ele realizará. Elas serão armazenadas em um objeto [ArrayList] associado à chave de sessão “simulações”. Quando a sessão é iniciada, essa chave é associada a um objeto [ArrayList] vazio. As informações necessárias para a aplicação são colocadas em seu arquivo 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\serge\devel\aspnet\poly\webforms\vs\impots5\impots.mdb" />
</appSettings>
</configuration>
A chave [chaineConnexion] identifica a cadeia de conexão com a fonte OLEDB. A outra parte do código de controle está em [main.aspx.vb]:
Imports System.Collections
Imports Microsoft.VisualBasic
Imports st.istia.univangers.fr
Imports System
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 lnkForm1 As System.Web.UI.WebControls.LinkButton
Protected WithEvents lnkForm2 As System.Web.UI.WebControls.LinkButton
Protected WithEvents panelerreurs As System.Web.UI.WebControls.Panel
Protected WithEvents panelsimulations As System.Web.UI.WebControls.Panel
Protected WithEvents simulationsHTML As System.Web.UI.WebControls.Literal
Protected WithEvents erreursHTML As System.Web.UI.WebControls.Literal
' variáveis locais
Private Sub Page_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
...
End Sub
Private Sub afficheFormulaire()
...
End Sub
Private Sub afficheSimulations(ByRef simulations As ArrayList, 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 Function checkData() As ArrayList
...
End Function
Private Sub lnkForm1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles lnkForm1.Click
....
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
End Class
Le premier événement traité par le code est [Page_Load] :
Private Sub Page_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
' primeiro, verifica-se o estado do aplicativo
If CType(Application("erreur"), Boolean) Then
' o aplicativo não conseguiu ser inicializado
' exibe-se a tela de erros
Dim erreurs As New ArrayList
erreurs.Add("Application momentanément indisponible (" + CType(Application("message"), String) + ")")
afficheErreurs(erreurs, "")
Exit Sub
End If
' sem erros — na primeira solicitação, exibimos o formulário
If Not IsPostBack Then afficheFormulaire()
End Sub
Vale lembrar que, quando o procedimento [Page_Load] é executado em um cliente POST, todos os componentes do formulário possuem um valor: ou o valor enviado pelo cliente, caso haja algum, ou o valor anterior do componente, graças ao [VIEWSTATE]. Neste formulário, todos os componentes possuem a propriedade [EnableViewState=true]. Antes de começar a processar a solicitação, verificamos se o aplicativo conseguiu ser inicializado corretamente. Caso contrário, exibimos a visualização [erreurs] com o procedimento [afficheErreurs]. Se for a primeira solicitação (IsPostBack=false), exibimos a visão [formulaire] com [afficheFormulaire].
O procedimento que exibe a visualização [erreurs] é o seguinte:
Private Sub afficheErreurs(ByRef erreurs As ArrayList, ByRef lien As String)
' exibe o contêiner de erros
panelerreurs.Visible = True
Dim i As Integer
erreursHTML.Text = ""
For i = 0 To erreurs.Count - 1
erreursHTML.Text += "<li>" + erreurs(i).ToString + "</li>" + ControlChars.CrLf
Next
lnkForm1.Text = lien
' os demais contêineres estão ocultos
panelform.Visible = False
panelsimulations.Visible = False
End Sub
O procedimento possui dois parâmetros:
- uma lista de mensagens de erro em [erreurs]
- um texto de link em [lien]
O código HTML a ser gerado para a lista de erros é inserido no literal [erreursHTML]. O texto do link, por sua vez, é colocado na propriedade [Text] do objeto [LinkButton] da visualização.
O procedimento que exibe a visualização [formulaire] é o seguinte:
Private Sub afficheFormulaire()
' exibe o formulário
panelform.Visible = True
' os demais contêineres estão ocultos
panelerreurs.Visible = False
panelsimulations.Visible = False
End Sub
Este procedimento limita-se a tornar visível o contêiner [panelform]. Os componentes são exibidos com seu valor lançado ou anterior (VIEWSTATE).
Quando o usuário clica no botão [Calculer] da visualização [formulaire], é realizada uma transição de POST para [main.aspx]. O procedimento [Page_Load] é executado e, em seguida, o procedimento [btnCalculer_Click]:
Private Sub btnCalculer_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnCalculer.Click
' verifica-se a validade dos dados inseridos
Dim erreurs As ArrayList = checkData()
' se houver erros, avisamos
If erreurs.Count <> 0 Then
' exibe-se a página de erros
afficheErreurs(erreurs, "Retour au formulaire")
Exit Sub
End If
' sem erros — calcula-se o imposto
Dim impot As Long = CType(Application("objImpot"), impot).calculer( _
rdOui.Checked, CType(txtEnfants.Text, Integer), CType(txtSalaire.Text, Long))
' adiciona-se o resultado às simulações existentes
Dim simulation() As String = New String() {CType(IIf(rdOui.Checked, "oui", "non"), String), _
txtEnfants.Text.Trim, txtSalaire.Text.Trim, impot.ToString}
' adiciona-se o resultado às simulações existentes
Dim simulations As ArrayList = CType(Session.Item("simulations"), ArrayList)
simulations.Add(simulation)
' as simulações são inseridas na sessão e no contexto
Session.Item("simulations") = simulations
' exibe-se a página de resultados
afficheSimulations(simulations, "Retour au formulaire")
End Sub
O procedimento começa verificando a validade dos campos do formulário por meio do procedimento [checkData], que retorna uma lista [ArrayList] de mensagens de erro. Se a lista não estiver vazia, a visualização [erreurs] é exibida e o procedimento é encerrado. Se os dados inseridos forem válidos, o valor do imposto é calculado por meio do objeto do tipo [impot], que havia sido armazenado no aplicativo ao seu início. Essa nova simulação é adicionada à lista de simulações já realizadas e armazenada na sessão.
A função [CheckData] verifica a validade dos dados. Ela retorna uma lista [ArrayList] de mensagens de erro, vazia se os dados forem válidos:
Private Function checkData() As ArrayList
' inicialmente, sem erros
Dim erreurs As New ArrayList
' número de filhos
Try
Dim nbEnfants As Integer = CType(txtEnfants.Text, Integer)
If nbEnfants < 0 Then Throw New Exception
Catch
erreurs.Add("Le nombre d'enfants est incorrect")
End Try
' salário
Try
Dim salaire As Long = CType(txtSalaire.Text, Long)
If salaire < 0 Then Throw New Exception
Catch
erreurs.Add("Le salaire annuel est incorrect")
End Try
' exibe a lista de erros
Return erreurs
End Function
Por fim, a visualização [simulations] é exibida pelo procedimento [afficheSimulations] a seguir:
Private Sub afficheSimulations(ByRef simulations As ArrayList, ByRef lien As String)
' exibe a visualização de simulações
panelsimulations.Visible = True
' os demais contêineres estão ocultos
panelerreurs.Visible = False
panelform.Visible = False
' conteúdo da visualização de simulações
' cada simulação é uma matriz de 4 elementos do tipo string
Dim simulation() As String
Dim i, j As Integer
simulationsHTML.Text = ""
For i = 0 To simulations.Count - 1
simulation = CType(simulations(i), String())
simulationsHTML.Text += "<tr>"
For j = 0 To simulation.Length - 1
simulationsHTML.Text += "<td>" + simulation(j) + "</td>"
Next
simulationsHTML.Text += "</tr>" + ControlChars.CrLf
Next
' link
lnkForm2.Text = lien
End Sub
O procedimento possui dois parâmetros:
- uma lista de simulações em [simulations]
- um texto de link em [lien]
O código HTML a ser gerado para a lista de simulações é inserido no literal [simulationsHTML]. O texto do link, por sua vez, é inserido na propriedade [Text] do objeto [LinkButton] da visualização.
Quando o usuário clica no botão [Effacer] da visualização [formulaire], é executado o procedimento [btnEffacer_click] (sempre após o [Page_Load]):
Private Sub btnEffacer_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnEffacer.Click
' exibe o formulário vazio
razForm()
afficheFormulaire()
End Sub
Private Sub razForm()
' esvazia o formulário
rdOui.Checked = False
rdNon.Checked = True
txtEnfants.Text = ""
txtSalaire.Text = ""
End Sub
O código acima é simples o suficiente para não precisar de comentários. Resta-nos tratar do clique nos links das visualizações [erreurs] e [simulations]:
Private Sub lnkForm1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles lnkForm1.Click
' exibe o formulário
afficheFormulaire()
End Sub
Private Sub lnkForm2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles lnkForm2.Click
' exibe o formulário
afficheFormulaire()
End Sub
Ambos os procedimentos se limitam a exibir a visualização [formulaire]. Sabemos que os campos dessa visualização receberão um valor que será ou o valor enviado para eles ou seu valor anterior. Como, neste caso, o POST do cliente não envia nenhum valor para os campos do formulário, estes recuperarão seu valor anterior. O formulário é, portanto, exibido com os valores inseridos pelo usuário. Lembramos que, na versão sem componentes de servidor, nós mesmos realizávamos essa tarefa de restauração.
7.14.2.5. Testes
Todos os arquivos necessários para a aplicação estão localizados em uma pasta <application-path>: ![]() | A pasta [bin] contém a DLL com as classes [impot], [impotsData] e [impotsOLEDB] necessárias para o aplicativo: |
O leitor poderá, se desejar, reler o capítulo 5, onde é explicada a maneira de criar o arquivo [impot.dll] mencionado acima. Feito isso, o servidor Cassini é iniciado com os parâmetros (<application-path>,/impots5). Acesse a URL [http://impots5/main.aspx] com um navegador:

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

7.14.3. Exemplo 3
Mostramos nesses dois exemplos que é possível construir aplicativos web que respeitem a arquitetura MVC com componentes de servidor. O último exemplo mostra que a solução com componentes de servidor é mais simples do que a solução que utiliza tags HTML padrão. Nossos dois exemplos tinham apenas uma página com várias visualizações dentro da mesma página. É possível ter uma arquitetura MVC com vários formulários ASP de servidor, desde que o fato de esses formulários enviarem os valores para si mesmos não represente um problema. Esse é frequentemente o caso de aplicativos com menu. Vejamos o seguinte exemplo:

Reunimos em uma mesma página links para as aplicações que escrevemos até agora. Esse tipo de aplicação se adapta bem a uma arquitetura MVC. A diferença é que não há mais um, mas vários controladores.

O controlador [main.aspx] atua como controlador principal. É ele que é chamado pelos links da página inicial do aplicativo. Ele poderá realizar operações comuns a todas as ações possíveis e, em seguida, executará a ação específica associada ao link utilizado. Em seguida, ele passará o controle para um dos controladores secundários, aquele encarregado de executar a ação. A partir desse momento, as trocas de dados ocorrem entre o cliente e esse controlador específico. Não se passa mais pelo controlador principal [main.aspx]. Portanto, não estamos mais no contexto MVC com um único controlador que filtra todas as solicitações. Cada um dos controladores acima pode apresentar várias visualizações por meio do mecanismo de contêineres dentro de uma única página, conforme apresentamos.
O fato de não termos mais um único controlador que seleciona as visualizações a serem enviadas ao cliente apresenta algumas desvantagens. Tomemos como exemplo o gerenciamento de erros. Cada uma das ações expostas pela aplicação pode precisar exibir uma visualização de erros. Cada controlador [applix.aspx] terá sua própria visualização [erreurs], pois esta é simplesmente um contêiner específico da página do controlador. Não há como ter uma visualização [erreurs] única que fosse utilizada por todas as aplicações individuais. De fato, tal visualização geralmente apresenta um link de retorno para o formulário com erros, e este deve ser restaurado ao estado em que foi validado para permitir que o usuário corrija seus erros. Essa restauração é feita pelo mecanismo do [VIEWSTATE], que não funciona entre controladores diferentes. Se as aplicações forem desenvolvidas por pessoas diferentes, corre-se o risco de ter páginas de erro com aparência diferente dependendo da ação escolhida pelo usuário, prejudicando a homogeneidade da aplicação como um todo. Veremos um pouco mais adiante que o ASP.NET oferece uma solução para esse problema específico de visualização compartilhada. Essa solução pode ser implementada por meio de um novo componente de servidor que nós mesmos construiremos. Basta utilizar esse componente nas diferentes aplicações para garantir a homogeneidade da aplicação global. Mais difícil de gerenciar é o problema da ordem das ações. Quando todas as solicitações passam por um único controlador, este pode verificar se a ação solicitada é compatível com a anterior. Esse código de controle está em um único local. Aqui, será necessário distribuí-lo pelos diferentes controladores, o que complica a manutenção da aplicação global.
Voltemos à nossa aplicação acima. A página inicial dela é uma página clássica HTML:
<html>
<head>
<TITLE>Composants ASP Serveur</TITLE>
<meta name="pragma" content="no-cache">
</head>
<frameset rows="130,*" frameborder="0">
<frame name="banner" src="bandeau.htm" scrolling="no">
<frameset cols="200,*">
<frame name="contents" src="options.htm">
<frame name="main" src="main.htm">
</frameset>
<noframes>
<p id="p1">
Ce jeu de frames HTML affiche plusieurs pages Web. Pour afficher ce jeu de
frames, utilisez un navigateur Web qui prend en charge HTML 4.0 et version
ultérieure.
</p>
</noframes>
</frameset>
</html>
Esta página inicial é composta por três frames chamados banner, contents e main:
![]() |
A página [bandeau.htm], inserida no quadro [banner], é a seguinte:

Seu código HTML é o seguinte:
<html>
<head>
<META HTTP-EQUIV="PRAGMA" CONTENT="NO-CACHE" />
<title>bandeau</title>
</head>
<body>
<P>
<TABLE>
<TR>
<TD><IMG alt="logo université d'angers" src="univ01.gif"></TD>
<TD>Composants serveurs ASP</TD>
</TR>
</TABLE>
</P>
<HR>
</body>
</html>
A página [options.htm] está inserida no banner [contents]. Trata-se de um conjunto de links:
![]() | |
Todos os links apontam para o controlador principal [main.aspx], com um parâmetro [action] indicando a ação a ser realizada. Solicita-se que o destino dos links seja exibido no quadro [main] (target="main").
A primeira página exibida no quadro [main] é [main.htm]:
|
O controlador principal [main.aspx, main.aspx.vb] é o seguinte:
[main.aspx]
[main.aspx.vb]
Classe pública main
Herdado de System.Web.UI.Page
Sub Privada Page_Load(ByVal remetente As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
' recuperamos a ação a ser realizada
Dim ação As String
If Request.QueryString("action") Is Nothing Then
action = "label"
Else
action = Request.QueryString("action").ToString.ToLower
End If
' executa-se a ação
Select Case ação
Case "label"
Server.Transfer("form2.aspx")
Case "button"
Server.Transfer("form3.aspx")
Case "textbox1"
Server.Transfer("form4.aspx")
Case "textbox2"
Server.Transfer("form5.aspx")
Case "lista suspensa"
Server.Transfer("form6.aspx")
Campo “listbox”
Server.Transfer("form7.aspx")
Caixa de seleção "casesacocher"
Server.Transfer("form8.aspx")
Caixa de seleção "listecasesacocher"
Server.Transfer("form8b.aspx")
Case "painel"
Server.Transfer("form9.aspx")
Caso Else
Server.Transfer("form2.aspx")
End Select
End Sub
End Class
Nosso controlador é simples. Dependendo do valor do parâmetro [action], ele redireciona o processamento da solicitação para a página adequada. Ele não agrega nenhum valor a mais em relação a uma página HTML com links. No entanto, bastaria adicionar uma página de autenticação para perceber sua utilidade. Se o usuário precisasse se autenticar (login, senha) para ter acesso aos aplicativos, o controlador [main.aspx] seria um bom local para verificar se essa autenticação foi realizada.




