Entendendo um pouco mais do Processo de geração de Página em Asp.Net

Fernando Cerqueira

Quando desenvolvemos em asp.net as páginas são criadas por uma interface gráfica arrastando objetos para um formulário, que depois de compilado e publicado no site recebemos em um formato html padrão após sua requisição. Até aqui não existe nenhuma novidade, somente um pergunta que muitas vezes nos passa despercebido: Como é feito este controle para interagir com nosso código?

Entender melhor como isso é feito é objetivo deste artigo, e tirando proveito deste conhecimento poderemos, por exemplo:

- Dispara uma rotina em nosso código sem estar associada a nenhum controle

- Criar controles mistos, ex. um cheboxlist junto com radiobutton html

- Usar uma página com frames controlando outro frame e disparando eventos em outra página.

Para chegarmos a estes exemplos iremos nos valer de alguns códigos em javascript que aliais é a base de como isso é controlado pelo asp.net.

Início da páginaInício da página

O Processo de geração de página para o cliente

Os Webcontrols que colocamos em nossa página não são “tag” reconhecidas pelos browsers, sendo necessário executar uma conversão para ser enviada e processada de forma compatível ao cliente (browser). Nesta conversão é necessário atribuir-se nomes únicos para cada controle e criar rotinas que possam ser disparadas pelo lado do cliente, correspondendo às ações feita pelo usuário, enviando ao servidor para que saiba quem disparou este evento.

No Diagrama acima um browser solicita uma pagina a um servidor e enviando junto com esta solicitação, suas características (Como versão, tipo, capacidade e sistema operacional). O servidor recebe estas informações, localiza a página e gera uma pseudo-página sobre o layout já definido.

O resultado deste processo é submetido a outro processo, que transforma esta pseudo-página no formato compatível pelo browser correspondente. Finalmente o resultado desta transformação é enviado ao cliente.

Início da páginaInício da página

Sabendo um pouco mais do que é gerado

Para sabermos um pouco mais do que é gerada basta apenas um pouco de curiosidade, e em nosso browser pedirmos para ver o código da página recebida. vamos ao um exemplo retirado de uma página:

...
...
<form name="Form1" method="post" action="WebForm1.aspx" id="Form1">
<input type="hidden" name="__EVENTTARGET" value="" />
<input type="hidden" name="__EVENTARGUMENT" value="" />
<input type="hidden" name="__VIEWSTATE" value="dDwtMTg2Nzc3NjAzNTs7…" /> 

<script language="javascript">
<!--
            function __doPostBack(eventTarget, eventArgument) {
                        var theform;
                        if (window.navigator.appName.toLowerCase().indexOf("netscape") > -1) {
                                    theform = document.forms["Form1"];
                        }
                        else {
                                    theform = document.Form1;
                        }
                        theform.__EVENTTARGET.value = eventTarget.split("$").join(":");
                        theform.__EVENTARGUMENT.value = eventArgument;
                        theform.submit();
            }
// -->
</script>
<a id="LinkButton1" href="javascript:__doPostBack('LinkButton1','')" style="Z-INDEX: 103; LEFT: 32px; POSITION: absolute; TOP: 232px">Enviar>

O código acima foi gerado de uma página com apenas 1 webcontrol (LinkButton).

Podemos perceber que alem da transformação do LinkButton em uma link ... foi inserido um script com uma função de nome __doPostBack e 2 campos invisíveis : __EVENTTARGET , __EVENTARGUMENT.

O webcontrol LinkButton1 também foi modificado e inserido dentro do elemento “href” uma chamada a função __doPostBack, passando como parâmetro o nome do controle e outro parâmetro vazio.

Com isso começamos a ter uma idéia melhor como esta sendo executado as páginas gerando nossa primeira dica:

Dica : Todo a submissão ao servidor é feita pela função __doPostBack , que por sua vez salva em __EVENTTARGET o nome do controle que foi utilizado para disparar o envio e em __EVENTARGUMENT um parâmetro opcional que pode ser passado por um controle.

