jueves, 26 de marzo de 2015

El Libro del Día: 20 Recipes for Programming PhoneGap

El Libro del Día: 2015-03-26

Titulo: 20 Recipes for Programming PhoneGap
Autor: Jamie Munro
Editorial: O'Reilly
Nro Paginas: 76

Capítulos:
1. Determining Whether the Device Is Ready
2. Retrieving Information About the Device
3. Creating a Persistent Navigation System
4. Detecting the Device’s Network Status
5. Detecting When the Network Status Changes
6. Executing a Callback Function Once the Device Is Ready
7. Detecting When the App Is Moved to the Background or Foreground
8. Using the GPS and Displaying a Position on a Map
9. Using the Compass to Help the User Navigate
10. Using the Accelerometer to Detect Motion
11. Displaying Table-View Data
12. Retrieving Contacts in the Address Book
13. Creating a New Contact in the Address Book
14. Accessing the Camera and Photo Album
15. Saving Data to a Remote Server
16. Capturing Audio and Video
17. Notifying the Device with Alert, Confirm, and Vibrate
18. Storing Data to the Device
19. Reading Data from the Device
20. Extending PhoneGap with Plug-ins

Descarga:
20_Recipes_for_Programming_PhoneGap

miércoles, 25 de marzo de 2015

El Libro del Día: PhoneGap Mobile Application Development Cookbook

El Libro del Día: 2015-03-25

Titulo: PhoneGap Mobile Application Development Cookbook
Autor: Matt Gifford
Editorial: Packt
Nro Paginas: 320

Capítulos:
Chapter 1: Movement and Location: Using the Accelerometer and Geolocation Sensors
Chapter 2: File System, Storage, and Local Databases
Chapter 3: Working with Audio, Images, and Video
Chapter 4: Working with Your Contacts
Chapter 5: Hook into Native Events
Chapter 6: Working with XUI
Chapter 7: User Interface Development with jQuery Mobile
Chapter 8: Extending PhoneGap with Plugins
Chapter 9: Development Tools and Testing

Descarga:
PhoneGap_Mobile_Application_Development_Cookbook

martes, 24 de marzo de 2015

El Demo del Día: Graficar Datos en el Servidor en ASP.NET MVC

Graficar Datos en el Servidor en ASP.NET MVC con llamada asíncrona usando Ajax Form y Vista Parcial

Requerimiento

Se desea crear una aplicación web que permita consultar los productos por categoría y mostrar los datos mas gráficos de diferentes tipos: Barras, Columnas, Lineas y Pie, pero el requisito es que sea conectado o en línea, es decir, el filtro por categoría debe hacerse directo de la base de datos y el gráfico debe crearse dinámicamente en el servidor web.

Solución

- Crear una aplicación en ASP.NET MVC con un método de acción que se conecte a la BD para traer la lista de categorías mas los tipos de gráficos.
- Crear un formulario MVC Ajax que realice una llamada asíncrona cada vez que se selecciona una categoría que llama a una acción que haga la consulta en base de datos y que cree el gráfico dinámicamente devolviendo una vista parcial.
- Para crear los diferentes tipos de gráficos usaremos la librería System.Drawing y el gráfico lo almacenaremos en un MemoryStream, a partir del cual obtendremos su array de bytes y lo almacenaremos en un ViewBag llamado Imagen con el contenido de la imagen en formato base64.

Crear los Procedimientos Almacenados en la Base de Datos Northwind de SQL Server

Para el ejemplo usaremos la Base de Datos Northwind de SQL Server, en la cual crearemos los siguientes Procedimiento almacenado:

Create Procedure [dbo].[uspCategoriesListar]
As
Select CategoryID,CategoryName From Categories
Go

Create Procedure [dbo].[uspProductsListarPorCategoria]
@CategoryId int
As
Select ProductID,ProductName,SupplierID,
CategoryID,UnitPrice,UnitsInStock
From Products
Where CategoryId=@CategoryId

Crear la Librería de Clases de Entidades del Negocio

Crear la librería de clases llamada: "Northwind.Librerias.EntidadesNegocio" y agregar la clase "beCategoria.cs":

namespace Northwind.Librerias.EntidadesNegocio
{
    public class beCategoria
    {
        public int IdCategoria { get; set; }
        public string Nombre { get; set; }
    }
}

Agregar otra clase a la librería llamada "beProducto.cs":

namespace Northwind.Librerias.EntidadesNegocio
{
    public class beProducto
    {
        public int IdProducto { get; set; }
        public string Nombre { get; set; }
        public int IdProveedor { get; set; }
        public int IdCategoria { get; set; }
        public decimal PrecioUnitario { get; set; }
        public short Stock { get; set; }
    }
}

Grabar y compilar la Librería de Entidades del Negocio.

Crear la Librería de Acceso a Datos

Crear la librería de clases llamada: "Northwind.Librerias.AccesoDatos", referenciar a la librería de clases de entidades creada anteriormente y agregar la clase "daCategoria.cs" y escribir el siguiente código:

using System;
using System.Collections.Generic; //List
using System.Data; //CommandType, CommandBehavior
using System.Data.SqlClient; //SqlConnection, SqlCommand, SqlDataReader
using Northwind.Librerias.EntidadesNegocio; //beCategoria

namespace Northwind.Librerias.AccesoDatos
{
    public class daCategoria
    {
        public List<beCategoria> listar(SqlConnection con)
        {
            List<beCategoria> lbeCategoria = null;

            SqlCommand cmd = new SqlCommand("uspCategoriesListar", con);
            cmd.CommandType = CommandType.StoredProcedure;
            SqlDataReader drd = cmd.ExecuteReader();
            if (drd != null)
            {
                lbeCategoria = new List<beCategoria>();
                beCategoria obeCategoria;
                int posIdCat = drd.GetOrdinal("CategoryID");
                int posNomCat = drd.GetOrdinal("CategoryName");
                while (drd.Read())
                {
                    obeCategoria = new beCategoria();
                    obeCategoria.IdCategoria = drd.GetInt32(posIdCat);
                    obeCategoria.Nombre = drd.GetString(posNomCat);
                    lbeCategoria.Add(obeCategoria);
                }
                drd.Close();
            }
            return (lbeCategoria);
        }
    }
}

