Descripción de los desencadenadores UpdatePanel de ASP.NET AJAX

por Scott Cate

Al trabajar en el editor de marcado en Visual Studio, quizás observe (de IntelliSense) que hay dos elementos secundarios de un control UpdatePanel. Uno de los cuales es el elemento Triggers, que especifica los controles de la página (o el control de usuario, si usa uno) que desencadenarán una representación parcial del control UpdatePanel donde se encuentra el elemento.

Introducción

La tecnología ASP.NET de Microsoft aporta un modelo de programación orientado a objetos y controlado por eventos y lo une a las ventajas del código compilado. Sin embargo, su modelo de procesamiento del lado servidor tiene varias desventajas inherentes a la tecnología, muchas de las cuales pueden abordarse mediante las nuevas características incluidas en las extensiones AJAX para Microsoft ASP.NET 3.5. Estas extensiones permiten muchas nuevas características de cliente enriquecidas, incluida la representación parcial de páginas sin necesidad de una actualización de página completa, la capacidad de acceder a los servicios web a través del script de cliente (incluida la API de generación de perfiles de ASP.NET) y una amplia API del lado cliente diseñada para reflejar muchos de los esquemas de control vistos en el conjunto de controles del lado servidor de ASP.NET.

En estas notas del producto se examina la funcionalidad de desencadenadores XML del componente UpdatePanel de ASP.NET AJAX. Los desencadenadores XML proporcionan control pormenorizado sobre los componentes que pueden provocar una representación parcial para controles UpdatePanel específicos.

Estas notas del producto se basan en la versión Beta 2 de .NET Framework 3.5 y Visual Studio 2008. Las extensiones AJAX para ASP.NET, anteriormente un ensamblado de complementos para ASP.NET 2.0, ahora se integran en la biblioteca de clases base de .NET Framework. En estas notas del producto también se da por supuesto que trabajará con Visual Studio 2008, no con Visual Web Developer Express, y se proporcionarán tutoriales según la interfaz de usuario de Visual Studio (aunque las listas de código serán totalmente compatibles independientemente del entorno de desarrollo).

Desencadenadores

De forma predeterminada, los desencadenadores de un control UpdatePanel determinado incluyen automáticamente los controles secundarios que invocan un postback, incluidos, por ejemplo, los controles TextBox que tienen la propiedad AutoPostBack establecida en true. Sin embargo, los desencadenadores también se pueden incluir mediante declaración con el marcado; esto se realiza en la sección <triggers> de la declaración del control UpdatePanel. Aunque se puede acceder a los desencadenadores a través de la propiedad de colección Triggers, se recomienda registrar cualquier desencadenador de representación parcial en tiempo de ejecución (por ejemplo, si un control no está disponible en tiempo de diseño) mediante el método RegisterAsyncPostBackControl(Control) del objeto ScriptManager de la página, en el evento Page_Load. Recuerde que Pages no tiene estado y, por tanto, debe volver a registrar estos controles cada vez que se creen.

También se puede establecer la propiedad ChildrenAsTriggers en false para deshabilitar la inclusión automática de desencadenadores secundarios (de modo que los controles secundarios que creen postbacks no desencadenen automáticamente representaciones parciales). Esta opción ofrece una mayor flexibilidad para asignar qué controles específicos pueden invocar la representación de una página, por lo que es recomendable para que el desarrollador pueda optar por responder a un evento en lugar de tener que controlar cualquier evento que pueda surgir.

Tenga en cuenta que, cuando los controles UpdatePanel están anidados y el elemento UpdateMode está establecido en Conditional, si se desencadena un elemento UpdatePanel secundario, pero no el primario, solo se actualizará el elemento UpdatePanel secundario. Sin embargo, si se actualiza el elemento UpdatePanel primario, también se actualizará el elemento UpdatePanel secundario.

El elemento <Triggers>

Al trabajar en el editor de marcado en Visual Studio, quizás observe (de IntelliSense) que hay dos elementos secundarios de un control UpdatePanel. El elemento más frecuente es el elemento <ContentTemplate>, que básicamente encapsula el contenido que el panel de actualización mantendrá (el contenido para el que se va a habilitar la representación parcial). El otro es elemento es el elemento <Triggers>, que especifica los controles de la página (o el control de usuario, si usa uno) que desencadenarán una representación parcial del control UpdatePanel donde se encuentra el elemento <Triggers>.