Quando inserimos algum webcontrol do tipo validator mais alguns códigos e funções Javascript são colocadas em nosso código. Embora não seja o foco deste artigo vejamos como fica o código gerado, após inserir um Textbox em um RequiredFieldValidator em um formulário:

Página antes da geração:

<HTML>
<HEAD>
<title>ARTIGO ASP.NET ENTENDO O PROCESSO GERAÇÃO DE PAGINA</title>
<meta name="GENERATOR" content="Microsoft Visual Studio .NET 7.1">
<meta name="CODE_LANGUAGE" content="Visual Basic .NET 7.1">
<meta name="vs_defaultClientScript" content="JavaScript">
<meta name="vs_targetSchema" content="http://schemas.microsoft.com/intellisense/ie5">
</HEAD>
<body MS_POSITIONING="GridLayout">
<form id="Form1" method="post" runat="server">
<asp:LinkButton id="LinkButton1" style="Z-INDEX: 100; LEFT: 40px; POSITION: absolute; TOP: 56px"
runat="server">Enviar</asp:LinkButton>
<asp:TextBox id="TextBox1" style="Z-INDEX: 101; LEFT: 40px; POSITION: absolute; TOP: 24px" runat="server"></asp:TextBox>
<asp:RequiredFieldValidator id="RequiredFieldValidator1" style="Z-INDEX: 102; LEFT: 200px; POSITION: absolute; TOP: 32px"
runat="server" ErrorMessage="RequiredFieldValidator" ControlToValidate="TextBox1"></asp:RequiredFieldValidator>
</form>
</body>
</HTML>

Página após o processamento

< script language="javascript" src="/aspnet_client/system_web/1_1_4322/WebUIValidation.js"></script>
< a id="LinkButton1" href="javascript:{if (typeof(Page_ClientValidate) != 'function' || Page_ClientValidate()) __doPostBack('LinkButton1','')} " style="Z-INDEX: 100; LEFT: 40px; POSITION: absolute; TOP: 56px"> Enviar </a>
< input name="TextBox1" type="text" id="TextBox1" style="Z-INDEX: 101; LEFT: 40px; POSITION: absolute; TOP: 24px" />
< span id="RequiredFieldValidator1" controltovalidate="TextBox1" errormessage="RequiredFieldValidator" evaluationfunction="RequiredFieldValidatorEvaluateIsValid" initialvalue="" style="color:Red;Z-INDEX:102;LEFT:200px;POSITION:absolute;TOP:32px;visibility:hidden;">RequiredFieldValidator </span>
< script language="javascript">
<!--
var Page_Validators = new Array(document.all["RequiredFieldValidator1"]);
// -->
</script>
< script language="javascript">
<!--
var Page_ValidationActive = false;
if (typeof(clientInformation) != "undefined" && clientInformation.appName.indexOf("Explorer") != -1) {
if (typeof(Page_ValidationVer) == "undefined")
alert("Unable to find script library '/aspnet_client/system_web/1_1_4322/WebUIValidation.js'. Try placing this file manually, or reinstall by running 'aspnet_regiis -c'.");
else if (Page_ValidationVer != "125")-->
alert("This page uses an incorrect version of WebUIValidation.js. The page expects version 125. The script library is " + Page_ValidationVer + ".");
else
ValidatorOnLoad();
}
function ValidatorOnSubmit() {
if (Page_ValidationActive) {
ValidatorCommonOnSubmit();
}
}
// -->
</script>
</form>

Olhando o código gerado, novamente vemos que possui a função __doPostBack e os 2 campos invisíveis : __EVENTTARGET , __EVENTARGUMENT, só que agora, ao final, temos mais algumas funções em javacript que são as responsáveis pela validação dos controles no lado ”cliente”.

Os detalhes a serem observados são:

<form name="Form1" method="post" action="WebForm2.aspx" language="javascript" onsubmit="ValidatorOnSubmit();" id="Form1"> (Que garante que antes de ser submetido o formulário seja executa a função ValidatorOnSubmit() )