Agregar una nueva clase a la librería llamada "daProducto.cs" y escribir el siguiente código:

using System;
using System.Collections.Generic; //List
using System.Data; //CommandType, CommandBehavior
using System.Data.SqlClient; //SqlConnection, SqlCommand, SqlDataReader
using Northwind.Librerias.EntidadesNegocio; //beProducto

namespace Northwind.Librerias.AccesoDatos
{
    public class daProducto
    {
        public List<beProducto> listarPorCategoria(SqlConnection con,int idCategoria)
        {
            List<beProducto> lbeProducto = null;

            SqlCommand cmd = new SqlCommand("uspProductsListarPorCategoria", con);
            cmd.CommandType = CommandType.StoredProcedure;

            SqlParameter par = cmd.Parameters.Add("@CategoryId", SqlDbType.Int);
            par.Direction = ParameterDirection.Input;
            par.Value = idCategoria;

            SqlDataReader drd = cmd.ExecuteReader(CommandBehavior.SingleResult);
            if (drd != null)
            {
                lbeProducto = new List<beProducto>();
                int posIdProducto = drd.GetOrdinal("ProductID");
                int posNombre = drd.GetOrdinal("ProductName");
                int posIdProveedor = drd.GetOrdinal("SupplierID");
                int posIdCategoria = drd.GetOrdinal("CategoryID");
                int posPrecioUnitario = drd.GetOrdinal("UnitPrice");
                int posStock = drd.GetOrdinal("UnitsInStock");
                beProducto obeProducto;
                while (drd.Read())
                {
                    obeProducto = new beProducto();
                    obeProducto.IdProducto = drd.GetInt32(posIdProducto);
                    obeProducto.Nombre = drd.GetString(posNombre);
                    obeProducto.IdProveedor = drd.GetInt32(posIdProveedor);
                    obeProducto.IdCategoria = drd.GetInt32(posIdCategoria);
                    obeProducto.PrecioUnitario = drd.GetDecimal(posPrecioUnitario);
                    obeProducto.Stock = drd.GetInt16(posStock);
                    lbeProducto.Add(obeProducto);
                }
                drd.Close();
            }

            return (lbeProducto);
        }
    }
}

Grabar y compilar la librería de acceso a datos creada.

Crear la Librería de Reglas del Negocio

Crear la librería de clases llamada: "Northwind.Librerias.ReglasNegocio", referenciar a la librería de clases de entidades y también a la de acceso a datos, luego agregar la clase "brCategoria.cs" y escribir el siguiente código:

using System;
using System.Data.SqlClient; //SqlConnection
using System.Collections.Generic; //List
using Northwind.Librerias.EntidadesNegocio; //beCategoria
using Northwind.Librerias.AccesoDatos; //daCategoria

namespace Northwind.Librerias.ReglasNegocio
{
    public class brCategoria
    {
        public List<beCategoria> listar()
        {
            List<beCategoria> lbeCategoria = null;
            string CadenaConexion = ConfigurationManager.ConnectionStrings["conNW"]
                                                     .ConnectionString;
            using (SqlConnection con = new SqlConnection(CadenaConexion))
            {
                try
                {
                    con.Open();
                    daCategoria odaCategoria = new daCategoria();
                    lbeCategoria = odaCategoria.listar(con);
                }
                catch (SqlException ex)
                {
                    //grabarLog(ex);
                }
                catch (Exception ex)
                {
                    //grabarLog(ex);
                }
            } //con.Close(); con.Dispose();
            return (lbeCategoria);
        }
    }
}

Agregar una nueva clase a la librería llamada "brProducto.cs" y escribir el siguiente código:

using System;
using System.Data.SqlClient; //SqlConnection
using System.Collections.Generic; //List
using Northwind.Librerias.EntidadesNegocio; //beProducto
using Northwind.Librerias.AccesoDatos; //daProducto

namespace Northwind.Librerias.ReglasNegocio
{
    public class brProducto
    {
        public List<beProducto> listarPorCategoria(int idCategoria)
        {
            List<beProducto> lbeProducto = null;
            string CadenaConexion = ConfigurationManager.ConnectionStrings["conNW"]
                                                     .ConnectionString;
            using (SqlConnection con = new SqlConnection(CadenaConexion))
            {
                try
                {
                    con.Open();
                    daProducto odaProducto = new daProducto();
                    lbeProducto = odaProducto.listarPorCategoria(con,idCategoria);
                }
                catch (SqlException ex)
                {
                    //grabarLog(ex);
                }
                catch (Exception ex)
                {
                    //grabarLog(ex);
                }
            } //con.Close(); con.Dispose();
            return (lbeProducto);
        }
    }
}

Nota: También es necesario hacer una referencia a la librería "System.Configuration" para leer la cadena de conexión definida en la aplicación.

Grabar la librería de reglas de negocio creada y compilarla.

Crear una Aplicación Web de ASP.NET MVC4 en C#

Seleccionar un nuevo proyecto de tipo: "Aplicación web de ASP.NET MVC4" y escribir como nombre "Demo10", luego seleccionar la opción "Vacio" y como motor de vista "Razor" y "Aceptar".

Nota: Antes de iniciar con el código hay que referenciar a las librerías creadas, sobre todo a la Regla de Negocio y las Entidades de Negocio.

Crear un Controlador para el Producto

Clic derecho a la carpeta Controllers y seleccionar "Agregar" y luego "Controlador", llamarle al archivo "ProductoController.cs" y en opciones de plantillas dejarlo en plantilla vacía (Vaciar Controlador MVC). Luego escribir el siguiente código:

using System;
using System.IO;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Imaging;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using Northwind.Librerias.EntidadesNegocio;
using Northwind.Librerias.ReglasNegocio;

namespace Demo10.Controllers
{
    public class ProductoController : Controller
    {        
        public ActionResult Lista()
        {
            brCategoria obrCategoria = new brCategoria();
            List<beCategoria> lbeCategoria = obrCategoria.listar();
            ViewBag.Categorias = lbeCategoria;
            List<string> tiposGrafico = new List<string>();
            tiposGrafico.Add("Barras");
            tiposGrafico.Add("Columnas");
            tiposGrafico.Add("Lineas");
            tiposGrafico.Add("Pie");
            ViewBag.TiposGrafico = tiposGrafico;
            return View();
        }

