La tecnología AJAX nos permite desarrollar aplicaciones web haciendo uso de nuevos diseños (o incluso podríamos decir que paradigmas) complementarios o substitutivos al proceso tradicional de desarrollo de web sites en ASP.NET. Uno de esos métodos de diseño de web sites es el diseño de la interfaz de página única (Single-page interface). 

Normalmente, cuando se acomete la tarea de diseñar una aplicación web, uno intenta reducir el número de páginas a diseñar a la mínima expresión (una página para la autenticación de usuario, otra página con el mapa del site, otra más para la gestión de usuarios, y así hasta obtener un número de páginas que nos permita un mantenimiento relativamente simple de la aplicación, así como para la extensibilidad de la misma). Pues bien, y porqué no crear una aplicación web que sólo disponga de una única página (WebForm)?

Imaginemos la siguiente situación:

Tenemos una página que dispone de un menú horizontal en la parte superior de la pantalla. Este menú nos permite navegar por nuestro site. La imagen muestra el escenario descrito:

Sin tener en cuenta el modelo de interfaz de página unica, podríamos decidir crear un webform para el “Home”, otra para “Manager’s Zone”, una más para “Costumers” y así por cada uno de los items que contiene el menú. Pero el diseño (alternativo, por supuesto) que vamos a proponer sólo dispone de una única página aspx (un único webform). Al hacer click sobre cada uno de los items del menú, lo que haremos es enviar una petición asíncrona para recargar el contenido de el único control que contiene nuestra página, un UpdatePanel. Este UpdatePanel contiene un control PlaceHolder donde iremos substituyendo los controles por aquellos que deban ser mostrados en cada momento. Cabe recordar que para hacer uso de ASP.NET AJAX es necesario incorporar el control ScriptManager a la página, e indicar al control UpdatePanel, cual o cuales serán los disparadores (triggers) que harán que el UpdatePanel se recargue. Para que se vea la propuesta, aquí muestro el diseño en Visual Web Developer:

Nosotros crearemos UserControl’s por cada uno de los items del menú (podríamos añadir los controles necesario para cada item de forma programática, pero hemos decidido utilizar UserControl’s). Asi pues, cuando el usuario seleccione un item del menú, se cargará el UserControl adecuado. Para ello, añadiremos al UpdatePanel un disparador (trigger) que apunte al evento MenuItemClick tal y como muestra esta imagen:

Todos los eventos que se lancen desde el UserControl que se ha cargado en el UpdatePanel, se ejecutarán de forma asíncrona. Esto lleva implícito un problema: Cuando el usuario lanza un evento del UserControl al servidor, evidentemente se ejecuta un Postback en el que se envian todos los datos de la página actual. Cuando ASP recibe la petición lo que hace es cargar los controles que se han especificado en el webForm solicitado (es decir, se cargan los controles de la única página que se ha especificado). Esto significa que los controles que se han cargado dinamicamente (es decir el contenido del PlaceHolder del UpdatePanel, en nuestro caso el UserControl) ASP no los cargará.

Intentaré explicarlo de forma más simple. Cuando ASP recibe una petición por una determinada página (por ejemplo la página Defualt.aspx) ocurre lo siguiente:

– ASP cogerá la página Default.aspx y la utilizará a modo de plantilla, creando en memoria los controles y los tags html que se indican en la misma.

– Después, utilizará los datos del ViewState que llegan en la petición para rellenar los datos y reestablecer el estado de los controles que se han generado en memória.

Así pues, ASP cargará el control PlaceHolder, pero no su contenido. Será responsabilidad nuestra cargar en el PlaceHolder el control que ha lanzado el evento que ha provocado el Postback que se está tratando. Evidentemente cargaremos el control siempre y cuando el Postback no esté provocado por el menú.