El elemento <Triggers> puede contener cualquier número de cada uno de los dos nodos secundarios: <asp:AsyncPostBackTrigger> y <asp:PostBackTrigger>. Ambos aceptan dos atributos, ControlID y EventName, y pueden especificar cualquier control en la unidad actual de encapsulación (por ejemplo, si el control UpdatePanel se encuentra en un control de usuario web, no se debería intentar hacer referencia a un control de la página en la que estará el control de usuario).

El elemento <asp:AsyncPostBackTrigger> es particularmente útil porque puede apuntar a cualquier evento de un control que exista como elemento secundario de cualquier control UpdatePanel en la unidad de encapsulación, no solo del elemento UpdatePanel del que este desencadenador sea un elemento secundario. Por lo tanto, se puede hacer que cualquier control desencadene una actualización parcial de la página.

Del mismo modo, se puede usar el elemento <asp:PostBackTrigger> para desencadenar una representación parcial de página, pero una que requiere un recorrido de ida y vuelta completo al servidor. También se puede usar este elemento desencadenador para forzar una representación de página completa en los casos en que un control normalmente activaría una representación parcial de la página. (por ejemplo, cuando existe un control Button en el elemento <ContentTemplate> de un control UpdatePanel). De nuevo, el elemento PostBackTrigger puede especificar cualquier control secundario de cualquier control UpdatePanel de la unidad actual de encapsulación.

Referencia del elemento <Triggers>

Descendientes de marcado:

Tag Descripción
<asp:AsyncPostBackTrigger> Especifica un control y un evento que provocarán una actualización parcial de página para el elemento UpdatePanel que contiene esta referencia de desencadenador.
<asp:PostBackTrigger> Especifica un control y un evento que provocarán una actualización de página completa. Esta etiqueta se puede usar para forzar una actualización completa en los casos en que un control desencadenaría una representación parcial.

Tutorial: Desencadenadores entre controles UpdatePanel

  1. Cree una nueva página ASP.NET con un objeto ScriptManager establecido para habilitar la representación parcial. Agregue dos controles UpdatePanel en esta página: en el primero, incluya un control Label ( Label1 ) y dos controles Button ( Button1 y Button2 ). Button1 debe decir Hacer clic para actualizar y Button2 debe decir Hacer clic para actualizar esto, o algo similar. En el segundo control UpdatePanel, incluya solo un control Label ( Label2 ), pero establezca su propiedad ForeColor un valor distinto del predeterminado para diferenciarlo.
  2. Establezca la propiedad UpdateMode de ambas etiquetas UpdatePanel en Conditional.

Lista 1: Marcado 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. En el controlador de eventos Click de Button1, establezca Label1.Text y Label2.Text en algo dependiente del tiempo (como DateTime.Now.ToLongTimeString()). En el controlador de eventos Click de Button2, establezca solo Label1.Text en el valor dependiente del tiempo.

Lista 2: Código subyacente (recortado) en 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. Presione F5 para compilar y ejecutar el proyecto. Tenga en cuenta que, al hacer clic en Actualizar los dos paneles, ambas etiquetas cambian de texto; sin embargo, al hacer clic en Actualizar este panel, solo se actualiza Label1.

Screenshot that shows the first button that states Update Both Panels and the second button that states Update This Panel.

(Haga clic para ver la imagen a tamaño completo).

Aspectos técnicos

Utilizando el ejemplo que acabamos de construir, podemos echar un vistazo a lo que hace ASP.NET AJAX y cómo funcionan los desencadenadores entre paneles de UpdatePanel. Para ello, trabajaremos con el HTML de origen de la página generada, así como con la extensión de Mozilla Firefox llamada FireBug; con ella, podemos examinar fácilmente los postbacks de AJAX. También usaremos la herramienta .NET Reflector de Lutz Roeder. Ambas herramientas están disponibles gratuitamente en línea y se pueden encontrar con una búsqueda en Internet.

