Partilhar via


Noções básicas sobre os gatilhos UpdatePanel do AJAX ASP.NET

por Scott Cate

Ao trabalhar no editor de marcação no Visual Studio, você pode observar (do IntelliSense) que há dois elementos filho de um controle UpdatePanel. Um deles é o elemento Triggers, que especifica os controles na página (ou o controle do usuário, se você estiver usando um) que disparará uma renderização parcial do controle UpdatePanel no qual o elemento reside.

Introdução

A tecnologia ASP.NET da Microsoft traz um modelo de programação orientado a objetos e controlado por eventos e o une com os benefícios do código compilado. No entanto, seu modelo de processamento do lado do servidor tem várias desvantagens inerentes à tecnologia, muitas das quais podem ser abordadas pelos novos recursos incluídos nas Extensões AJAX do Microsoft ASP.NET 3.5. Essas extensões permitem muitos novos recursos avançados do cliente, incluindo a renderização parcial de páginas sem a necessidade de uma atualização de página inteira, a capacidade de acessar os Serviços Web por meio do script do cliente (incluindo a API de criação de perfil de ASP.NET) e uma extensa API do lado do cliente projetada para espelho muitos dos esquemas de controle vistos no conjunto de controle do lado do servidor ASP.NET.

Este white paper examina a funcionalidade gatilhos XML do componente ASP.NET AJAX UpdatePanel . Os gatilhos XML dão controle granular sobre os componentes que podem causar renderização parcial para controles UpdatePanel específicos.

Esse white paper é baseado na versão Beta 2 do .NET Framework 3.5 e no Visual Studio 2008. As extensões ASP.NET AJAX, anteriormente um assembly de complemento direcionado ao ASP.NET 2.0, agora são integradas à Biblioteca de Classes Base do .NET Framework. Esse white paper também pressupõe que você trabalhará com o Visual Studio 2008, não com o Visual Web Developer Express, e fornecerá instruções passo a passo de acordo com a interface do usuário do Visual Studio (embora as listagens de código sejam totalmente compatíveis, independentemente do ambiente de desenvolvimento).

Gatilhos

Os gatilhos para um determinado UpdatePanel, por padrão, incluem automaticamente todos os controles filho que invocam um postback, incluindo (por exemplo) controles TextBox que têm sua AutoPostBack propriedade definida como true. No entanto, os gatilhos também podem ser incluídos declarativamente usando marcação; isso é feito na <triggers> seção da declaração de controle UpdatePanel. Embora os gatilhos possam ser acessados por meio da Triggers propriedade de coleção, é recomendável registrar quaisquer gatilhos de renderização parciais em tempo de execução (por exemplo, se um controle não estiver disponível no momento do design) usando o RegisterAsyncPostBackControl(Control) método do objeto ScriptManager para sua página, dentro do Page_Load evento. Lembre-se de que As páginas são sem estado e, portanto, você deve registrar novamente esses controles sempre que eles forem criados.

A inclusão automática de gatilho filho também pode ser desabilitada (para que os controles filho que criam postbacks não disparem automaticamente renderizações parciais) definindo a ChildrenAsTriggers propriedade como false. Isso permite a maior flexibilidade na atribuição de quais controles específicos podem invocar uma renderização de página e é recomendado, para que um desenvolvedor opte por responder a um evento, em vez de manipular quaisquer eventos que possam surgir.

Observe que quando os controles UpdatePanel estão aninhados, quando o UpdateMode é definido como Condicional, se o UpdatePanel filho for disparado, mas o pai não for, somente o UpdatePanel filho será atualizado. No entanto, se o UpdatePanel pai for atualizado, o UpdatePanel filho também será atualizado.

O <elemento Triggers>

Ao trabalhar no editor de marcação no Visual Studio, você pode observar (do IntelliSense) que há dois elementos filho de um UpdatePanel controle. O elemento mais visto é o <ContentTemplate> elemento , que essencialmente encapsula o conteúdo que será mantido pelo painel de atualização (o conteúdo para o qual estamos habilitando a renderização parcial). O outro elemento é o <Triggers> elemento , que especifica os controles na página (ou o controle do usuário, se você estiver usando um) que disparará uma renderização parcial do controle UpdatePanel no qual o <elemento Triggers> reside.