Para determinar la procedéncia del PostBack, haremos uso de una propiedad del control ScriptManager (control necesario para hacer uso de ASP.NET AJAX) llamada AsyncPostBackSourceElementID. Esta propiedad nos indicará el Identificador del control que ha lanzado el PostBack. Si el postback proviene del menu, cargaremos en el control UpdatePanel (mejor dicho, en el PlaceHolder que hay en su interior) el UserControl perteneciente al item seleccionado. En caso de que el Postback esté generado por otro control, primero será necesario recargar el UserControl actual con tal de que pueda recargar los datos que le llegan del Postback, y ejecutar los eventos que el usuario haya lanzado. 

A continuación muestro el código que proporciona la funcionalidad que hemos comentado:

 

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;

public partial class _Default : System.Web.UI.Page 
{

    /// <summary>
    /// Constante que identifica el control cargado actualmente en el UpdatePanel
    /// </summary>

    private const string ViewStateControl = “CurrentLoadedControl”;   

    protected void Page_Load(object sender, EventArgs e)
    {

        //Al hacer un load, debemos saber si es por un postback o por que se ha
        //generado la pagina por primera vez. 
        //Si es un postback, es necesario saber si lo ha producido el menu, o alguno
        //de los UserControls que hemos cargado en el UpdatePanel

        if (IsPostBack && !IsAMenuItemPostBack())
        {

            //El postback lo ha producido un usercontrol cargado en el UpdatePanel,
            //por lo tanto, debemos recargar ese control con tal de que le lleguen los 
            //datos de Postback (que estan contenidos en el ViewState) y se pueda atender el 
            //evento que ha generado el PostBack.
 

            Control control = null;

            try
            {
                control = this.LoadControl(ViewState[ViewStateControl] as string);
            }
            catch (Exception)
            {
                throw;
            }

            if (control != null)
            {
                MainPlaceHolder.Controls.Add(control);
            }
        }
    }

    protected void MainMenu_MenuItemClick(object sender, MenuEventArgs e)
    {
        //Si el item seleccionado no es el actual, se debe cargar el UserControl
        //que debe mostrarse en el UpdatePanel.

        string controlToLoad = e.Item.Value+”.aspx“;

        if (ViewState[ViewStateControl] as string != controlToLoad)
        {
            Control control = LoadControl(controlToLoad); 

            if (control != null)
            {
                MainPlaceHolder.Controls.Clear();
                MainPlaceHolder.Controls.Add(control); 

                ViewState[ViewStateControl] = controlToLoad;
            }
        }
    }

    private bool IsAMenuItemPostBack()
    {
        //Miramos si el control que ha generado el Postback es el MainMenu.
        if (MyScriptManager.AsyncPostBackSourceElementID == “MainMenu“)
            return true;
        else
            return false
;
    }
}

Queda claro en el código que es necesario dar a la propiedad Value de cada MenuItem el nombre del UserControl que se va a cargar (es una propuesta de funcionamiento. Bien podríamos utilizar una clausula switch o if anidados si se cree conveniente. 

Así es como implementaríamos la interfaz de página única. De esta forma obtendriamos una aplicación totalmente implementada haciendo uso de AJAX, lo que proporcionará una interfaz de usuario más interactiva.

 

Es evidente que sobre este modelo pueden discutirse la mantenibilidad y extensibilidad (quizás más complejas que en otros modelos), pero no es mi intención vender este modelo como la única ni mejor alternativa, pero si como eso, como una alternativa. No todos los proyectos requieren de un modelo en tres capas, ni de una inversión enorme en el diseño del mismo para que sea fácil su mantenimiento. Evidentemente hay que evaluar las ventajas e inconvenientes de este modelo antes de proponerlo siquiera como solución a algún problema aunque esta práctica debería hacerse con cualquier modelo y/o paradigma. Y digo esto último porque pocas veces alguien se atreve a cuestionar que utilizar el paradigma de la programación orientada a objetos sea el conveniente para un proyecto concreto (cuando es una de las primeras cuestiones que deberíamos plantearnos). Hay alternativas a este paradigma que nos pueden facilitar el desarrollo de un proyecto (paradigma de programación guiada por eventos, programación secuencial, programación funcional, programación basada en políticas, y muchos otros paradigmas).

Como siempre, espero que os resulte interesante este artículo. Espero vuestros comentarios!!!

Gracias!!😉