        public PartialViewResult ConsultaGrafico(int idCategoria,string tipoGrafico)
        {
            brProducto obrProducto = new brProducto();
            List<beProducto> lbeProducto = obrProducto.listarPorCategoria(idCategoria);
            int ancho = 800;
            int alto = 500;
            Bitmap bmp = new Bitmap(ancho, alto);
            Graphics grafico = Graphics.FromImage(bmp);
            Rectangle rect = new Rectangle(0, 0, ancho, alto);
            LinearGradientBrush degradado = new LinearGradientBrush
            (rect, Color.Aqua, Color.Blue, LinearGradientMode.BackwardDiagonal);
            grafico.FillRectangle(degradado, rect);
            int x, y;
            Font fuente=new Font("Arial",10);
            Pen lapiz=new Pen(Brushes.Yellow,5);
            StringFormat formato = new StringFormat(StringFormatFlags.DirectionVertical);
            switch (tipoGrafico)
            {
                case "Barras":
                    x = 20;
                    y=20;
                    foreach(beProducto obeProducto in lbeProducto)
                    {
                        x = 20;
                        grafico.DrawString(obeProducto.Nombre, fuente, 
                        Brushes.White, x, y);
                        x = 200;
                        rect = new Rectangle(x, y, obeProducto.Stock, 20);
                        grafico.FillRectangle(Brushes.Yellow, rect);
                        x = 220 + obeProducto.Stock;
                        grafico.DrawString(obeProducto.Stock.ToString(), fuente, 
                        Brushes.White, x, y);
                        y += (int)fuente.GetHeight() + 20;
                    }
                    break;
                case "Columnas":
                    x = 20;
                    y=350;
                    foreach(beProducto obeProducto in lbeProducto)
                    {
                        grafico.DrawString(obeProducto.Nombre, fuente, 
                        Brushes.White, x, y, formato);
                        alto = 340 - obeProducto.Stock;
                        rect = new Rectangle(x, alto, 20, obeProducto.Stock);
                        grafico.FillRectangle(Brushes.Yellow, rect);
                        grafico.DrawString(obeProducto.Stock.ToString(), fuente, 
                        Brushes.White, x, alto - 20);
                        x += 50;
                    }
                    break;
                case "Lineas":
                    x = 20;
                    y=350;
                    for(int i=0;i<lbeProducto.Count;i++)
                    {
                        grafico.DrawString(lbeProducto[i].Nombre, fuente, 
                        Brushes.White, x, y, formato);
                        if (i < lbeProducto.Count - 1)
                        {
                            grafico.DrawLine(lapiz, new Point(x, 340 - lbeProducto[i].Stock), 
                            new Point(x + 50, 340 - lbeProducto[i + 1].Stock));
                        }
                        grafico.DrawString(lbeProducto[i].Stock.ToString(), fuente, 
                        Brushes.White, x, 320 - lbeProducto[i].Stock);
                        x += 50;
                    }
                    break;
                case "Pie":
                    decimal total = lbeProducto.Sum(obj => obj.Stock);
                    float anguloInicio = 00.0F;
                    float anguloFin;
                    rect = new Rectangle(100, 10, ancho-200, alto - 20);
                    int R, G, B;
                    Color color;
                    Random oAzar = new Random();
                    for (int i = 0; i < lbeProducto.Count; i++)
                    {
                        R = oAzar.Next(255);
                        G = oAzar.Next(255);
                        B = oAzar.Next(255);
                        color = Color.FromArgb(R, G, B);
                        anguloFin = (float)((lbeProducto[i].Stock * 360) / total);
                        grafico.FillPie(new SolidBrush(color), rect, anguloInicio, anguloFin);
                        anguloInicio += anguloFin;
                    }
                    break;
            }
            byte[] imagen=null;
            using (MemoryStream ms = new MemoryStream())
            {
                bmp.Save(ms, ImageFormat.Png);
                imagen = ms.ToArray();
            }
            ViewBag.Imagen = String.Format("data:image/png;base64,{0}",
            Convert.ToBase64String(imagen));
            return PartialView("Grafico", lbeProducto);
        }
    }
}

Nota: Para el gráfico de Pie se esta creando un color al azar para cada producto.

Crear una Hoja de Estilos para la Vista

Primero crear una carpeta llamada "Content", luego clic derecho "Agregar" y luego "Hoja de estilos" y como nombre llamarle "ACME.css" y escribir el siguiente código:

body {
    background-color:gray;
}
.Titulo {
    background-color:black;
    color:white;
    font-size:larger;
}
.Subtitulo {
    background-color:white;
    color:black;
    font-size:large;
}
.AnchoTotal {
    width:100%;
}
.Centrado {
    text-align:center;
}
.FilaCabecera {
    background-color:lightgray;
    color:black;
}
.FilaDatos {
    background-color:white;
    color:black;
}

Crear la Vista Productos para mostrar los datos

Ir al controlador y ubicarse sobre el método "Lista" (acción), clic derecho y seleccionar "Agregar vista", desmarcar todos los check ya que es una vista sin tipo y escribir el siguiente código:

@{
    Layout = null;
}
<!DOCTYPE html>
<html>
<head>
    <meta name="viewport" content="width=device-width" />
    <title>Lista</title>
    <link href="~/Content/ACME.css" rel="stylesheet" />
</head>
<body>
    <div>
        @using (Ajax.BeginForm("ConsultaGrafico", "Producto", 
         new AjaxOptions { HttpMethod = "get", InsertionMode = InsertionMode.Replace, 
         UpdateTargetId = "divGrafico" }, new { id = "frmGrafico" }))
        {
        <table class="AnchoTotal">
            <tr class="Titulo">
                <td colspan="2">Gráficar Datos en el Servidor en ASP.NET MVC 
                 con llamada asíncrona usando Ajax Form y Vista Parcial</td>
            </tr>
            <tr class="Subtitulo">
                <td colspan="2">Consulta de Productos por Categoría</td>
            </tr>
            <tr>
                <td style="width:40%">Seleccione la Categoría</td>
                <td style="width:60%">Seleccione el Tipo de Gráfico</td>
            </tr>
            <tr>
                <td>
                    @Html.DropDownList("idCategoria",
                     new SelectList(ViewBag.Categorias,"IdCategoria","Nombre"))
                </td>
                <td>
                    @Html.DropDownList("tipoGrafico",
                     new SelectList(ViewBag.TiposGrafico))
                </td>
            </tr>
            <tr>
                <td colspan="2">
                    <div id="divGrafico"></div>
                </td>
            </tr>
        </table>
        }
    </div>
    <script src="~/Scripts/jquery-1.8.2.min.js"></script>
    <script src="~/Scripts/jquery.unobtrusive-ajax.min.js"></script>
    <script src="~/Scripts/Rutinas.js"></script>
    <script>iniciarLista();</script>
