Precarga en ASP.NET con AJAX (2)
En el artículo anterior vimos cómo realizar una precarga con el control UpdatePanel, en esta ocasión vamos a ver cómo implementar nuestro sistema de precarga en una página independiente y redirigir al usuario a la página donde se mostrarán los datos.
Primero el usuario seleccionaría la información de un producto, una vez seleccionado, redirigimos la petición a otra página donde se cargaría de forma asíncrona mientras muestra una animación (para que el usuario no desespere), obtenida la información del producto en cuestión, la guardamos en el estado de la sesión y le redirigimos a la página donde se mostrará la información del producto que obtendremos de la variable de sesión.
Una vez vistos los preliminares, empezamos con el código.
La primera página es la que mostrará el formulario de búsqueda de producto:
viewproduct.aspx
<%@
Page
Language=”C#”
AutoEventWireup=”true” %>
<!DOCTYPE
html
PUBLIC
“-//W3C//DTD XHTML 1.1//EN”
“http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd”>
<script
runat=”server”>
protected
void DropDownList1_SelectedIndexChanged(object sender, EventArgs e)
{
if(DropDownList1.SelectedIndex>0)
Response.Redirect(“loadproduct.aspx?id=” + DropDownList1.SelectedValue);
}
</script>
<html
xmlns=”http://www.w3.org/1999/xhtml”>
<head
runat=”server”>
<title>Untitled Page</title>
</head>
<body>
<form
id=”form1″
runat=”server”>
<div>
Seleccione una categoría para ver sus productos:
<asp:DropDownList
ID=”DropDownList1″
runat=”server”
onselectedindexchanged=”DropDownList1_SelectedIndexChanged”
AutoPostBack=”True”>
<asp:ListItem>– Productos –</asp:ListItem>
<asp:ListItem
Value=”1″>Carnes</asp:ListItem>
<asp:ListItem
Value=”2″>Verduras</asp:ListItem>
<asp:ListItem
Value=”3″>Pescados</asp:ListItem>
</asp:DropDownList>
</div>
</form>
</body>
</html>
La segunda es la más importante, es donde cargaremos el producto asincronamente y se lo asignaremos a una variable de sesión:
loadproduct.aspx
<%@
Page
Language=”C#” %>
<%@
Import
Namespace=”System.Collections.Generic” %>
<%@
Import
Namespace=”System.Web.Services” %>
<!DOCTYPE
html
PUBLIC
“-//W3C//DTD XHTML 1.0 Transitional//EN”
“http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd”>
<script
runat=”server”>
protected
void Page_Load(object sender, EventArgs e)
{
if (Request.QueryString["id"] == null)
Response.Redirect(“viewproduct.aspx”);
}
[WebMethod()]
public
static
void LoadProduct(int id)
{
//Aqui se cargaría el producto
System.Threading.Thread.Sleep(2000);//Esto es sólo para hacer más real el e.0tjemplo (no hacer esto en aplicaciones reales)
HttpContext context = HttpContext.Current;
List<Product> Productos=new
List<Product>();
Random precioAleatorio=new
Random();
//Para hacer más sencillo el ejemplo rellenamos una lista ficticia de productos. En un entorno
//real usaríamos el id pasado cómo parametro para recuperarla de un orígen de datos.
for(int i=0;i<=20;i++)
{
Product p=new
Product();
p.Nombre=“Producto_”+i;
p.Precio=(decimal)precioAleatorio.Next(2,25);
if (i > 0 && i < 5)
{
p.IdCat = 1;
p.Categoria = “Carnes”;
}
else
if (i > 5 && i < 12)
{
p.IdCat = 2;
p.Categoria = “Verduras”;
}
else
{
p.IdCat = 3;
p.Categoria = “Pescados”;
}
if(p.IdCat==id)
Productos.Add(p);
}
//Asignamos la lista de productos a una variable de sesión para recuperarla desde otra página
context.Session["ProductosActuales"] = Productos;
}
</script>
<html
xmlns=”http://www.w3.org/1999/xhtml”>
<head
runat=”server”>
<title>Cargando…</title>
<script
language=”javascript”
type=”text/javascript”>
// <!CDATA[
function load(id) {
PageMethods.LoadProduct(id, onOk, onError);
}
function onOk() {document.location = 'productinfo.aspx';}
function onError() { document.location.href = 'error.aspx'; }
// ]]>
</script>
</head>
<body
onload=”load(<%=Request.QueryString["id"]%>)”>
<form
id=”form1″
runat=”server”>
<div>
<asp:ScriptManager
ID=”ScriptManager1″
runat=”server”
EnablePageMethods=”True”>
</asp:ScriptManager>
<br
/>
<br
/>
<img
src=”loading.gif”
/> Cargando…</div>
</form>
</body>
</html>
En la tercera página mostramos los datos de la variable de sesión:
productinfo.aspx
<%@
Page
Language=”C#” %>
<!DOCTYPE
html
PUBLIC
“-//W3C//DTD XHTML 1.0 Transitional//EN”
“http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd”>
<script
runat=”server”>
protected
void Page_Load(object sender, EventArgs e)
{
if(Session["ProductosActuales"]==null)
Response.Redirect(“viewproduct.aspx”);
GridView1.DataSource = Session["ProductosActuales"];
GridView1.DataBind();
}
</script>
<html
xmlns=”http://www.w3.org/1999/xhtml”>
<head
runat=”server”>
<title></title>
</head>
<body>
<form
id=”form1″
runat=”server”>
<div>
</div>
<asp:GridView
ID=”GridView1″
runat=”server”
AutoGenerateColumns=”false”>
<Columns>
<asp:BoundField
DataField=”Precio”
DataFormatString=”{0:C}”
/>
<asp:BoundField
DataField=”Nombre”
/>
<asp:BoundField
DataField=”Categoria”
/>
</Columns>
</asp:GridView>
</form>
</body>
</html>
Soy consciente de que se han quedado algunas cosas en el tintero, lo más importante es saber que hay que establecer la propiedad EnablePageMethods
del control ScriptManager en true y declarar el método de página cómo static con el ámbito público.
Espero que os haya servido.
Add comment Septiembre 7, 2009
Mostrar progresos con BackgroundWorker
-Mediante la clase Thread.
-Mediante delegados asíncronos.
-Mediante el componente BackgroundWorker.
Se me ocurren diversas situaciones en la que cada uno puede ser más apropiado. Pero, si lo que queremos es mostrar el progreso de la tarea, la mejor opción es el componente BackgroundWorker. El componente BackgroundWorker tiene varias características que le hacen interesante; como puede ser la notificación del progreso y notificación de cancelación.
El ejemplo de código siguiente es un modo sencillo de dar soporte para cancelación y notificación del progreso de una tarea asíncrona.
| Nota: Este componente solo está disponible desde la versión 2.0 del Framework. |
Lo primero es arrastrar el componente a nuestra aplicación