Un examen del código fuente de la página no muestra prácticamente nada fuera de lo común: los controles UpdatePanel se representan como contenedores <div> y podemos ver que en el recurso de script se incluye proporcionado por <asp:ScriptManager>. También hay algunas nuevas llamadas específicas de AJAX a PageRequestManager que son internas de la biblioteca de scripts de cliente de AJAX. Por último, vemos los dos contenedores UpdatePanel: uno con los botones <input> representados con los dos controles <asp:Label> representados como contenedores <span>. (Si inspecciona el árbol DOM en FireBug, observará que las etiquetas están atenuadas para indicar que no generan contenido visible).

Haga clic en el botón Actualizar este panel y observe que el elemento UpdatePanel superior se actualizará con la hora actual del servidor. En FireBug, elija la pestaña Consola para examinar la solicitud. Examine primero los parámetros de solicitud POST:

Screenshot that shows a Firebug dialog with Console selected.

(Haga clic para ver la imagen a tamaño completo).

Tenga en cuenta que UpdatePanel ha indicado al código AJAX del lado servidor exactamente qué árbol de control se desencadenó a través del parámetro ScriptManager1: Button1 del control UpdatePanel1. Ahora, haga clic en el botón Actualizar los dos paneles. A continuación, al examinar la respuesta, vemos una serie de variables delimitadas por canalizaciones establecidas en una cadena; en concreto, vemos que el elemento UpdatePanel superior, UpdatePanel1, ha enviado la totalidad de su código HTML al explorador. La biblioteca de scripts de cliente de AJAX sustituye el contenido HTML original de UpdatePanel por el nuevo contenido mediante la propiedad .innerHTML y, por tanto, el servidor envía el contenido cambiado del servidor como HTML.

Ahora, haga clic en el botón Actualizar los dos paneles y examine los resultados del servidor. Los resultados son muy similares: los dos elementos UpdatePanel reciben nuevo código HTML del servidor. Al igual que con la devolución de llamada anterior, se envía el estado de página adicional.

Como podemos ver, dado que no se usa ningún código especial para realizar un postback de AJAX, la biblioteca de scripts de cliente de AJAX puede interceptar postbacks de formulario sin ningún código adicional. Los controles del servidor usan automáticamente JavaScript para no enviar automáticamente el formulario. ASP.NET inserta código de manera automática para la validación y el estado del formulario, lo que se consigue principalmente mediante la inclusión automática de recursos de script, la clase PostBackOptions y la clase ClientScriptManager.

Por ejemplo, considere un control CheckBox; examine el desensamblado de clase en .NET Reflector. Para ello, asegúrese de que el ensamblado System.Web está abierto, vaya a la clase System.Web.UI.WebControls.CheckBox y abra el método RenderInputTag. Busque un condicional que compruebe la propiedad AutoPostBack:

Screenshot that shows code that begins with on Click equals.

(Haga clic para ver la imagen a tamaño completo).

Cuando el postback automático está habilitado en un control CheckBox (tras establecer la propiedad AutoPostBack en true), la etiqueta resultante <input> se representa con un script de control de eventos ASP.NET en el atributo onclick. La interceptación del envío del formulario, por tanto, permite insertar ASP.NET AJAX en la página de forma no intrusiva, evitando así cualquier cambio importante que pudiera producirse al usar un reemplazo de cadena probablemente impreciso. Además, esto permite que cualquier control de ASP.NET personalizado aproveche la eficacia de ASP.NET AJAX sin necesidad de agregar más código para admitir su uso en un contenedor UpdatePanel.

La funcionalidad <triggers> corresponde a los valores inicializados en la llamada de PageRequestManager a _updateControls (tenga en cuenta que la biblioteca de scripts de cliente de ASP.NET AJAX usa una convención según la cual los métodos, los eventos y los nombres de campo que comienzan con un guión bajo se marcan como internos, por lo que no está previsto su uso fuera de la propia biblioteca). Con ella, podemos observar qué controles están diseñados para provocar postbacks de AJAX.