</body>
</html>

Nota: Es mejor colocar los archivos de JavaScript al final de la página para renderizar mas rápido.

Crear la Vista Parcial con la Consulta y el Gráfico a Mostrar

Ir nuevamente al controlador y ubicarse sobre el método "Lista" (acción), clic derecho y seleccionar "Agregar vista", cambiar de nombre a la vista por "Grafico", seleccionar el check "Crear como vista parcial" y escribir el siguiente código:

@using Northwind.Librerias.EntidadesNegocio
@model List<beProducto>
<table class="AnchoTotal">
    <tr>
        <td style="vertical-align:top">
            <table class="AnchoTotal">
                <tr class="FilaCabecera">
                    <td style="width:200px">Nombre del Producto</td>
                    <td style="width:10px">Stock</td>
                </tr>
                @foreach (beProducto obeProducto in Model)
                {
                    <tr class="FilaDatos">
                        <td>@obeProducto.Nombre</td>
                        <td>@obeProducto.Stock</td>
                    </tr>
                }
            </table>
        </td>
        <td>
            <img src="@ViewBag.Imagen" width="800" height="500" />
        </td>
    </tr>
</table>

Crear el archivo JavaScript con el código cliente

Antes que nada crear una carpeta llamada "Scripts" y arrastrar del explorador de Windows el archivo de jQuery: "jquery-1.8.2.min.js" y "jquery.unobtrusive-ajax.min.js" (necesario para el Ajax Form), luego agregar un archivo de JavaScript llamado "Rutinas.js" y escribir el siguiente código:

function iniciarLista() {
    $("#frmGrafico").submit();
    var cboIdCategoria = document.getElementById("idCategoria");
    cboIdCategoria.onchange = function () { llamadaAsincrona(); };
    var cboTipoGrafico = document.getElementById("tipoGrafico");
    cboTipoGrafico.onchange = function () { llamadaAsincrona(); };
    function llamadaAsincrona() {
        $("#frmGrafico").submit();
    }
}

Nota: Cuando se carga la página se esta haciendo un submit al formulario "frmGrafico" via jQuery el cual es asíncrono por que el formulario en un Ajax Form. También ocurre lo mismo al seleccionar una categoría del combo.

Modificar el archivo web.config para incluir la cadena de conexión

<configuration>
  <connectionStrings>
    <add name="conNW" providerName="SQLServer"
         connectionString="uid=UsuarioNW;pwd=123456;
         data source=DSOFT\Sqlexpress;database=Northwind"/>
  </connectionStrings>
  .....
</configuration>

Configurar el inicio y ejecutar la aplicación web

Para probar la aplicación web debemos configurar el inicio para lo cual nos vamos a la carpeta "App_Start" y abrimos el archivo "RouteConfig.cs" cambiando el nombre del controlador y la acción tal como sigue: controller = "Producto", action = "Lista".

Ejecución y Pruebas de la Aplicación Web ASP.NET MVC

Finalmente, grabar y pulsar F5 para ejecutar la aplicación, mostrándose el resultado similar a la siguiente figura:


Por defecto se muestra la categoría "Bebidas" y en el tipo de gráfico "Barras", cambiar el tipo de gráfico a "Columnas" y se verá la siguiente figura:


Probar ahora con el gráfico de "Lineas" y se mostrará algo similar a la siguiente figura:


Finalmente, cambiar el tipo de gráfico a "Pie" y se verá lo siguiente:


Nota: En el gráfico de Pie falta agregar una leyenda para distinguir cada producto de acuerdo al color generado. Esto queda como tarea para que aprendan mas sobre Drawing.

Comentario Final

En este post hemos visto como trabajar en forma conectada, y también hemos aprendido a crear gráficos dinámicamente usando la librería System.Drawing y las clases: Bitmap, Graphics, Rectangle, Font, Pen, StringFormat, Color, SolidBrush, las enumeraciones: Brushes y StringFormatFlags, también el Namespace: System.Drawing.Drawing2D para usar la clase: LinearGradientBrush (degradado).

En un anterior post vimos como crear gráficos en el Cliente usando HTML5 Canvas con JSON:

Aunque en apariencia los Demos son similares el anterior es desconectado de la base de datos y del servidor web, este demo es conectado o en línea para ver los últimos datos, por ejemplo muy útil para reportes gerenciales en línea.

Descarga del código

Video del Demo

Nota: El video es muy similar al código presentado en el Demo, pero la versión original era desconectada del servidor de datos y usaba una sesión, en cambio el demo es totalmente conectado, la diferencia solo es en el controller y es mínima.

El Libro del Día: Beginning PhoneGap

El Libro del Día: 2015-03-24

Titulo: Beginning PhoneGap
Autor: Thomas Myer
Editorial: Wrox
Nro Paginas: 388

Capítulos:
CHAPTER 1 Introducing PhoneGap
CHAPTER 2 Installing and Configuring PhoneGap
CHAPTER 3 Basic Walkthrough
CHAPTER 4 Events
CHAPTER 5 Working with the Device, the Network, and Notifications
CHAPTER 6 Accelerometer
CHAPTER 7 Compass
CHAPTER 8 Geolocation
CHAPTER 9 Media
CHAPTER 10 Camera
CHAPTER 11 Storage
CHAPTER 12 Files
CHAPTER 13 Contacts
CHAPTER 14 Capture
CHAPTER 15 Creating a Note-Taking Application
APPENDIX A Answers to Exercises
APPENDIX B Tools for PhoneGap
APPENDIX C PhoneGap.js
APPENDIX D PhoneGap Plug-ins