O <Triggers> elemento pode conter qualquer número de cada um dos dois nós filho: <asp:AsyncPostBackTrigger> e <asp:PostBackTrigger>. Ambos aceitam dois atributos, ControlID e EventName, e podem especificar qualquer Controle dentro da unidade atual de encapsulamento (por exemplo, se o controle UpdatePanel residir em um Controle de Usuário da Web, você não deve tentar fazer referência a um Controle na Página na qual o Controle de Usuário residirá).

O <asp:AsyncPostBackTrigger> elemento é particularmente útil, pois pode direcionar qualquer evento de um Controle que exista como um filho de qualquer controle UpdatePanel na unidade de encapsulamento, não apenas o UpdatePanel sob o qual esse gatilho é filho. Portanto, qualquer controle pode ser feito para disparar uma atualização parcial da página.

Da mesma forma, o <asp:PostBackTrigger> elemento pode ser usado para disparar uma renderização de página parcial, mas que requer uma viagem de ida e volta completa para o servidor. Esse elemento de gatilho também pode ser usado para forçar uma renderização de página inteira quando um controle normalmente dispararia uma renderização de página parcial (por exemplo, quando existe um Button controle no <ContentTemplate> elemento de um controle UpdatePanel). Novamente, o elemento PostBackTrigger pode especificar qualquer controle que seja filho de qualquer controle UpdatePanel na unidade atual de encapsulamento.

<Referência do elemento Triggers>

Descendentes de marcação:

Tag Descrição
<asp:AsyncPostBackTrigger> Especifica um controle e um evento que causarão uma atualização parcial da página para o UpdatePanel que contém essa referência de gatilho.
<asp:PostBackTrigger> Especifica um controle e um evento que causarão uma atualização de página inteira (uma atualização de página inteira). Essa marca pode ser usada para forçar uma atualização completa quando um controle dispararia a renderização parcial.

Passo a passo: gatilhos cross-updatePanel

  1. Crie uma nova página ASP.NET com um objeto ScriptManager definido para habilitar a renderização parcial. Adicione dois UpdatePanels a esta página : no primeiro, inclua um controle Label ( Label1 ) e dois controles Button ( Button1 e Button2 ). Button1 deve dizer Clique para atualizar ambos e Button2 deve dizer Clique para atualizar isso ou algo nesse sentido. No segundo UpdatePanel, inclua apenas um controle Label ( Label2 ), mas defina sua propriedade ForeColor como algo diferente do padrão para diferenciá-lo.
  2. Defina a propriedade UpdateMode de ambas as marcas UpdatePanel como Condicional.

Listagem 1: Marcação para default.aspx:



<%@ Page Language="C#" AutoEventWireup="true" CodeFile="Default.aspx.cs" Inherits="_Default" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
   "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" >
   <head runat="server">
      <title>Untitled Page</title>
   </head>
   <body>
      <form id="form1" runat="server">
         <asp:ScriptManager EnablePartialRendering="true"
            ID="ScriptManager1" runat="server"></asp:ScriptManager>
         <div>
            <asp:UpdatePanel ID="UpdatePanel1" runat="server"
               UpdateMode="Conditional">
               <ContentTemplate>
                  <asp:Label ID="Label1" runat="server" />
                  <br />
                  <asp:Button ID="Button1" runat="server"
                     Text="Update Both Panels" OnClick="Button1_Click" />
                  <asp:Button ID="Button2" runat="server"
                     Text="Update This Panel" OnClick="Button2_Click" />
               </ContentTemplate>
            </asp:UpdatePanel>
            <asp:UpdatePanel ID="UpdatePanel2" runat="server"
               UpdateMode="Conditional">
               <ContentTemplate>
                  <asp:Label ID="Label2" runat="server" ForeColor="red" />
               </ContentTemplate>
               <Triggers>
                  <asp:AsyncPostBackTrigger ControlID="Button1" EventName="Click" />
               </Triggers>
            </asp:UpdatePanel>
         </div>
      </form>
   </body>
</html>

  1. No manipulador de eventos Click para Button1, defina Label1.Text e Label2.Text como algo dependente de tempo (como DateTime.Now.ToLongTimeString()). Para o manipulador de eventos Clique para Button2, defina apenas Label1.Text como o valor dependente de tempo.