Una vez que los hemos situado en el área de trabajo configuramos las siguientes propiedades:
WorkerReportsProgress = true
WorkerSupportsCancellation=true (solo en caso de querer dar soporte para cancelación)
Ahora definimos los eventos de cancelación, notificación y trabajo.
DoWork: Este evento es disparado al comienzo de la operación asíncrona y sera donde realicemos las tareas.
ProgressChanged: Este evento será disparado el notificar un progreso al componente.
RunWorkerCompleted: Este evento será disparado al terminar la operación asíncrona.
| Nota: Si desea acceder a un control desde la operación que este realizando el componente BackgroundWorker. Por ejemplo, mostrar el estado de progreso en un control Label, es más que aconsejable hacerlo desde el evento ProgressChanged, ya que la aplicación podria comportarse de forma inesperada. |
Ya podemos escribir el código que sera lanzado con cada evento.
C#
private void bwProgress_DoWork(object sender, DoWorkEventArgs e)
{
for (int i = 1; i <= 100; i++)
{
//Realiza una tarea
System.Threading.Thread.Sleep(100);
bwProgress.ReportProgress(i);
if (bwProgress.CancellationPending)
return;
}
}
private void bwProgress_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
//Notificar el progreso de la tarea
progressBar1.Value = e.ProgressPercentage;
lblInfo.Text = e.ProgressPercentage + “%”;
}
private void bwProgress_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
//Realizamos las operaciones que haya que realizar al terminar el progreso
lblInfo.Text = “Tarea terminada”;
btnCancelar.Enabled = false;
btnIniciar.Enabled = true;
progressBar1.Value = 0;
}
private void btnIniciar_Click(object sender, EventArgs e)
{
//Iniciamos el trabajo
if (!bwProgress.IsBusy)
{
bwProgress.RunWorkerAsync();
btnCancelar.Enabled = true;
btnIniciar.Enabled = false;
}
}
private void btnCancelar_Click(object sender, EventArgs e)
{
//Solicitamos la cancelación de la operación
bwProgress.CancelAsync();
}
VB
Private Sub bwProgress_DoWork(ByVal sender As System.Object, ByVal e As System.ComponentModel.DoWorkEventArgs) Handles bwProgress.DoWork
For i As Integer = 1 To 100
”Realiza una tarea
System.Threading.Thread.Sleep(100)
bwProgress.ReportProgress(i)
If (bwProgress.CancellationPending) Then
Return
End If
Next
End Sub
Private Sub bwProgress_ProgressChanged(ByVal sender As System.Object, ByVal e As System.ComponentModel.ProgressChangedEventArgs) Handles bwProgress.ProgressChanged
‘Notificar el progreso de la tarea
progressBar1.Value = e.ProgressPercentage
lblInfo.Text = e.ProgressPercentage + “%”
End Sub
Private Sub bwProgress_RunWorkerCompleted(ByVal sender As System.Object, ByVal e As System.ComponentModel.RunWorkerCompletedEventArgs) Handles bwProgress.RunWorkerCompleted
”Realizamos las operaciones que haya que realizar al terminar el progreso
lblInfo.Text = “Tarea terminada”
btnCancelar.Enabled = False
btnIniciar.Enabled = True
progressBar1.Value = 0
End Sub
Private Sub btnIniciar_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnIniciar.Click
”Iniciamos el trabajo
If (Not bwProgress.IsBusy) Then
bwProgress.RunWorkerAsync()
btnCancelar.Enabled = True
btnIniciar.Enabled = False
End If
End Sub
Private Sub btnCancelar_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnCancelar.Click
‘Solicitamos la cancelación de la operación
bwProgress.CancelAsync()
End Sub

