Icono del sitio Programando a medianoche

Deshabilitar controles de la página hasta que finalicen los UpdatePanels

Hace unos meses comenzamos a desarrollar para un cliente una aplicación web más o menos compleja, con .NET 3.5 y bastante uso de AJAX. En esta aplicación hay páginas que utilizan varios UpdatePanels, y algunos de estos realizan procesos complejos que pueden llegar a demorar varios segundos, es por esto que nuestro cliente nos pidió que, al iniciar estos procesos, se bloquee la página para que el usuario no pueda ejecutar ninguna otra acción en la misma hasta tanto no finalice el proceso pendiente. Obviamente no queríamos ejecutar un script «a mano» cada vez que se ejecutaba una acción y otro al finalizar la misma, así que buscamos la manera de automatizarlo un poco. Comenzamos buscando los eventos propios del motor de AJAX de Microsoft® y encontramos que podemos capturar los del RequestManager cuando inicia una petición al servidor y cuando la misma finaliza. Esto lo podemos hacer utilizando los métodos add_initializeRequest y add_endRequest de la instancia actual del PageRequestManager, la cual podemos obtener con el siguiente código: Sys.WebForms.PageRequestManager.getInstance().

En el primer evento mostrábamos un ModalPopUp (control que muestra una «ventana» en la página y deshabilita todos los demás controles de la misma) de la librería ASP.NET AJAX Control Toolkit de Microsoft®, el cual contenía un mensaje que decía «Aguarde un momento…», y en el segundo evento lo cerrábamos. A continuación se muestra como quedaba el PopUp en la página:

<cc1:modalpopupextender ID="mpeLoading" runat="server" BehaviorID="idmpeLoading" PopupControlID="pnlLoading" TargetControlID="btnLoading" EnableViewState="false" DropShadow="true" BackgroundCssClass="ModalBackground"></cc1:modalpopupextender><br />
<asp:panel ID="pnlLoading" runat="server" Width="300" Height="50" HorizontalAlign="Center" CssClass="ModalPopup" EnableViewState="false" Style="display: none"><br />
    <br />Aguarde un momento...</asp:panel>
<asp:button ID="btnLoading" runat="server" Style="display: none"></asp:button>

y acá está el código JavaScript:

function initializeRequest(sender, args){
    $find('idmpeLoading').show();
}
function endRequest(sender, args){
    $find('idmpeLoading').hide();
}
Sys.WebForms.PageRequestManager.getInstance().add_initializeRequest(initializeRequest);
Sys.WebForms.PageRequestManager.getInstance().add_endRequest(endRequest);

Cabe aclarar que la variable idmpeLoading del código anterior contiene el identificador del ModalPopUp a mostrar.
Hasta este punto todo funcionaba bien, el problema surgió cuando utilizamos paneles modales con UpdatePanels dentro, en ese momento la ventana con el mensaje «Aguarde un momento…» se «dibujaba» debajo del panel que estaba utilizando el usuario, no cumpliendo con su finalidad, ya que de esta manera no se deshabilitaban todos los controles de la página.
Paso siguiente revisamos el código del ModalPopUp (el mismo se puede bajar desde el mismo link que mostré antes) y encontramos que a los paneles se les establece un valor fijo en el estilo zIndex para mostrarlos sobre los demás controles. Luego revisamos las propiedades del objeto de JavaScript que representa el panel y encontramos que posee dos objetos interesantes: _backgroundElement y _foregroundElement. Estos representan los DIVs del fondo y del contenido de PopUp respectivamente, entonces para hacer que estos objetos de HTML se posicionen sobre todos los demás simplemente tenemos que incrementarles la propiedad zIndex de su estilo. Entonces, nuestro código JavaScript queda como se muestra a continuación:

function initializeRequest(sender, args){
    var mpeLoading = $find('idmpeLoading');
    mpeLoading.show();
    mpeLoading._backgroundElement.style.zIndex += 10;
    mpeLoading._foregroundElement.style.zIndex += 10;
}
function endRequest(sender, args){
    $find('idmpeLoading').hide();
}
Sys.WebForms.PageRequestManager.getInstance().add_initializeRequest(initializeRequest);
Sys.WebForms.PageRequestManager.getInstance().add_endRequest(endRequest);

Por último, para hacer que esta funcionalidad esté presente en todas las páginas de nuestra aplicación, lo agregamos en la página «master» del sitio, logrando que en cualquier página (que utilice esta master) posea la funcionalidad aquí descripta.

En la siguiente imagen muestro como queda una aplicación de ejemplo que cree con Visual Studio 2008 para este artículo:

A continuación dejo el proyecto con el código fuente para quien quiera utilizar esta utilidad:

Salir de la versión móvil