<script language="javascript" src="/aspnet_client/system_web/1_1_4322/WebUIValidation.js"></script> (Que insere no formulário um conjunto de funções responsáveis pela validação dos diversos controles do tipo validator, este arquivo é criado automaticamente no IIS quando instalamos o Framework)

<a id="LinkButton1" href="javascript:{if (typeof(Page_ClientValidate) != 'function' || Page_ClientValidate()) __doPostBack('LinkButton1','')} " style="Z-INDEX: 100; LEFT: 40px; POSITION: absolute; TOP: 56px">Enviar </a> (Que foi alterada para chamar antes da função __doPostBack as funções de validação do formulário)

Dica: A função __doPostBack não é gerada caso não possua nenhum controle de submissão declarado na pagina (ex: LinkButtom, CommandButtom etc.). Uma forma simples de contornar isso é colocar um linkbutton com atributo Texto vazio no formulário. Como atributo vazio ele fica “invisível” no formulário porem é criado a função de submissão __doPostBack.

Início da páginaInício da página

Usando o __doPostBack , __EVENTTARGET e _EVENTARGUMENT para dispara rotinas dentro do Código

Conhecendo agora a função __doPostBack e os campos invisíveis __EVENTTARGET e __EVENTARGUMENT podemos disparar eventos em código, usando apenas as chamadas a esta função e campos. Vejamos o exemplo abaixo:

<HEAD>
<title>ARTIGO ASP.NET ENTENDO O PROCESSO GERAÇÃO DE PAGINA</title>
<meta content="Microsoft Visual Studio .NET 7.1" name="GENERATOR">
<meta content="Visual Basic .NET 7.1" name="CODE_LANGUAGE">
<meta content="JavaScript" name="vs_defaultClientScript">
<meta content="http://schemas.microsoft.com/intellisense/ie5" name="vs_targetSchema">
</HEAD>
<body MS_POSITIONING="GridLayout">
<form id="Form1" method="post" runat="server">
<asp:linkbutton id="LinkButton1" style="Z-INDEX: 100; LEFT: 112px; POSITION: absolute; TOP: 152px"
runat="server" Visible="true"></asp:linkbutton>
<DIV style="DISPLAY: inline; Z-INDEX: 106; LEFT: 40px; WIDTH: 70px; POSITION: absolute; TOP: 80px; HEIGHT: 15px"
ms_positioning="FlowLayout">
<P>Parametro</P>
</DIV>
<DIV style="DISPLAY: inline; Z-INDEX: 105; LEFT: 40px; WIDTH: 70px; POSITION: absolute; TOP: 24px; HEIGHT: 15px"
ms_positioning="FlowLayout">Controle</DIV>
<asp:textbox id="Textbox2" style="Z-INDEX: 104; LEFT: 40px; POSITION: absolute; TOP: 48px" runat="server"></asp:textbox><asp:textbox id="TextBox1" style="Z-INDEX: 101; LEFT: 40px; POSITION: absolute; TOP: 104px" runat="server"></asp:textbox>
<INPUT style="Z-INDEX: 103; LEFT: 40px; POSITION: absolute; TOP: 144px" onclick="javascript:__doPostBack('MinhaFuncao','MeuParametro')"
type="button" value="Button"></form>
</body>
</HTML>

Private Sub Page_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
'Put user code to initialize the page here
If Not Page.Request("__EVENTTARGET") Is Nothing AndAlso _
Page.Request("__EVENTTARGET").ToLower = "minhafuncao" Then
MinhaFuncao()
End If
End Sub
Private Sub MinhaFuncao()
Textbox2.Text = Page.Request("__EVENTTARGET")
TextBox1.Text = Page.Request("__EVENTARGUMENT")
End Sub

Abaixo o resultado antes e depois de pressionado o botão.

A execução do código acima demonstra um botão do tipo input type="button" disparando uma rotina especifica declarada em nosso código(MinhaFunção) com uma passagem de parâmetro sem estar associado a nenhum evento ou webcontrol.

Início da páginaInício da página

Usando a forma de transformação para criar um efeito de um controle misto