Como hemos podido ver, el código es sencillo. Con unas pocas lineas podremos dar soporte estable a nuestras operaciones asíncronas.
3 comments Mayo 11, 2008
Delegados genéricos
Para poder entender correctamente los delegados genéricos debemos comprender antes algunos aspectos que explico a continuación.
Delegados:
Los delegados son una parte fundamental del Framework de .Net, pero… ¿Que es exactamente un delegado?, un delegado es un tipo que apunta a un método. Con la característica principal que dicho método ha de tener el mismo tipo de parámetros y valor devuelto.
| Nota: Los delegados son algo parecido a los punteros a funciones de C++, pero más potentes y seguros. |
Veamos un ejemplo:
C#
class Program
{
/*
Declaramos un delegado que apuntará a un metodo
que ha de retornar un tipo int y tener dos parametros de tipo int
*/
private delegate int CalculateHandler(int numA, int numB);
static void Main(string[] args)
{
//Creamos una instancia del delegado que apunte al metodo Calc
CalculateHandler calcDelegate = new CalculateHandler(Calc);
//Invocamos al delegado
Console.WriteLine(“Resultado de la invocación al delegado: “+calcDelegate(5,5).ToString());
//Otra forma de invocar al delegado
Console.WriteLine(“Resultado de la invocación al delegado con Invoke: “+calcDelegate.Invoke(5, 5).ToString());
Console.Read();
}
//Aquí hacemos la declaración del metodo para nuentro delegado.
private static int Calc(int numA, int numB)
{
return numA + numB;
}
}
VB
Module Module1
‘Declaramos un delegado que apuntará a un metodo que ha de retornar un tipo Integer y tener dos parametros de tipo Integer
Delegate Function CalculateHandler(ByVal numA As Integer, ByVal numB As Integer) As Integer
Sub Main()
‘Creamos una instancia del delegado que apunte al metodo Calc
Dim calcDelegate As New CalculateHandler(AddressOf Calc)
‘Invocamos al delegado
Console.WriteLine(“Resultado de la invocación al delegado: “ + calcDelegate(5, 5).ToString())
‘Otra forma de invocar al delegado
Console.WriteLine(“Resultado de la invocación al delegado con Invoke: “ + calcDelegate.Invoke(5, 5).ToString())
Console.Read()
End Sub
Private Function Calc(ByVal numA As Integer, ByVal numB As Integer)
Return numA + numB
End Function
End Module
En este sencillo ejemplo declaramos un delegado que apunta a una función y despues lo invocamos.
Tipos genéricos:
Desde la versión del Framework 2.0 se incluyen los tipos genéricos. Los tipos genéricos nos servirán para declarar código genérico, de forma que podremos preparar un elemento para recibir cualquier tipo.
| Nota: Antes de la implementación de los tipos genéricos se podía obtener un resultado parecido con el tipo object, que es el tipo base del cual descienden todos los tipos del .Net Framework. |
Veamos un ejemplo:
C#
class Program
{
//Lista genérica que contendrá elementos del tipo empleado
static List<Empleados> listaGenericaEmpleados;
static void Main(string[] args)
{
listaGenericaEmpleados = new List<Empleados>();
listaGenericaEmpleados.Add(new Empleados());
}
public class Empleados
{//Miembros de la clase empleados.
}
}
VB
Module Module1
‘Lista genérica que contendrá elementos del tipo empleado
Dim listaGenericaEmpleados As List(Of Empleados)
Sub Main()
Dim listaGenericaEmpleados As New List(Of Empleados)
listaGenericaEmpleados.Add(New Empleados())
End Sub
Class Empleados
‘Miembros de la clase empleados.
End Class
End Module
En este ejemplo declaramos una lista que contendrá elmentos del tipo empleados que ha sido definido por nosotros.
Delegados genénericos:
Al principio de este post, he descrito que un delegado ha de tener el mismo tipo de parámetros y valor devuelto que el método al que apuntará, pero… ¿Y si un delegado se pudiera declarar con parámetros del tipo genérico, y así poder dar flexibilidad a la función a la que apunte para poder implementar cualquier tipo de parámetros sin la necesidad de que sean del tipo object? Eso se consigue con los delegados genéricos.
C#
class Program
{
//Declaramos un delegado genérico
delegate string GenericDelegate<T,T2>(T valor1, T2 valor2);
static void Main(string[] args)
{
//Instanciamos el delegado genérico con tipos int y string
GenericDelegate<int, string> delegado1 = new GenericDelegate<int, string>(Valores);
Console.WriteLine(delegado1(1,“1″));
//Instanciamos el delegado genérico con tipos string y string
GenericDelegate<string, string> delegado2 = new GenericDelegate<string, string>(Valores);
Console.WriteLine(delegado2(“1″, “1″));
Console.Read();
}
public static string Valores(int val1, string val2)
{
return val1 + val2;
}
public static string Valores(string val1, string val2)
{
return val1 + val2;
}
}
VB
Module Module1
‘Declaramos el delegado genérico
Delegate Function GenericDelegate(Of T, T2)(ByVal val1 As T, ByVal val2 As T2) As String
Sub Main()
‘Instanciamos el delegado genérico con tipos int y string
Dim delegado1 As New GenericDelegate(Of Integer, String)(AddressOf Valores)
Console.WriteLine(delegado1(1, “1″))
‘Instanciamos el delegado genérico con tipos string y string
Dim delegado2 As New GenericDelegate(Of String, String)(AddressOf Valores)
Console.WriteLine(delegado2(“1″, “1″))
Console.Read()
End Sub
Private Function Valores(ByVal val1 As Integer, ByVal val2 As String)
Return val1 + val2
End Function
Private Function Valores(ByVal val1 As String, ByVal val2 As String)
Return val1 + val2
End Function
End Module
Como hemos podido ver, los delegados genericos nos ofrecen una flexibilidad importante a la hora de desarrollar de una forma más escalable.
1 comment Mayo 10, 2008
Acceso a base de datos embebidas Firebird desde .net
Hace tiempo, se me planteo la necesidad de implementar una base de datos independiente de un servidor y que el mantenimiento de dicha base de datos fuera lo más escalable posible, cuando programas desde y hacia tecnologías de Microsoft la respuesta es obvia: Access, pero Access tiente el serio inconveniente de que no acepta procedimientos almacenados, para mi esta característica es fundamental, ya que los procedimientos almacenados aumentan la potencia y escalabilidad de la base de datos de una forma considerable, entonces ¿Cómo lo hacemos?, ¿es posible implementar una base de datos que admita procedimientos almacenados y se pueda implementar con nuestra aplicación sin un servidor de base de datos?, la respuesta en sí, se llama Firebird. Firebird es una base de datos relacional (RDBMS) de código abierto y que además dispone de unas bibliotecas para .Net Framework.
Manos a la obra.
Lo primero es descargar desde aquí la versión embedded, que nos permitirá, como su propio nombre indica, “embeber” la base de datos y todos sus controladores en el directorio de nuestra aplicación.