Descarga:
Beginning_PhoneGap

lunes, 23 de marzo de 2015

El Demo del Día: Paginación y Ordenación Asíncrona en ASP.NET MVC con Ajax ActionLink y Vista Parcial

Paginación y Ordenación Asíncrona en ASP.NET MVC con Ajax ActionLink y Vista Parcial

Luego de casi un mes de no publicar un Demo, esta semana vamos a publicar uno diario para nivelarnos. En esta ocasión veremos como paginar y ordenar en forma asíncrona en ASP.NET MVC usando el control Helper Ajax.ActionLink y una Vista Parcial.

Requerimiento

Se desea crear una aplicación web en ASP.NET MVC que permita consultar la lista de empleados, dando la posibilidad de mostrar por páginas y ordenar los registros en forma ascendente y descendente por cada columna mostrada.
El requerimiento debe acompañarse de una buena experiencia de usuario que permita actualizar los datos al paginar u ordenar sin refrescar toda la pantalla.

Solución

Usaremos los controles Ajax.ActionLink tanto para la ordenación como la paginación, los cuales realizarán una llamada asíncrona (internamente usan jQuery Ajax) y actualizarán la vista mediante una vista parcial con los empleados.

Crear el Procedimiento Almacenado en la Base de Datos Northwind de SQL Server

Para listar los datos de los empleados crear el procedimiento "uspEmployeesListar":

Create procedure [dbo].[uspEmployeesListar]
As
Select EmployeeId,LastName,FirstName,IsNull(BirthDate,'1/1/1900') as Birthdate
From Employees

Crear una Librería de Entidades de Negocio para Northwind

Crear una librería de clases "Northwind.Librerias.EntidadesNegocio" con una clase llamada "beEmpleado" que tenga el siguiente código:

using System;
namespace Northwind.Librerias.EntidadesNegocio
{
    public class beEmpleado
    {
        public int IdEmpleado { get; set; }
        public string Apellido { get; set; }
        public string Nombre { get; set; }
        public DateTime FechaNacimiento { get; set; }
        public byte[] Foto { get; set; }
    }
}

Crear una Librería de Acceso a Datos para Northwind

Crear una librería de clases "Northwind.Librerias.AccesoDatos" con una clase llamada "daEmpleado" que tenga el siguiente código:

namespace Northwind.Librerias.AccesoDatos
{
    public class daEmpleado
    {
        public List<beEmpleado> Listar(SqlConnection con)
        {
            List<beEmpleado> lbeEmpleado = null;
            SqlCommand cmd = new SqlCommand("uspEmployeesListar", con);
            cmd.CommandType = CommandType.StoredProcedure;

            SqlDataReader drd = cmd.ExecuteReader(CommandBehavior.SingleResult);
           
            if (drd != null)
            {
                lbeEmpleado = new List<beEmpleado>();
                beEmpleado obeEmpleado;
                int posIdEmpleado = drd.GetOrdinal("EmployeeID");
                int posApellido = drd.GetOrdinal("LastName");
                int posNombre = drd.GetOrdinal("FirstName");
                int posFechaNacimiento = drd.GetOrdinal("BirthDate");
                while (drd.Read())
                {
                    obeEmpleado = new beEmpleado();
                    obeEmpleado.IdEmpleado = drd.GetInt32(posIdEmpleado);
                    obeEmpleado.Apellido = drd.GetString(posApellido);
                    obeEmpleado.Nombre = drd.GetString(posNombre);
                    obeEmpleado.FechaNacimiento = drd.GetDateTime(posFechaNacimiento);
                    lbeEmpleado.Add(obeEmpleado);
                }
                drd.Close();
            }
            return (lbeEmpleado);
        }
    }
}

Nota: Hay que referenciar a la librería de entidades de negocio anteriormente creada.

Crear una Librería de Reglas de Negocio para Northwind

Crear una librería de clases "Northwind.Librerias.AccesoDatos" con una clase llamada "daEmpleado" que tenga el siguiente código:

using System;
using System.Data;
using System.Data.SqlClient;
using System.Collections.Generic;
using System.Configuration;
using Northwind.Librerias.EntidadesNegocio;
using Northwind.Librerias.AccesoDatos;

namespace Northwind.Librerias.ReglasNegocio
{
    public class brEmpleado
    {
        public List<beEmpleado> Listar()
        {
            List<beEmpleado> lbeEmpleado = null;
            string CadenaConexion = ConfigurationManager.
            ConnectionStrings["conNW"].ConnectionString;
            using (SqlConnection con = new SqlConnection(CadenaConexion))
            {
                try
                {
                    con.Open();
                    daEmpleado odaEmpleado = new daEmpleado();
                    lbeEmpleado = odaEmpleado.Listar(con);
                }
                catch (SqlException ex)
                {
                    foreach (SqlError err in ex.Errors)
                    {
                        //GrabarLog
                    }
                }
                catch (Exception ex)
                {
                    //GrabarLog
                }
            } //con.close(); con.Dispose();
            return (lbeEmpleado);
        }
    }
}

Nota: Hay que referenciar a las librería "System.Configuration", la librería de entidades de negocio y de acceso a datos.

Crear una Aplicación Web de ASP.NET MVC4 en C#

Abrir el Visual Studio 2012 y seleccionar un nuevo proyecto de tipo: "Aplicación web de ASP.NET MVC4".

Escribir como nombre "Demo09" y "Aceptar", en el siguiente diálogo seleccionar la opción "Vacio" y como motor de vista "Razor" y "Aceptar".

Antes de empezar a programar debemos referenciar las librerías anteriormente creadas, es decir, la librería de entidades del negocio, de acceso a datos y de reglas del negocio de Northwind.

Crear un Controlador para el Empleado

Clic derecho a la carpeta "Controllers" y seleccionar "Agregar" y luego "Controlador", llamarle al archivo "EmpleadoController.cs" y en opciones de plantillas dejarlo en plantilla vacia (Vaciar Controlador MVC). Luego escribir el siguiente código:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using Northwind.Librerias.EntidadesNegocio;
using Northwind.Librerias.ReglasNegocio;