Listagem 2: Codebehind (cortado) em default.aspx.cs:

public partial class _Default : System.Web.UI.Page
{
    protected void Button1_Click(object sender, EventArgs e)
    {
        Label1.Text = DateTime.Now.ToLongTimeString();
        Label2.Text = DateTime.Now.ToLongTimeString();
    }
    protected void Button2_Click(object sender, EventArgs e)
    {
        Label1.Text = DateTime.Now.ToLongTimeString();
    }
}
  1. Pressione F5 para criar e executar o projeto. Observe que, quando você clica em Atualizar Ambos os Painéis, ambos os rótulos alteram o texto; no entanto, quando você clica em Atualizar Este Painel, somente o Label1 é atualizado.

Captura de tela que mostra o primeiro botão que indica Atualizar Ambos os Painéis e o segundo botão que indica Atualizar Este Painel.

(Clique para exibir a imagem em tamanho real)

Sob o Capô

Utilizando o exemplo que acabamos de construir, podemos dar uma olhada no que ASP.NET a AJAX está fazendo e como nossos gatilhos entre painéis do UpdatePanel funcionam. Para fazer isso, trabalharemos com o HTML de origem de página gerado, bem como a extensão do Mozilla Firefox chamada FireBug– com ela, podemos examinar facilmente os postbacks do AJAX. Também usaremos a ferramenta Refletor do .NET por Lutz Roeder. Ambas as ferramentas estão disponíveis gratuitamente online e podem ser encontradas com uma pesquisa na Internet.

Um exame do código-fonte da página não mostra quase nada fora do comum; os controles UpdatePanel são renderizados como <div> contêineres e podemos ver que o recurso de script inclui fornecido pelo <asp:ScriptManager>. Também há algumas novas chamadas específicas do AJAX para o PageRequestManager que são internas para a biblioteca de scripts do cliente AJAX. Por fim, vemos os dois contêineres UpdatePanel : um com os botões renderizados <input> com os dois <asp:Label> controles renderizados como <span> contêineres. (Se você inspecionar a árvore DOM em FireBug, observará que os rótulos estão esmaecidos para indicar que eles não estão produzindo conteúdo visível).

Clique no botão Atualizar Este Painel e observe que o UpdatePanel superior será atualizado com a hora atual do servidor. Em FireBug, escolha a guia Console para que você possa examinar a solicitação. Examine os parâmetros de solicitação POST primeiro:

Captura de tela que mostra uma caixa de diálogo Firebug com Console selecionado.

(Clique para exibir a imagem em tamanho real)

Observe que o UpdatePanel indicou ao código AJAX do lado do servidor precisamente qual árvore de controle foi disparada por meio do parâmetro ScriptManager1: Button1 do UpdatePanel1 controle . Agora, clique no botão Atualizar ambos os painéis. Em seguida, examinando a resposta, vemos uma série delimitada por pipe de variáveis definida em uma cadeia de caracteres; especificamente, vemos que o UpdatePanel superior, UpdatePanel1, tem a totalidade de seu HTML enviado para o navegador. A biblioteca de scripts do cliente AJAX substitui o conteúdo HTML original do UpdatePanel pelo novo conteúdo por meio da .innerHTML propriedade e, portanto, o servidor envia o conteúdo alterado do servidor como HTML.

Agora, clique no botão Atualizar ambos os painéis e examine os resultados do servidor. Os resultados são muito semelhantes – ambos os UpdatePanels recebem um novo HTML do servidor. Assim como no retorno de chamada anterior, o estado da página adicional é enviado.

Como podemos ver, como nenhum código especial é utilizado para executar um postback AJAX, a biblioteca de scripts do cliente AJAX é capaz de interceptar postbacks de formulário sem nenhum código adicional. Os controles de servidor utilizam automaticamente o JavaScript para que eles não enviem automaticamente o formulário – ASP.NET injeta automaticamente o código para validação de formulário e estado já, obtidos principalmente pela inclusão automática de recursos de script, pela classe PostBackOptions e pela classe ClientScriptManager.

Por exemplo, considere um controle CheckBox; examine a desmontagem de classe no Refletor do .NET. Para fazer isso, verifique se o assembly System.Web está aberto e navegue até a System.Web.UI.WebControls.CheckBox classe , abrindo o RenderInputTag método . Procure um condicional que verifique a AutoPostBack propriedade :