Cuando descomprimamos el archivo veremos una serie de archivos y carpetas, los que he marcado en un recuadro son los que debemos copiar el directorio raíz de nuestra aplicación.
Después descargamos desde aquí el Firebird .NET Data Provider para poder acceder desde nuestro lenguaje .Net (C# y Vb.Net en nuestro caso), y lo instalamos.

Bien, ya tenemos el controlador para usar Firebird desde .Net y los archivos de Firebird pero… ¿Cómo empezamos a trabajar con ella? Pues instalando el servidor de Firebird para crear el archivo de datos que contendrá los procedimientos, tablas etc. ¿Pero… no hemos dicho que no necesitamos servidor de base de datos?, si así es, la aplicación en producción no lo necesitara, pero nosotros para trabajar con ella sí, así es que descargamos el servidor desde aquí y los instalamos.
Durante la instalación nos dejara elegir el tipo de servidor que queremos, Classic o Superserver, seleccionaremos “Superserver”.
Una vez instalado veremos que nos ha dejado entre otros este archivo: “C:\Archivos de programa\Firebird\Firebird_2_1\bin\isql.exe”, que nos servirá para crear y administrar los objetos de la base de datos en modo de línea comandos, pero que no cunda el pánico, hay herramientas que le proporcionan un entorno visual o IDE para trabajar más cómodamente, en nuestro caso usaremos FlameRobin, que es una herramienta multiplataforma para la administración de la base de datos de Firebird, que al menos, en el momento de escribir estas líneas es de uso libre y podemos descargar desde aquí

Una vez hecho todo, registramos el servidor en el entorno de FlameRobin y creamos nuestra base de datos. En el momento de crear la base de datos nos pedirá que le indiquemos donde guardar el archivo de base de datos, este archivo hay que copiarlo en el raíz de nuestra aplicación junto con los archivos de Firebird embebido que nos hemos descargado antes.

Ahora solo nos queda acceder a nuestra base de datos desde nuestra aplicación.
Agregamos a nuestra aplicación las referencias a las bibliotecas que contienen el proveedor de datos de Firebird.

Ahora nos ponemos manos a la obra con el código.
SQL

C#


VB

Y esto es todo, ya tenemos nuestra base de datos embebida y lista para agregarla al instalador de nuestra aplicación o distribuirla simplemente junto con los ejecutables y bibliotecas de la aplicación.
4 comments Mayo 6, 2008
Precarga en ASP.NET con AJAX (1)
A todos los que desarrollamos en entorno Web con Asp.Net se nos ha presentado en algún momento la necesidad de mostrar una precarga en mientras se realizaba alguna operación. Por ejemplo, mientras se llena un Datagrid. Desde la versión 2.0 de Asp.Net esto es más fácil gracias a AJAX.
Que es AJAX:
AJAX (Asynchronous JavaScript And XML) nos permitirá realizar operaciones de manera asíncrona en nuestras aplicaciones Web. Esto quiere decir, que no tendremos que hacer Postback (Viajes de ida y vuelta al servidor) para realizar algunas operaciones que antes lo requerían.
Empezamos:
En nuestro ejemplo realizaremos una tarea que llevara algo de tiempo y mostraremos una imagen animada mientras esto sucede.
Código:
<%@ Page Language=”C#” AutoEventWireup=”true” %>
<script runat=”server” language=”C#”>
public void Fill()
{
ArrayList vals = new ArrayList(30);
for (int i = 1; i <= 30; i++)
{
vals.Add(“Elemento: “ + i.ToString());
}
Grid1.DataSource = vals;
Grid1.DataBind();
}
protected void Button1_Click(object sender, EventArgs e)
{
Fill();
}
</script>
<!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>Página sin título</title>
<style type=”text/css”>
.loading {
color: #FF9900;
}
</style>
</head>
<body>
<form id=”form1″ runat=”server”>
<div>
</div>
<asp:ScriptManager ID=”ScriptManager1″ runat=”server”>
</asp:ScriptManager>
<asp:UpdatePanel ID=”UpdatePanel1″ runat=”server”>
<ContentTemplate>
<asp:Button ID=”Button1″ runat=”server” Text=”Cargar datos” onclick=”Button1_Click” />
<asp:GridView ID=”Grid1″ runat=”server” Width=”519px”>
</asp:GridView>
</ContentTemplate>
</asp:UpdatePanel>
<asp:UpdateProgress ID=”UpdateProgress1″ runat=”server”
AssociatedUpdatePanelID=”UpdatePanel1″>
<ProgressTemplate>
<span> <img src=”imgs/loader.gif” /></span> <span class=”loading”> Cargando datos… </span>
</ProgressTemplate>
</asp:UpdateProgress>
</form>
</body>
</html>
Como podeis ver, el ejemplo es muy básico. Hemos introducido los controles que se han de ejecutar en un contexto asincrono en el control UpdatePanel, a su vez colocamos un control UpdateProgress que mostrara la imagen mientras dure la operación y lo hemos asociado al control UpdatePanel.
Esto solo a sido la primera parte de una serie de articulos sobre el mismo tema. Según tenga tiempo los ire subiendo.
|
Nota: |
6 comments Mayo 2, 2008