namespace Demo09.Controllers
{
    public class EmpleadoController : Controller
    {
        List<beEmpleado> lbePagina;
        List<beEmpleado> lbeEmpleado;
        int indicePaginaActual;
        int indiceUltimaPagina;
        int registrosPorPagina = 10;

        private void mostrarPagina()
        {            
            lbePagina = new List<beEmpleado>();
            int inicio = indicePaginaActual * registrosPorPagina;
            int fin = inicio + registrosPorPagina;
            for (int i = inicio; i < fin; i++)
            {
                if (i==lbeEmpleado.Count) break;
                lbePagina.Add(lbeEmpleado[i]);
            }
        }

        public ActionResult Lista()
        {
            brEmpleado obrEmpleado = new brEmpleado();
            lbeEmpleado = obrEmpleado.listar();
            Session["Empleados"] = lbeEmpleado;
            indiceUltimaPagina = (lbeEmpleado.Count / registrosPorPagina);
            if (lbeEmpleado.Count % registrosPorPagina == 0) indiceUltimaPagina--;
            ViewBag.IndiceUltimaPagina = indiceUltimaPagina;
            mostrarPagina();
            return View(lbePagina);
        }

        public PartialViewResult Ordenar(string campo)
        {
            ViewBag.Campo = campo;
            lbeEmpleado = (List<beEmpleado>)Session["Empleados"];
            int n = 0;
            string simbolo = "▲";
            if (TempData[campo] != null)
            {
                if (TempData[campo].Equals(0))
                {
                    n = 1;
                    simbolo = "▼";
                }
                else simbolo = "▲";
            }
            TempData[campo] = n;
            ViewBag.Simbolo = simbolo;
            if (n == 0) lbeEmpleado = lbeEmpleado.OrderBy
            (x => x.GetType().GetProperty(campo).GetValue(x, null)).ToList();
            else lbeEmpleado = lbeEmpleado.OrderByDescending
            (x => x.GetType().GetProperty(campo).GetValue(x, null)).ToList();
            Session["Empleados"] = lbeEmpleado;
            if (TempData["indicePaginaActual"] != null)
            {
                indicePaginaActual = (int)TempData["indicePaginaActual"];
                TempData["indicePaginaActual"] = indicePaginaActual;
            }
            mostrarPagina();
            return PartialView("Tabla", lbePagina);
        }

        public PartialViewResult Paginar(int pagina)
        {
            lbeEmpleado = (List<beEmpleado>)Session["Empleados"];
            indicePaginaActual = pagina;
            TempData["indicePaginaActual"] = indicePaginaActual;
            mostrarPagina();
            return PartialView("Tabla", lbePagina);
        }
    }
}

Nota: Inicialmente se llamará al método de acción "Lista" y luego los Ajax.ActionLink de ordenar llamarán a la acción "Ordenar" que devolverá una vista parcial llamada "Tabla" con los datos ordenados ascendente o descendente de acuerdo al valor del TempData de cada campo.
Si se da clic a los Ajax.ActionLink de paginación se llamará a la acción "Paginar" que configura el "indicePaginaActual" y muestra la vista parcial "Tabla" paginada.

Crear una Hoja de Estilos para la Vista Principal y la Vista Parcial

Primero crear una carpeta llamada "Content", luego clic derecho "Agregar" y luego "Hoja de estilos" y como nombre llamarle "ACME.css" y escribir el siguiente código:

body {
    background-color:gray;
}
.Titulo {
    background-color:black;
    color:white;
    font-size:larger;
}
.Subtitulo {
    background-color:white;
    color:black;
    font-size:large;
}
.AnchoTotal {
    width:100%;
}
.Centrado {
    text-align:center;
}
.FilaCabecera {
    background-color:lightgray;
    color:black;
}
.FilaDatos {
    background-color:white;
    color:black;
}

Crear el archivo JavaScript con el código cliente para la Vista Principal

Antes que nada crear una carpeta llamada "Scripts" y arrastrar del explorador de Windows los archivos de jQuery: "jquery-1.8.2.min.js" y "jquery.unobtrusive-ajax.min.js", luego agregar un archivo de JavaScript llamado "Rutinas.js" y escribir el siguiente código:

function mostrarSimbolo() {
    var campo = document.getElementById("hdfCampo").value;
    var simbolo = document.getElementById("hdfSimbolo").value;
    var span = document.getElementById("spn" + campo);
    span.innerHTML = simbolo;
}

Nota: Los dos campos ocultos serán creados en la vista parcial y se usan para actualizar el símbolo de ordenación en la vista principal.

Crear la Vista Principal

Ir al controlador "Empleado" y ubicarse sobre el método "Lista" (acción), clic derecho y seleccionar "Agregar vista", desmarcar todas las casillas (checkboxs) y agregar la vista llamada "Lista", luego escribir el siguiente código:

@using Northwind.Librerias.EntidadesNegocio
@model List<beEmpleado>
@{
    Layout = null;
    AjaxOptions configura = new AjaxOptions{ HttpMethod="get", InsertionMode=InsertionMode.Replace, UpdateTargetId="tbEmpleado", OnComplete="mostrarSimbolo"};
}
<!DOCTYPE html>
<html>
<head>
    <meta name="viewport" content="width=device-width" />
    <title>Lista</title>
    <link href="~/Content/ACME.css" rel="stylesheet" />