Por ejemplo, vamos a agregar dos controles adicionales a la página, dejando uno fuera de los elementos UpdatePanel por completo, pero dejando el otro en un elemento UpdatePanel. Agregaremos un control CheckBox en el elemento UpdatePanel superior, y colocaremos un control DropDownList con una serie de colores definidos en la lista. Este es el nuevo marcado:

Lista 3: Nuevo marcado

<%@ 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>

Y este es el nuevo código subyacente:

Lista 4: Código subyacente

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;
    }
}

En esta página, la idea es que la lista desplegable seleccione uno de los tres colores para mostrar la segunda etiqueta y que la casilla determine si está en negrita y si las etiquetas muestran tanto la fecha como la hora. La casilla no debe provocar una actualización de AJAX, pero la lista desplegable sí debería hacerlo aunque no se encuentre en un elemento UpdatePanel.

Screenshot that shows a web browser called Untitled Page and a drop down list with the color Blue selected below the button that says Update Both Panels.

(Haga clic para ver la imagen a tamaño completo).

Como se aprecia en la captura de pantalla anterior, el último botón en el que se hizo clic fue el botón derecho Actualizar este panel, que actualizó la hora de la parte superior con independencia de la hora de la parte inferior. La fecha también se desactivó entre clics, ya que la fecha es visible en la etiqueta inferior. Por último, cabe destacar el color de la etiqueta inferior: se actualizó más recientemente que el texto de la etiqueta, lo que demuestra que el estado del control es importante y que los usuarios esperan que se conserve a través de los postbacks de AJAX. Sin embargo, la hora no se actualizó. La hora se volvió a rellenar automáticamente por medio de la persistencia del campo __VIEWSTATE de la página que el entorno de ejecución ASP.NET interpreta cuando se vuelve a representar el control en el servidor. El código de servidor de ASP.NET AJAX no reconoce en qué métodos cambian de estado los controles; simplemente se vuelve a rellenar a partir del estado de visualización y, luego, ejecuta los eventos que sean adecuados.

Sin embargo, debería señalarse que, si inicializase la hora en el evento Page_Load, la hora se habría incrementado correctamente. Por lo tanto, los desarrolladores deben tener cuidado de que se ejecute el código adecuado durante los controladores de eventos adecuados, además de evitar el uso de Page_Load cuando lo adecuado sería un controlador de eventos de control.

Resumen

El control UpdatePanel de las extensiones AJAX para ASP.NET es versátil y puede usar una serie de métodos para identificar los eventos de control que deben hacer que se actualice. Admite la actualización automática por sus controles secundarios, pero también puede responder a eventos de control en otras partes de la página.

Para reducir la potencial carga de procesamiento del servidor, se recomienda establecer la propiedad ChildrenAsTriggers de un elemento UpdatePanel en false, así como y optar por incluir los eventos en lugar de incluirlos de forma predeterminada. De este modo también se evita que los eventos innecesarios causen efectos potencialmente no deseados, como la validación y los cambios en los campos de entrada. Estos tipos de errores pueden ser difíciles de aislar, ya que la página se actualiza de forma transparente para el usuario y, por tanto, la causa puede no ser inmediatamente obvia.

Al examinar el funcionamiento interno del modelo posterior a la interceptación del formulario de ASP.NET AJAX, pudimos determinar que emplea el marco ya proporcionado por ASP.NET. Al hacerlo, conserva la máxima compatibilidad con los controles diseñados con el mismo marco e interfiere mínimamente en cualquier código JavaScript adicional escrito para la página.

Biografía

Rob Paveza es un desarrollador sénior de aplicaciones .NET de Terralever (www.terralever.com), una empresa de marketing interactivo líder de Tempe, Arizona. Puede ponerse en contacto con él a través de robpaveza@gmail.com y encontrará su blog en http://geekswithblogs.net/robp/.

Scott Cate ha trabajado con las tecnologías web de Microsoft desde 1997 y es el presidente de myKB.com (www.myKB.com) donde es especialista en escribir aplicaciones ASP.NET basadas en soluciones de software de Knowledge Base. Puede ponerse en contacto con Scott por correo electrónico a través de scott.cate@myKB.com o en su blog, en ScottCate.com