Centrar UpdateProgress en un UpdatePanel ( JQuery )

Todos conocemos el control UpdateProgress de ASP.NET y el gran avance que supone para la experiencia de usuario cuando trabajamos con UpdatePanels.

Pero para mi gusto le ha faltado poder posicionar ese control sobre el panel que estamos actualizando y de esta manera dejar mucho más claro al usuario que parte de la pantalla estamos actualizando.

Para eso después de consultar muchos ejemplos que no me terminaban de convencer y con un poquito de imaginación he utilizado un poquito de Javascript para poder realizar este efecto.

Comencemos con los controles que necesitamos para recrear este efecto:

  • Progress.ascx: Este control de usuario es el que contendrá la barra de proceso, en este caso una animación y una label con el texto «cargando…», la ventaja de utilizar un control de usuario es que no tendremos que repetir el código para cada página que necesitemos una barra de progreso y si nuestra aplicación es multiidioma podremos utilizar los recursos para mostrar el mensaje en el idioma adecuado.

  • UpdatePanel: El panel que utilizaremos para recargar partes de nuestra página asíncronamenete. Dentro del UpdatePanel tendremos:
    • Una Label: Título del registro.
    • Un TextBox: Para recuperar el nombre del usuario.
    • Un Botón: Para lanzar la petición asíncrona.
    • Una Label: Para mostrar el resultado.

Sé que no es lo más recomendable poner todos los controles dentro del updatePanel si solo queremos actualizar finalmente una label, pero para no complicar el ejemplo no utilizaré los Triggers.

En el click del botón lo que haremos es recoger el texto del textbox, mostrar el resultado en la segunda label y para que el efecto del updateProgress retardamos un poco el Thread.

protected void Button1_Click(object sender, EventArgs e)
{
    System.Threading.Thread.Sleep(5000);
    lblTest0.Text = string.Format("Tu nombre es {0}", TextBox1.Text);
}

Para utilizar este control tendremos dos posibilidades, utilizar el UpdateProgress de toda la vida con nuestro Progress.ascx dentro o un simple div oculto que tras el código javascript tendrá el mismo efecto. Yo para esta prueba he optado por el div.

<div id="progress" style="display: none;">
    <uc1:Progess ID="ProgessControl" runat="server" />
</div>

Ahora veremos el verdadero protagonista del artículo, que es tratar de mejorar la experiencia del usuario modificando el DOM con JavaScript y en este caso utilizaremos un framework de javascript «JQuery» que está teniendo mucho éxito entre los desarrolladores, gracias a su simplicidad y potencia de uso.

1. Lo primero que tenemos que hacer es poner una referencia a la librería JQuery.

<script src="Recursos/jquery-1.2.6.min.js" type="text/javascript"></script>

2. Luego crearemos nuestro propio fichero js para no tener el código mezclado con la página, pero la referencia la ubicaremos con el scriptManager porque necesitamos poder acceder a la funcionalidad de las librerías de ASP.NET AJAX.

<asp:ScriptManager ID="ScriptManager1" runat="server">
    <Scripts>
        <asp:ScriptReference Path="Recursos/JScript.js" />
    </Scripts>
</asp:ScriptManager>

3. Lo primero en nuestro script es declarar los eventos beginRequest y endRequest para que utilicen las funciones que definiremos en nuestro fichero.

Sys.Application.add_load(ApplicationLoadHandler);
function ApplicationLoadHandler() {
    Sys.WebForms.PageRequestManager.getInstance().add_beginRequest(beginRequest);
    Sys.WebForms.PageRequestManager.getInstance().add_endRequest(endRequest);
}
  • Sys.WebForms.PageRequestManager.getInstance().add_beginRequest:  El evento beginRequest se provoca antes de que se inicie el procesamiento de una devolución de datos asincrónica y se envíe la devolución de datos al servidor. Puede utilizar este evento para llamar a un script personalizado que establezca un encabezado de la solicitud o que inicie una animación que notifique al usuario que se está procesando la devolución de datos.
  • Sys.WebForms.PageRequestManager.getInstance().add_endRequest: El evento endRequest se provoca después de que finalice una devolución de datos asincrónica y se haya devuelto el control al explorador. Puede utilizar este evento para proporcionar una notificación a los usuarios o para registrar los errores.