</head>
<body>
    <div>
        <table class="AnchoTotal">
            <tr class="Titulo">
                <td>Demo 09: Grilla Paginada y Ordenada Asíncrona con 
                       Ajax ActionLink y Vista Parcial</td>
            </tr>
            <tr class="Subtitulo">
                <td>Consulta de Empleados</td>
            </tr>
            <tr>
                <td>
                    <table class="AnchoTotal">
                        <thead>
                            <tr class="FilaCabecera">
                                <td style="width:70px">
                                    @Ajax.ActionLink("Código","Ordenar",
                                     new {campo="IdEmpleado"},configura)
                                    @Html.Raw(" ")<span id="spnIdEmpleado"></span>
                                </td>
                                <td style="width:200px">
                                    @Ajax.ActionLink("Apellido","Ordenar",
                                     new {campo="Apellido"},configura)
                                    @Html.Raw(" ")<span id="spnApellido"></span>
                                </td>
                                <td style="width:200px">
                                    @Ajax.ActionLink("Nombre","Ordenar",
                                     new {campo="Nombre"},configura)
                                    @Html.Raw(" ")<span id="spnNombre"></span>
                                </td>
                                <td style="width:100px">
                                    @Ajax.ActionLink("Fecha Nac","Ordenar",
                                     new {campo="FechaNacimiento"},configura)
                                    @Html.Raw(" ")<span id="spnFechaNacimiento"></span>
                                </td>
                            </tr>
                        </thead>
                        <tbody id="tbEmpleado">
                            @Html.Partial("Tabla",Model)
                        </tbody>
                        <tfoot>
                            <tr class="FilaCabecera">
                                <td colspan="4" class="Centrado">
                                    @for (int i = 0; i <= ViewBag.IndiceUltimaPagina; i++)
                                    {
                                        @Ajax.ActionLink((i + 1).ToString(), "Paginar", 
                                         new { pagina=i}, configura)                                        
                                        @Html.Raw("  ")
                                    }
                                </td>
                            </tr>
                        </tfoot>
                    </table>
                </td>
            </tr>
        </table>
    </div>
    <script src="~/Scripts/jquery-1.8.2.min.js"></script>
    <script src="~/Scripts/jquery.unobtrusive-ajax.min.js"></script>
    <script src="~/Scripts/Rutinas.js"></script>
</body>
</html>

Crear la Vista Parcial

Ir al controlador "Empleado" y ubicarse sobre el método "Lista" (acción), clic derecho y seleccionar "Agregar vista", seleccionar la casilla (check) "Crear como vista parcial" y agregar la vista llamada "Tabla", luego escribir el siguiente código:

@using Northwind.Librerias.EntidadesNegocio
@model List<beEmpleado>
@foreach (beEmpleado obeEmpleado in Model)
{
    <tr class="FilaDatos">
        <td>@obeEmpleado.IdEmpleado</td>
        <td>@obeEmpleado.Apellido</td>
        <td>@obeEmpleado.Nombre</td>
        <td>@obeEmpleado.FechaNacimiento.ToShortDateString()</td>
    </tr>
}
<input id="hdfCampo" type="hidden" value="@ViewBag.Campo" />
<input id="hdfSimbolo" type="hidden" value="@ViewBag.Simbolo" />

Nota: Es necesario que los dos campos ocultos se encuentren dentro de la vista parcial para que se actualicen con el nombre del campo y el símbolo que se va a mostrar al hacer la ordenación asíncrona.

Modificar el archivo Web.Config para incluir la cadena de conexión

Ir al archivo Web.config ubicado en la raíz del proyecto e incluir lo siguiente:

<connectionStrings>
    <add name="conNW" providerName="SQLServer" connectionString=
"uid=UsuarioNW;pwd=123456;server=DSOFT\Sqlexpress;database=Northwind"/>
</connectionStrings>

Nota: Cambiar el nombre del servidor, el usuario y la clave de acuerdo a su configuración.

Configurar la Acción de Inicio

Para probar la aplicación web debemos configurar el inicio para lo cual nos vamos a la carpeta "App_Start" y abrimos el archivo "RouteConfig.cs" cambiando el nombre del controlador y la acción tal como se muestra en el siguiente código:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using System.Web.Routing;

namespace Demo09
{
    public class RouteConfig
    {
        public static void RegisterRoutes(RouteCollection routes)
        {
            routes.IgnoreRoute("{resource}.axd/{*pathInfo}");

            routes.MapRoute(
                name: "Default",
                url: "{controller}/{action}/{id}",
                defaults: new { controller = "Empleado", action = "Lista", id = UrlParameter.Optional }
            );
        }
    }
}

Ejecutar y Probar la Aplicación Web

Finalmente, grabar y pulsar F5 para ejecutar la aplicación, mostrándose el resultado similar a la siguiente figura:


Probar la ordenación ascendente y descendente por cada columna y se mostrará similar a la siguiente figura:


Finalmente, navegar por cada página y se verá los datos similar a la siguiente figura:


Comentario Final

En este post, hemos aprendido como paginar y ordenar en forma asíncrona usando los controles Helpers Ajax.ActionLink de ASP.NET MVC para mostrar los datos de los empleados.

Como siempre en este Blog no hemos necesitado de ningún control de terceros para realizar estas operaciones, sino que lo hemos hecho con los controles y librerías que vienen con el Framework de ASP.NET MVC.

Descarga del código

Video del Demo


Mañana publicaré otro Demo interesante de como hacer lo mismo pero totalmente desconectado, es decir sin ir al servidor web ni al servidor de datos.

Si les gusto compartanlo y denle like o +.

Videos - Curso de ASP.NET MVC Domingos (Parte 3)

Videos del Curso de ASP.NET MVC Domingos (Parte 3)

Comparto la tercera parte del Curso de ASP.NET MVC realizado los Domingos.

Curso de MVC Domingos - Demo 11 - Consulta Asíncrona en el Servidor con jQuery Ajax y JSON

En este video se muestra como realizar una consulta asíncrona de productos por categoría usando jQuery Ajax y devolviendo una lista de objetos como JSON para escribir el HTML en el cliente por Javascript.


Curso de MVC Domingos - Demo 12 - Consulta Asíncrona en el Servidor con XmlHttpRequest y Strings

En este video aprenderemos como realizar una consulta asíncrona de productos por categoría usando XmlHttpRequest (lo que usa internamente jQuery y Ajax) y devolviendo una cadena (String) para reducir el ancho de banda ya que es la octava parte de una vista parcial y la tercera parte de un JSON.


Curso de MVC Domingos - Demo 13 - Consulta Desconectada en el Cliente con jQuery Ajax y JSON

Este video muestra como realizar una consulta de productos por categoría totalmente desconectada del servidor web y de base de datos, recibiendo la primera vez todos los datos como JSON usando jQuery Ajax y filtrándolos en el cliente por JavaScript.


Curso de MVC Domingos - Demo 14 - Ordenación Desconectada en el Cliente con jQuery Ajax y JSON

En este video se muestra como ordenar los productos en el lado del cliente, totalmente desconectado del servidor web y de datos, para lo cual inicialmente hacemos una llamada asíncrona con jQuery Ajax y devolvemos un JSON con los productos los cuales se ordenan usando una función genérica de JavaScript.



