Indudablemente AJAX es una de las tecnologías que mejoran la satisfacción del usuario (menos mal, porque sino no nos pagan ...), de manera que esta publicación pretende mostrar un ejemplo de cómo se puede utilizar.
Simpre que desarrollo una aplicación me ha preocupado mantener una especie de diálogo con el usuario final, avisarle que está ocurriendo y por supuesto cuando las cosas no funcionan indicarle de una manera razonable que lamentablemente debe llamarme para arreglar el problema ...
Por otro lado, mi participación en las Celulas Académicas de Microsoft (Jujuy ASP NET y THE ASP-BERRIES) me enseñan que es importante mostrar algunos aspectos del desarrollo, los que a veces nos parecen trivales.
Bien, la idea es contar con un mecanismo que nos permitma mostrar mensajes en una aplicación WEB.
Cuando desarrollamos para escritorio siempre podremos utilizar el MsgBox. Pero resulta que en el desarrollo de aplicaciones WEB no existe, si bién hay bastantes ejemplos sobre cómo armar una ventana modal que muestre mensajes, ocurre que mi ego y preferencias personales me llevaron a escribir el siguiente código. Obviamente le copie la idea a la gente de Gmail (no se cómo será el código que utilizan pero esta es la implementación que se me ocurrió).
Una aplicación WEB seguramente tiene una Master Page (al menos en el mundo de ASP NET así es ...), además se incorpora una hoja de estilo que nos permite cambiar la presentación de forma rápida y elegante. A todo esto, la programción orientada a objetos nos brinda la posibliad de hacer que todas las páginas de la aplicación sean derivadas o hereden propiedades y métodos de alguna clase base que implementa el comportamiento común que deseamos para la aplicación.
Lo primero es crear un proyecto, y como vamos a utilizar AJAX entonces en el Visual Studio 2008, utilizamos File-New-Project... y en las opciones elegimos Visual C# para ver todas las plantillas disponibles de las cuales marcamos AJAX 1.0-Enabled ASP.NET 2.0 W...
Bueno hay que darle un nombre e indicar si se va a crear una solución para este proyecto. Cuando está todo listo tendrán un proyecto que incorpora todo lo necesario para desarrollor un sitio web que cuenta con las extensiones AJAX que ya vienen en el Microsoft Framework 3.5.
Recuerden que para el Framework 2.0 hay que bajarse las extensiones e instalarlas, todo esto se puede ver mejor en la página oficial de AJAX.
Con el proyecto en la mano, vamos a incorporar una master page que debe contener al menos lo siguiente:
- Un control <asp:ScriptManager ... />, este control es imprecindible si vamos a utilizar comunicación asincrónica con el servidor.
- Un control <asp:UpdatePanel> ... </asp:UpdatePanel>, nos permitirá mostrar los mensajes sin recargar toda la página (a esto es lo que le llaman mayor satisfacción del usuario final).
- Un contenedor simple <div> ... </div> al que le asignaremos una clase de la hoja de estilo (más adelante detallamos como es el estilo).
- Un control <asp:Label> ... </asp:Label> que utilizaremos para mostrar los mensajes, es posible utilizar otro tipo de control pero por ahora quiero mantener las cosas lo más simples posibles.
- Un control <asp:Timer> ... </asp:Timer>, con el que controlaremos el tiempo que se muestran los mensajes.
A continuación está el código de la master page, observen que yá está haciendo referencia a una hoja de estilo, mas adelante voy a explicar lo que haga falta sobre ella.
Es importante destacar que el control Label tiene el ViewState en False, de ese modo no tenemos que borrar los mensajes dado que esa información solamente viene del servidor se muestra en el browser del cliente y no vuelve al servidor en el siguiente postback se o no asincrónico.
La otra cuestión importante es que al control UpdatePanel hay que indicarle que tiene un "gatillo" trigger que lo relaciona con el control Timer, de esta manera es como se logra que el timer se ejecute de acuerdo al intervalo que le indicamos (en este ejemplo es 10000 milisegundos) y entonces es atrapado por el trigger del UpdatePanel que lo fuerza a realizar el postback asincrónico.
Las otras cosas que están en la master page son las que corresponden a la aplicación y no tienen nada que ver con el esquema de Mensajes.
Listado 1: MasterPage.master
1: <%@ Master Language="C#" AutoEventWireup="true" CodeBehind="MasterPage.master.cs" Inherits="AJAXDemo30.MasterPage" %>
2:
3: <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
4:
5: <html xmlns="http://www.w3.org/1999/xhtml" >
6: <head runat="server">
7: <title>AJAX Demo in MS Framework 3.0</title>
8: <link href="Styles/SiteStyle.css" rel="stylesheet" type="text/css" />
9: <asp:ContentPlaceHolder ID="head" runat="server">
10: </asp:ContentPlaceHolder>
11: </head>
12: <body>
13: <form id="form1" runat="server">
14: <asp:ScriptManager ID="ScriptManager1" runat="server" />
15: <div>
16: <div id="center">
17: Aquí debería estar el título y menú de la aplicación
18: <hr />
19: <asp:UpdatePanel id="updpnlMsg" runat="server" UpdateMode="Conditional" >
20: <ContentTemplate>
21: <div class="divMsg">
22: <asp:Label id="lblMsg" runat="server" EnableViewState="False" CssClass="lblMsg"></asp:Label>
23: </div>
24: </ContentTemplate>
25: <Triggers>
26: <asp:AsyncPostBackTrigger ControlID="timerMsg" />
27: </Triggers>
28: </asp:UpdatePanel>
29: <asp:Timer ID="timerMsg" runat="server" Interval="10000" >
30: </asp:Timer>
31: <hr />
32: <asp:ContentPlaceHolder ID="ContentPlaceHolder1" runat="server">
33: ... En este lugar puede estar algo que siempre queremos que esté
34: </asp:ContentPlaceHolder>
35: <hr />
36: Aquí deberí estar el pié de página, siempre es bueno hacer un poco de propagana
37: </div>
38: </div>
39: </form>
40: </body>
41: </html>
El código asociado a la Master Page no tiene nada, solamente lo que el Visual Studio le pone, de manera que no lo muestro.
Ahora veamos como funciona una página de la aplicación. Para ello vamos a borrar la que puso el Visual Studio cuando creamos el proyecto, resulta que está no utiliza Master Page.
Agregamos una página que utilice Master Pages, se hace con Add-New Item ... y elegimos Web Content Form, le damos el nombre Default.aspx (esto no es necesario, pero normalmente esta es la página de inicio de un sitio web) y luego le indicamos que página maestra queremos asociarle.
Observern que le puse algo de texto, y un par de botones que serviran para utilizar el esquema de mensajes. A continuación está el código de la página.
Listado 2: Default.aspx
1: <%@ Page Language="C#" MasterPageFile="~/MasterPage.Master" AutoEventWireup="true" CodeBehind="Default.aspx.cs" Inherits="AJAXDemo30.Default" Title="AJAX Demo in MS Framework 3.0" %>
2: <asp:Content ID="Content1" ContentPlaceHolderID="head" runat="server">
3: </asp:Content>
4: <asp:Content ID="Content2" ContentPlaceHolderID="ContentPlaceHolder1" runat="server">
5: <asp:UpdatePanel id="upnl1" runat="server" >
6: <ContentTemplate>
7: <p>
8: Esta es la página por defecto del Demo de AJAX en MS Framework 3.0
9: <br />
10: Vamos a probar como funciona el esquema de mensajes
11: </p>
12: <p>
13: El Mensaje Normal muestra un texto durante un tiempo (en este caso es de 10
14: segundos) y luego desaparace.
15: </p>
16: <p>
17: En cambio el Mensaje Crítico muestra un texto que debe estar en pantalla mucho
18: más tiempo (ahora lo hace por una hora).
19: </p>
20: <p>
21: Por supuesto, si el usuario realiza alguna operación que implique un mensaje,
22: este mensaje es reemplazado por el otro y seguramente cambia la duración establecida
23: </p>
24: <p>
25: Sirve para mostrar los mensajes de error cuando las cosas no funcionan bien.
26: </p>
27: <div class="right">
28: <asp:Button ID="btnMsgNormal" runat="server" Text="Mensaje Normal" onclick="btnMsgNormal_Click" />
29:
30: <asp:Button ID="btnMsgCritial" runat="server" Text="Mensaje Crítico" onclick="btnMsgCritial_Click" />
31: </div>
32: </ContentTemplate>
33: </asp:UpdatePanel>
34: </asp:Content>
El código asociado a la página es el siguiente. Simplemente se invoca a un par de metodos MsgSet() y MsgSetCritical() que nos permite enviar mensajes.
Observen que la clase deriva de PageBase, es en esa clase donde vamos a poner el código del esquema de mensajes.
Listado 3: Default.aspx.cs
1: using System;
2:
3: namespace AJAXDemo30
4: {
5: public partial class Default : PageBase
6: {
7: protected void Page_Load(object sender, EventArgs e)
8: {
9: }
10:
11: protected void btnMsgNormal_Click(object sender, EventArgs e)
12: {
13: MsgSet("Este mensaje debería desaparecer en 10 segundos !!!");
14: }
15:
16: protected void btnMsgCritial_Click(object sender, EventArgs e)
17: {
18: MsgSetCritical("Este es un mensaje crítico se mantiene <b>durante una hora</b>");
19: }
20:
21: }
22: }
Bueno veamos entonces como es la clase base que implementa el esquema de mensajes. En este ejercicio incorporé la clase directamente en la carpeta raíz del sitio web, esta clase debería estar en un directorio de clases o mejor aún en un dll probablemente en otro proyecto para que de ese modo se pueda reutilizar desde todos los proyectos que queramos.
Hay unos campos privados para referenciar a los controles que se supone existen en la master page de la instancia de página que está ejecutando los métodos.
La propiedad MsgIsOk se utiliza para asegurarse que existen los controles. El método MsgSetTimer sirve para fijar el intervalo del control Timer para que se dispare el postback asincrónico en el Update Panel. El método MsgShow es el encargado de actualizar el Update Panel.
Los métodos MsgSet y MsgSetCritical que se pueden heredar simplemente le dan formato al mensaje y ajustan el valor del timer según sea un mensaje común (10 segundos) o un mensaje crítico (1 hora).
Listado 4: PageBase.cs
1: using System;
2: using System.Web.UI;
3: using System.Web.UI.WebControls;
4:
5: namespace AJAXDemo30
6: {
7: /// <summary>
8: /// Clase base para todas las páginas
9: /// </summary>
10: public abstract class PageBase : System.Web.UI.Page
11: {
12:
13: #region Messages
14:
15: /// <summary>
16: /// Reference to Label lblMsg in Master Page
17: /// </summary>
18: private Label _lblMsg;
19: /// <summary>
20: /// Reference to Timer timerMsg in Master Page
21: /// </summary>
22: private Timer _timerMsg;
23:
24: /// <summary>
25: /// Test if exist reference for lblMsg in Master Page
26: /// also it test if exist reference for timerMsg in Master Pager too
27: /// </summary>
28: private Boolean MsgIsOk
29: {
30: get
31: {
32: this._lblMsg = (Label)this.Master.FindControl("lblMsg");
33: this._timerMsg = (Timer)this.Master.FindControl("timerMsg");
34: return (this._lblMsg != null);
35: }
36: }
37: /// <summary>
38: /// Set timer interval in miliseconds
39: /// </summary>
40: /// <param name="interval"></param>
41: private void MsgSetTimer(int interval)
42: {
43: if (this._timerMsg != null)
44: {
45: _timerMsg.Interval = interval;
46: }
47: }
48: /// <summary>
49: /// Shows messages with partial postback
50: /// </summary>
51: private void MsgShow()
52: {
53: UpdatePanel updpnlMsg = (UpdatePanel)this.Master.FindControl("updpnlMsg");
54: if (updpnlMsg != null)
55: {
56: updpnlMsg.Update();
57: }
58: }
59: /// <summary>
60: /// Show a message
61: /// </summary>
62: /// <param name="msg">Message text to put in Label control.</param>
63: protected void MsgSet(string msg)
64: {
65: if (MsgIsOk)
66: {
67: MsgSetTimer(10000);
68: _lblMsg.Text = " " + msg + " ";
69: MsgShow();
70: }
71: }
72: /// <summary>
73: /// Show a message preceded with critial error
74: /// </summary>
75: /// <param name="msg">Message text to put in Label control</param>
76: protected void MsgSetCritical(string msg)
77: {
78: if (MsgIsOk)
79: {
80: MsgSetTimer(3600000);
81: _lblMsg.Text = " " + string.Format("Error crítico: {0}", msg) + " ";
82: _lblMsg.ForeColor = System.Drawing.Color.Red;
83: MsgShow();
84: }
85: }
86:
87: #endregion
88:
89: }
90: }
Por último comentamos un poco de la hoja de estilo, en body tiene lo mínimo para una página de 800x600. Hay una clase .right que permite poner los controles a la derecha.
El estilo para el esquema de mensajes básicamente establece un área minima min-height de manera que la página no esté cambiando de tamaño a cada rato, utilizar min-heght permite que se muestren grandes bloques de texto dado que se establece el mínimo.
Listado 5: SiteStyle.css
1: body {
2: font-family: Verdana, Tahoma, Arial, Helvetica, sans-serif;
3: font-size:12px;
4: margin:0;
5: padding:0;
6: width:780px;
7: }
8: /* Esto es el para el contenedor principal */
9: #center {
10: position:relative;
11: width:100%;
12: }
13: .right {
14: float: right;
15: }
16:
17: /* Esquema de Mensajes */
18: .divMsg {
19: width: auto;
20: min-height: 20px;
21: text-align: center;
22: }
23: .lblMsg {
24: background: #CCFF99;
25: color: #000000;
26: font-size: 1.1em;
27: /* font-weight: bold;*/
28: }
Bien, después de realizar el ejercicio quede bastante conforme con el resultado y lo estoy utilizando. Sin embargo siempre vuelvo a pensar que debería implementar un mecanismo que active y desactive el timer dado que una vez que se borra un mesaje no hace falta estar disparando el autoposback del Update Panel a cada rato dado que no hay nada para borrar y es una acción innecesaria, pero eso lo dejo para otra publicación.
Auí está el proyecto completo para que se lo bajen AJAXDemo30.zip (12,48 kb), si lo pueden mejorar me avisan.
Espero que les sirva