beginRequest:

function beginRequest(sender, args) {       

    //Recuperamos el identificador del updatePanel que ha lanzado la petición asincrona
    var id = sender._postBackSettings.panelID.split('|')[0];
    while ( id.indexOf('$') != -1)
        id = id.replace('$', '_');
    if (!sender.get_isInAsyncPostBack()){  

        if ( $("#"+id).length & $("#progress").length ){
          $("#"+id).fadeTo("slow", 0.7);
          //Calculamos la posición para centrar el progress
          var x = $("#"+id).position().left + ($("#"+id).children(0).width()
            / 2)  - ($("#progress").children(0).width()  / 2);
          var y = $("#"+id).position().top +( $("#"+id).children(0).height()
            / 2) - ($("#progress").children(0).height() / 2);        

          Sys.UI.DomElement.setLocation($("#progress")[0], Math.round(x), Math.round(y));
          //Mostramos el progress
          $("#progress").show("slow");
        }
        //DesHabilita los botones
        $('input[@type=submit]').attr('disabled', true);
    }
}

 

Sé que la primera parte es un poco manual, pero no he encontrado otra manera de saber cual es el updatePanel que ha lanzado la llamada asíncrona al iniciar la petición. Recuperamos el identificador del control que ha lanzado la petición y su panel, separamos el id del panel y sustituimos los signos $ por _ para poder buscarla posteriormente el control por su identificador correctamente.

var id = sender._postBackSettings.panelID.split('|')[0];
   while ( id.indexOf('$') != -1)
       id = id.replace('$', '_');

Luego comprobamos que la llamada es realmente asíncrona y que el UpdatePanel y el div que utilizaremos como updateProgress existen.

if (!sender.get_isInAsyncPostBack()){
        if ( $("#"+id).length & $("#progress").length ){

Si os fijáis en el segundo if ya comenzamos a utilizar la potencia de JQuery $(«#»+id).length. $ significa que estamos utilizando una función JQuery, buscamos el control por su identificador gracias al «#».

$(«#»+id).fadeTo(«slow», 0.7);  Nos permitirá muy fácilmente animar el updatePanel con un efecto Fade para marcar el panel que estamos actualizando.

Luego calcularemos la posición que tiene que ocupar el updateProgress en el centro del UpdatePanel y lo mostramos con show de JQuery.

Sys.UI.DomElement.setLocation($("#progress")[0], Math.round(x), Math.round(y));
         //Mostramos el progress
         $("#progress").show("slow");

Finalmente he utilizado otra potente función de JQuery para deshabilitar todos los botones del la página mientras se realice la petición asíncrona, que esto mismo con javascript tendríamos que haber utilizado muchas más líneas para poder realizar lo mismo.

//DesHabilita los botones
$('input[@type=submit]').attr('disabled', true);

endRequest:

function endRequest(sender, args) {

 if (!sender.get_isInAsyncPostBack()){
    var id = sender._postBackSettings.panelID.split('|')[0];
    while ( id.indexOf('$') != -1)
        id = id.replace('$', '_');
    if (!sender.get_isInAsyncPostBack()){
        $("#"+id).fadeTo("fast",100);
        $("#progress").hide("fast");
    }
    $('input[@type=submit]').attr('disabled', false);
  }
}

 

La función que se lanza al finalizar la petición lanza la animación Fade a la inversa, oculta el div que hemos utilizado de updateProgress y vuelve a activar todos los botones.

Finalmente el resultado es un updateProgress que se adapta al tamaño del Panel, le da un efecto fade para realzar la carga y deshabilita los botones para evitar que el usuario provoque otra petición sin que se haya terminado la anterior.

Este código lo he probado en IE 7 y Firefox 2.0.0.6

Un consejo más para finalizar el artículo, si utilizáis un MasterPage le podéis poner el div con el updateProgres y todas las páginas que utilicen este masterPage contarán con la funcionalidad comentada.  😉

Espero que este experimento os haya gustado.

 

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *

Scroll al inicio