El Libro del Día: Xamarin Cross-platform Application Development

El Libro del Día: 2015-03-23

Titulo: Xamarin Cross-platform Application Development
Autor: Jonathan Peppers
Editorial: Packt
Nro Paginas: 462

Capítulos:
1. Setting Up Xamarin
2. Hello, Platforms!
3. Code Sharing between iOS and Android
4. XamChat a Cross-platform App
5. XamChat for iOS
6. XamChat for Android
7. Deploying and Testing on Devices
8. Web Services with Push Notifications
9. Third-party Libraries
10. Contacts, Camera, and Location
11. Xamarin.Forms
12. App Store Submission

Descarga:
Xamarin_Cross_Platform_Application_Development

viernes, 20 de marzo de 2015

El Libro del Día: Xamarin Mobile Application Development for Android

El Libro del Día: 2015-03-20

Titulo: Xamarin Mobile Application Development for Android
Autor: Mark Reynolds
Editorial: Packt
Nro Paginas: 168

Capítulos:
Chapter 1: The Anatomy of an Android App
Chapter 2: Xamarin.Android Architecture
Chapter 3: Creating the Points of Interest App
Chapter 4: Creating a Data Storage Mechanism
Chapter 5: Adding a List View
Chapter 6: Adding a Detail View
Chapter 7: Making POIApp Location Aware
Chapter 8: Adding Camera App Integration
Chapter 9: Deploying Your App

Descarga:
Xamarin_Mobile_Application_Development_for_Android

jueves, 19 de marzo de 2015

Videos - Curso de ASP.NET MVC CIPSA (Parte 4)

Videos del Curso de ASP.NET MVC CIPSA (Parte 4)

Cuarta parte del curso dictado en CIPSA de Desarrollo de Aplicaciones Web con ASP.NET MVC. Esta vez los demos son sobre Gráficos Asíncronos en el Servidor con Vista Parcial y el Carrito de Compras en MVC.

Curso de MVC CIPSA - Demo 16 (Parte 1) - Gráfico Asíncrono en el Servidor con Vista Parcial

Este video es la primera parte de una aplicación que permite consultar los productos por categorías y mostrar gráficos de datos de diferentes tipos: Barras, Columnas, Lineas y Pie.
La consulta se realiza en forma asíncrona usando un Formulario Ajax y una vista parcial para mostrar solo la tabla y el gráfico sin actualizar toda la pantalla.


Curso de MVC CIPSA - Demo 16 (Parte 2) - Gráfico Asíncrono en el Servidor con Vista Parcial

Este video es la segunda parte de una aplicación que permite consultar los productos por categorías y mostrar gráficos de datos de diferentes tipos: Barras, Columnas, Lineas y Pie.
La consulta se realiza en forma asíncrona usando un Formulario Ajax y una vista parcial para mostrar solo la tabla y el gráfico sin actualizar toda la pantalla.


Curso de MVC CIPSA - Demo 17 (Parte 1) - Carrito de Compras

Este video es la primera parte de una aplicación que permite crear una orden o pedido de un conjunto de productos por Internet.
Incluye una pagina de login con Captcha y cifrado de clave mediante MD5 usando CrytpoJS, una pagina de selección de Categorías, una pagina de Productos por Categoría y finalmente la pagina del Pedido del Cliente.


Curso de MVC CIPSA - Demo 17 (Parte 2) - Carrito de Compras

Este video es la segunda parte de una aplicación que permite crear una orden o pedido de un conjunto de productos por Internet.
Incluye una pagina de login con Captcha y cifrado de clave mediante MD5 usando CrytpoJS, una pagina de selección de Categorías, una pagina de Productos por Categoría y finalmente la pagina del Pedido del Cliente.


Curso de MVC CIPSA - Demo 17 (Parte 3) - Carrito de Compras

Este video es la tercera parte de una aplicación que permite crear una orden o pedido de un conjunto de productos por Internet.
Incluye una pagina de login con Captcha y cifrado de clave mediante MD5 usando CrytpoJS, una pagina de selección de Categorías, una pagina de Productos por Categoría y finalmente la pagina del Pedido del Cliente.


Curso de MVC CIPSA - Demo 17 (Parte 4) - Carrito de Compras

Este video es la cuarta parte de una aplicación que permite crear una orden o pedido de un conjunto de productos por Internet.
Incluye una pagina de login con Captcha y cifrado de clave mediante MD5 usando CrytpoJS, una pagina de selección de Categorías, una pagina de Productos por Categoría y finalmente la pagina del Pedido del Cliente.


Curso de MVC CIPSA - Demo 17 (Parte 5) - Carrito de Compras

Este video es la quinta parte de una aplicación que permite crear una orden o pedido de un conjunto de productos por Internet.
Incluye una pagina de login con Captcha y cifrado de clave mediante MD5 usando CrytpoJS, una pagina de selección de Categorías, una pagina de Productos por Categoría y finalmente la pagina del Pedido del Cliente.


Curso de MVC CIPSA - Demo 17 (Parte 6) - Carrito de Compras

Este video es la sexta y última parte de una aplicación que permite crear una orden o pedido de un conjunto de productos por Internet.
Incluye una pagina de login con Captcha y cifrado de clave mediante MD5 usando CrytpoJS, una pagina de selección de Categorías, una pagina de Productos por Categoría y finalmente la pagina del Pedido del Cliente.


El Libro del Día: iOS Development with Xamarin Cookbook

El Libro del Día: 2015-03-19

Titulo: iOS Development with Xamarin Cookbook
Autor: Dimitris Tavlikos
Editorial: Packt
Nro Paginas: 386

Capítulos:
Chapter 1: Development Tools
Chapter 2: User Interface – Views
Chapter 3: User Interface – View Controllers
Chapter 4: Data Management
Chapter 5: Displaying Data
Chapter 6: Web Services
Chapter 7: Multimedia Resources
Chapter 8: Integrating iOS Features
Chapter 9: Interacting with Device Hardware
Chapter 10: Location Services and Maps
Chapter 11: Graphics and Animation
Chapter 12: Multitasking
Chapter 13: Localization
Chapter 14: Deploying
Chapter 15: Advanced Features

Descarga:
iOS_Development_with_Xamarin_Cookbook