Captura de tela que mostra o código que começa com em Clique igual a.

(Clique para exibir a imagem em tamanho real)

Quando o postback automático é habilitado em um CheckBox controle (por meio da propriedade AutoPostBack sendo true), a marca resultante <input> é renderizada com um script de tratamento de eventos ASP.NET em seu onclick atributo. A interceptação do envio do formulário, então, permite que ASP.NET AJAX seja injetado na página de forma não intrusiva, ajudando a evitar possíveis alterações interruptivas que possam ocorrer utilizando uma substituição de cadeia de caracteres possivelmente imprecisa. Além disso, isso permite que qualquer controle de ASP.NET personalizado utilize o poder de ASP.NET AJAX sem nenhum código adicional para dar suporte ao seu uso em um contêiner UpdatePanel.

A <triggers> funcionalidade corresponde aos valores inicializados na chamada PageRequestManager para _updateControls (observe que a biblioteca de scripts de cliente ASP.NET AJAX utiliza a convenção de que métodos, eventos e nomes de campo que começam com um sublinhado são marcados como internos e não são destinados a uso fora da própria biblioteca). Com ele, podemos observar quais controles se destinam a causar postbacks do AJAX.

Por exemplo, vamos adicionar dois controles adicionais à página, deixando um controle fora do UpdatePanels inteiramente e deixando um dentro de um UpdatePanel. Adicionaremos um controle CheckBox no UpdatePanel superior e descartaremos um DropDownList com várias cores definidas na lista. Aqui está a nova marcação:

Listagem 3: Nova Marcação

<%@ Page Language="C#" AutoEventWireup="true"
 CodeFile="Default.aspx.cs" Inherits="_Default" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
 "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" >
 <head id="Head1" runat="server">
 <title>Untitled Page</title>
 </head>
 <body>
 <form id="form1" runat="server">
 <asp:ScriptManager EnablePartialRendering="true"
 ID="ScriptManager1" runat="server"></asp:ScriptManager>
 <div>
 <asp:UpdatePanel ID="UpdatePanel1" runat="server"
 UpdateMode="Conditional">
 <ContentTemplate>
 <asp:Label ID="Label1" runat="server" /><br />
 <asp:Button ID="Button1" runat="server"
 Text="Update Both Panels" OnClick="Button1_Click" />
 <asp:Button ID="Button2" runat="server"
 Text="Update This Panel" OnClick="Button2_Click" />
 <asp:CheckBox ID="cbDate" runat="server"
 Text="Include Date" AutoPostBack="false"
 OnCheckedChanged="cbDate_CheckedChanged" />
 </ContentTemplate>
 </asp:UpdatePanel>
 <asp:UpdatePanel ID="UpdatePanel2" runat="server"
 UpdateMode="Conditional">
 <ContentTemplate>
 <asp:Label ID="Label2" runat="server"
 ForeColor="red" />
 </ContentTemplate>
 <Triggers>
 <asp:AsyncPostBackTrigger ControlID="Button1" 
 EventName="Click" />
 <asp:AsyncPostBackTrigger ControlID="ddlColor" 
 EventName="SelectedIndexChanged" />
 </Triggers>
 </asp:UpdatePanel>
 <asp:DropDownList ID="ddlColor" runat="server"
 AutoPostBack="true"
 OnSelectedIndexChanged="ddlColor_SelectedIndexChanged">
 <asp:ListItem Selected="true" Value="Red" />
 <asp:ListItem Value="Blue" />
 <asp:ListItem Value="Green" />
 </asp:DropDownList>
 </div>
 </form>
 </body>
</html>

E aqui está o novo code-behind:

Listagem 4: Codebehind