Como já vimos, existe a transformação de nossas webcontrol em tags Html que possam ser reconhecidas pelo nosso browser. Se valendo deste conhecimento podemos inserir de uma forma não tradicional controles dentro de controles como veremos a seguir:

Primeiro vamos saber como é o resultado de um controle do tipo chekboxlist:

<form name="Form1" method="post" action="WebForm3.aspx" id="Form1">
<input type="hidden" name="__VIEWSTATE" value="dDwtMTI3NDg5MzI2MDs7bDxDaGVja0JveExpc3QxOjA7Q2hlY2tCb3h
MaXN0MToxO0NoZWNrQm94TGlzdDE6MTs+PrfXKaiCHJ60c6rllV5e1kFWo8Y3" />
<table id="CheckBoxList1" border="0" style="width:88px;Z-INDEX: 101; LEFT: 32px;
POSITION: absolute; TOP: 24px">
<tr>
<td><input id="CheckBoxList1_0" type="checkbox" name="CheckBoxList1:0" />
<label for="CheckBoxList1_0">Opção1</label></td>
</tr><tr>
<td><input id="CheckBoxList1_1" type="checkbox" name="CheckBoxList1:1" />
<label for="CheckBoxList1_1">Opção 2</label></td>
</tr>
</table>
</form>

Pelo resultado vemos que as opções estão sobre tags o que sugere a próxima dica:

DICA : Se em vez de um texto colocamos toda uma instrução HTML, ela será mostrada de acordo com a tag inserida (desde que o resultado do controle seja uma tag que permita esta operação).

Agora veja o resultado que se pode obter baseado nesta dica:

<HTML>
<HEAD>
<title>WebForm3</title>
<meta name="GENERATOR" content="Microsoft Visual Studio .NET 7.1">
<meta name="CODE_LANGUAGE" content="Visual Basic .NET 7.1">
<meta name="vs_defaultClientScript" content="JavaScript">
<meta name="vs_targetSchema" content="http://schemas.microsoft.com/intellisense/ie5">
</HEAD>
<body MS_POSITIONING="GridLayout">
<form id="Form1" method="post" runat="server">
<asp:CheckBoxList id="CheckBoxList1" style="Z-INDEX: 101; LEFT: 32px; POSITION: absolute; TOP: 24px" runat="server" Width="128px">
<asp:ListItem Value="1">
<INPUT type='radio' id='opt1' name='opt1'>Opçâo 1
</asp:ListItem>
<asp:ListItem Value="2">
<a href='http://www.gurj.net'>Opção2</a>
</asp:ListItem>
</asp:CheckBoxList>
</form>
</body>
</HTML>

O objetivo foi colocar dentro de um listitem um outro controle (HTML) que é apresentado junto com o texto do item, ou ainda modificando a forma de apresentar (como foi o caso do link <a>...</a> da opção 2).

Juntado tudo e com persistência e auto-postback dos controles HTML inseridos.

Vejam a tela abaixa e o código que esta sendo usado:

<HTML>
<HEAD>
<title>WebForm3</title>
<meta name="GENERATOR" content="Microsoft Visual Studio .NET 7.1">
<meta name="CODE_LANGUAGE" content="Visual Basic .NET 7.1">
<meta name="vs_defaultClientScript" content="JavaScript">
<meta name="vs_targetSchema" content="http://schemas.microsoft.com/intellisense/ie5">
</HEAD>
<body MS_POSITIONING="GridLayout">
<form id="Form1" method="post" runat="server">
<asp:CheckBoxList id="CheckBoxList1" style="Z-INDEX: 101; LEFT: 32px; POSITION: absolute; TOP: 24px" runat="server" Width="128px">
<asp:ListItem Value="1">
<INPUT type='radio' id='opt1' name='opt1'>Opçâo 1
</asp:ListItem>
<asp:ListItem Value="2">
<a href='http://www.gurj.net'>Opção2</a>
</asp:ListItem>
</asp:CheckBoxList>
</form>
</body>
</HTML>