public partial class _Default : System.Web.UI.Page
{
    protected void Button1_Click(object sender, EventArgs e)
    {
        if (cbDate.Checked)
        {
            Label1.Text = DateTime.Now.ToString("MM/dd/yyyy hh:mm:ss");
            Label2.Text = DateTime.Now.ToString("MM/dd/yyyy hh:mm:ss");
        }
        else
        {
            Label1.Text = DateTime.Now.ToLongTimeString();
            Label2.Text = DateTime.Now.ToLongTimeString();
        }
    }
    protected void Button2_Click(object sender, EventArgs e)
    {
        if (cbDate.Checked)
        {
            Label1.Text = DateTime.Now.ToString("MM/dd/yyyy hh:mm:ss");
        }
        else
        {
            Label1.Text = DateTime.Now.ToLongTimeString();
        }
    }
    protected void cbDate_CheckedChanged(object sender, EventArgs e)
    {
        cbDate.Font.Bold = cbDate.Checked;
    }
    protected void ddlColor_SelectedIndexChanged(object sender, EventArgs e)
    {
        Color c = Color.FromName(ddlColor.SelectedValue);
        Label2.ForeColor = c;
    }
}

A ideia por trás dessa página é que a lista suspensa selecione uma das três cores para mostrar o segundo rótulo, que a caixa marcar determina se ela está em negrito e se os rótulos exibem a data e a hora. A caixa marcar não deve causar uma atualização do AJAX, mas a lista suspensa deve, mesmo que não esteja alojada em um UpdatePanel.

Captura de tela que mostra um navegador da Web chamado Página sem título e uma lista suspensa com a cor Azul selecionada abaixo do botão que diz Atualizar ambos os painéis.

(Clique para exibir a imagem em tamanho real)

Como é evidente na captura de tela acima, o botão mais recente a ser clicado foi o botão direito Atualizar Este Painel, que atualizou a hora superior independentemente da hora inferior. A data também foi desativada entre cliques, pois a data é visível no rótulo inferior. Por fim, o interesse é a cor do rótulo inferior: ele foi atualizado mais recentemente do que o texto do rótulo, o que demonstra que o estado de controle é importante e os usuários esperam que ele seja preservado por meio de postbacks AJAX. No entanto, o tempo não foi atualizado. O tempo foi preenchido automaticamente por meio da persistência do campo __VIEWSTATE da página que está sendo interpretada pelo runtime do ASP.NET quando o controle estava sendo renderizado novamente no servidor. O código do servidor ASP.NET AJAX não reconhece em quais métodos os controles estão alterando o estado; ele simplesmente preenche novamente do estado de exibição e, em seguida, executa os eventos apropriados.

Deve-se ressaltar, no entanto, que se eu tivesse inicializado o tempo dentro do evento Page_Load, o tempo teria sido incrementado corretamente. Consequentemente, os desenvolvedores devem ter cuidado com a execução do código apropriado durante os manipuladores de eventos apropriados e evitar o uso de Page_Load quando um manipulador de eventos de controle for apropriado.

Resumo

O controle UpdatePanel ASP.NET extensões AJAX é versátil e pode utilizar vários métodos para identificar eventos de controle que devem fazer com que ele seja atualizado. Ele dá suporte à atualização automática por seus controles filho, mas também pode responder a eventos de controle em outro lugar da página.

Para reduzir o potencial de carregamento de processamento do servidor, é recomendável que a ChildrenAsTriggers propriedade de um UpdatePanel seja definida falsecomo e que os eventos sejam aceitos em vez de incluídos por padrão. Isso também impede que eventos desnecessários causem efeitos potencialmente indesejados, incluindo validação e alterações nos campos de entrada. Esses tipos de bugs podem ser difíceis de isolar, pois a página é atualizada de forma transparente para o usuário e, portanto, a causa pode não ser imediatamente óbvia.

Examinando o funcionamento interno do modelo pós-interceptação de formulário AJAX ASP.NET, conseguimos determinar que ele utiliza a estrutura já fornecida pelo ASP.NET. Ao fazer isso, ele preserva a compatibilidade máxima com controles projetados usando a mesma estrutura e se intromete minimamente em qualquer JavaScript adicional gravado para a página.

Biografia

Rob Paveza é um desenvolvedor sênior de aplicativos .NET da Terralever (www.terralever.com), uma empresa líder em marketing interativo em Tempe, AZ. Ele pode ser contatado em robpaveza@gmail.come seu blog está localizado em http://geekswithblogs.net/robp/.

Scott Cate trabalha com tecnologias da Microsoft Web desde 1997 e é presidente de myKB.com (www.myKB.com), onde é especialista em escrever aplicativos baseados em ASP.NET com foco em soluções de Software da Base de Dados de Conhecimento. Scott pode ser contatado por e-mail em scott.cate@myKB.com ou seu blog em ScottCate.com