Private Sub Page_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
'Put user code to initialize the page here
Hidden1.Value = ""
If Not Page.Request("__EVENTTARGET") Is Nothing AndAlso Page.Request("__EVENTTARGET").ToLower = "opt" Then
Opts(Page.Request("__EVENTARGUMENT"))
End If
End Sub
Private Sub Opts(ByVal Tipo As String)
TextBox1.Text = Tipo.Substring(0, 1)
Hidden1.Value = Tipo.Substring(1, 1)
End Sub

Explicando o código

Inserimos dentro dos Listitens do checkboxlist, tags Html de um controle radiobutton. Para poderemos persistir a seleção dos radiosButton foi criado outro controle “Hidden1” para guardar a seleção feita e ser manipulado pelo nosso código (alteramos para rodar na forma runat="server").

Dentro do evento Page_Load identificamos o disparo dos radiobuttons (pelo campo __EVENTTARGET) e mostramos em texbox1 o valor selecionado, salvando a posição do radiobuttom selecionado em “Hidden1”. Quando a página é retornada o script, ao final da página, se encarrega de “setar” corretamente o radiobuttom escolhido, persistindo assim a informação.

Frame controlando outro frame e disparando eventos no código

Trabalhar com frames em página possibilita algumas facilidades e poupa o custo de estamos indo ao servidor para redesenhar toda a tela, porem tem o inconveniente de complicar o controle ou disparo de eventos em outra paginas e/ou frames. Conhecendo agora a função __doPostBack fica bem mais simples o trabalho, veja o script abaixo:

<form name="Form1" method="post" action="WebForm3.aspx" id="Form1">
<input type="hidden" name="__VIEWSTATE" value="dDwtMTI3NDg5MzI2MDs7bDxDaGVja0JveExpc3QxOjA7Q2hlY2tCb3hMaXN0MTox
O0NoZWNrQm94TGlzdDE6MTs+PrfXKaiCHJ60c6rllV5e1kFWo8Y3" />
<table id="CheckBoxList1" border="0" style="width:88px;Z-INDEX: 101; LEFT: 32px;
POSITION: absolute; TOP: 24px">
<tr>
<td><input id="CheckBoxList1_0" type="checkbox" name="CheckBoxList1:0" />
<label for="CheckBoxList1_0">Opção1</label></td>
</tr><tr>
<td><input id="CheckBoxList1_1" type="checkbox" name="CheckBoxList1:1" />
<label for="CheckBoxList1_1">Opção 2</label></td>
</tr>
</table>
</form>

A primeira função AtualizaFrame é usada para controlar qual página vai ser apresentada e sobre qual frame.

A segunda função PostFrame dispara um evento(PostBack) na pagina que vai ser diaparda o evento.

Desta forma controlamos a apresentação de páginas e eventos em outros frames!.

Obs.: Este script deve ser colocado na página que ira controlar as ações.

Conclusão

Apresentamos neste artigo algumas maneiras de se tirar proveito da forma que é feito o processo de controle e envido de informações. Os códigos apresentados têm apenas o intuito de demonstrar a funcionalidade do que foi proposto deixando a cargo do desenvolvedor explorar este recurso adaptando os conceitos aos mais diversos cenários. Até o próximo artigo.

Sobre o Autor: Fernando Cerqueira (fernandocerqueira@msn.com)é Microsoft MVP em ASP.NET , possui formação acadêmica em administração de empresas, CEO da FCI Sistemas, especializada em Call Center e BI/CRM. Atuou em diversas empresas de grande porte, sempre utilizando a plataforma Microsoft. Desde 2002 vem trabalhando com o Visual Studio .NET prestando consultoria e treinamento focando os resultados em ASP.NET. Em 2003 criou o GURJ.NET - Grupo de usuários .NET do Rio de Janeiro (www.gurj.net) sendo reconhecido pelo INETA.O grupo já realizou diversos eventos de porte como os Roads Shows e DevDay no Rio de Janeiro. Blog: (http://br.thespoke.net/MyBlog/FCerqueira/MyBlog.aspx)


Início da páginaInício da página