miércoles, 11 de julio de 2012

ASP.NET + jqGrid + Exportación Excel


Hola.
Para tratar la exportación de los datos del jqGrid a Excel, vamos a partir del proyecto que generamos en el anterior tutorial sobre jqGrid.
Lo primero que vamos a hacer es crear una Clase que imulará el suministro de datos desde la base de datos. Así pues crearemos una nueva carpeta “Controller”en nuestro proyecto , y dentro de esta carpeta agregaremos una nueva clase a la que llamaremos “PagosController”.
En el proyecto anterior ya habíamos creado dentro del webservice un código que generaba los datos suministrados por nuestra supuesta Base de datos, así pues lo que haremos a continuación es mover ese código del web service a la clase que hemos creado y hacer que el WebService solicite esta información a dicha clase y que continúe funcionando como hasta ahora.
Así pues moveremos del WebService el siguiente bloque de código:

List<Pago> data = new List<Pago>();
Random rnd = new Random();
for (int n = 0; n < 30; n++)
{
  int num_days = rnd.Next(-365, -30);
  DateTime fecha = DateTime.Today.AddDays(num_days);
  decimal importe = Math.Round(Convert.ToDecimal((rnd.NextDouble() * 1000)), 2);
  decimal tasa = Math.Round(Convert.ToDecimal((rnd.NextDouble() * 100)), 2);
  decimal total = Math.Round((importe + tasa), 2);
  data.Add(new Pago()
  {
    ID = n + 1,
    FechaPago = fecha,
    Concepto = "JOHN DOE " + (n + 1).ToString(),
    Importe = importe,
    Tasas = tasa,
    Total = total,
    Notas = "Nota " + (n + 1).ToString()
  });
}

Y crearemos en la clase “PagosController” el siguiente método con el código movido:

public static List<Pago> GetListaPagos()
{
  List<Pago> data = new List<Pago>();
  Random rnd = new Random();
  for (int n = 0; n < 30; n++)
  {
    int num_days = rnd.Next(-365, -30);
    DateTime fecha = DateTime.Today.AddDays(num_days);
    decimal importe = Math.Round(Convert.ToDecimal((rnd.NextDouble() * 1000)), 2);
    decimal tasa = Math.Round(Convert.ToDecimal((rnd.NextDouble() * 100)), 2);
    decimal total = Math.Round((importe + tasa), 2);
    data.Add(new Pago()
    {
      ID = n + 1,
      FechaPago = fecha,
      Concepto = "JOHN DOE " + (n + 1).ToString(),
      Importe = importe,
      Tasas = tasa,
      Total = total,
      Notas = "Nota " + (n + 1).ToString()
    });
  }
  return data;
}

Por último haremos que el webservice llame a este método para solicitar la información

List<Pago> data = Controller.PagosController.GetListaPagos();

De esta forma el WebService se comporta igual y ahora podemos llamar a GetListaPagos desde otros puntos de la solución.
En este punto, antes de continuar, podríais ejecutar la solución para aseguraros que la página del grid continua funcionando como hasta ahora.
Ahora vamos a hacer es generar una Pagina aspx, la cual va a recibir las distintas peticiones de exportación y se encargará de recoger los datos y exportarlos a Excel.
Así pues crearemos una nueva página a la que llamaremos “Export2Excel.aspx”.
La idea en esta página es llamar el método del controlador que nos proporciona los datos a exportar. Convertir la lista de Pagos en un DataTable y retornar el data table exportado a Excel.
Para realizar estas acciones, lo primero que vamos a generar es un conjunto de métodos que nos permitirán convertir nuestras listas genéricas en Datatables. Creamos el siguiente código en nuestra página:

public static DataTable ConvertTo<T>(IList<T> list)
{
  DataTable table = CreateTable<T>();
  Type entityType = typeof(T);
  PropertyDescriptorCollection properties = TypeDescriptor.GetProperties(entityType);
  foreach (T item in list)
  {
    DataRow row = table.NewRow();
    foreach (PropertyDescriptor prop in properties)
    {
      row[prop.Name] = prop.GetValue(item) ?? DBNull.Value;
    }
    table.Rows.Add(row);
  }
  return table;
}
public static DataTable CreateTable<T>()
{
  Type entityType = typeof(T);
  DataTable table = new DataTable(entityType.Name);
  PropertyDescriptorCollection properties = TypeDescriptor.GetProperties(entityType);
  foreach (PropertyDescriptor prop in properties)
  {
    // HERE IS WHERE THE ERROR IS THROWN FOR NULLABLE TYPES
    table.Columns.Add(prop.Name, Nullable.GetUnderlyingType(prop.PropertyType) ?? prop.PropertyType);
  }
  return table;
}

Con los métodos creados, vamos a proceder a crear una función que se encargue de generar la respuesta en formato Excel a partir de un Datatable. Así pues, creamos el siguiente código:

public static void ExportToExcel(string fileName, DataTable dt)
{
  HttpContext.Current.Response.Clear();
  HttpContext.Current.Response.AddHeader(
"content-disposition", string.Format("attachment; filename={0}", fileName));
  HttpContext.Current.Response.ContentType = "application/ms-excel";
  string tab = "";
  foreach (DataColumn dc in dt.Columns)
  {
    HttpContext.Current.Response.Write(tab + dc.ColumnName);
    tab = "\t";
  }
  HttpContext.Current.Response.Write("\n");
  int i;
  foreach (DataRow dr in dt.Rows)
  {
    tab = "";
    for (i = 0; i < dt.Columns.Count; i++)
    {
      HttpContext.Current.Response.Write(tab + dr[i].ToString());
      tab = "\t";
    }
    HttpContext.Current.Response.Write("\n");
  }
  HttpContext.Current.Response.End();
}

Ya tenemos todos los elementos que necesitamos para la exportación.
Solo nos falta unificar todos estos puntos en el Page_Load de la página:

protected void Page_Load(object sender, EventArgs e)
{
  List<Pago> data = Controller.PagosController.GetListaPagos();
  DataTable dt = ConvertTo(data);
  ExportToExcel("ListaPagos.xls", dt);
}

Ya tenemos nuestra página de exportación a Excel lista para ser invocada.
Vamos ahora a enlazar nuestro jqGrid con esta página.
Para ello vamos a agregar al jqGrid un botón que al ser pulsado invocara nuestra página.
Asi el código de nuestro jqGrid quedará de la siguiente forma.

jQuery(document).ready(function () {
  $("#contactsList").jqGrid({
    url: '/WsDataProvider.asmx/GetGridDataOrder',
    datatype: 'json',
    mtype: 'POST',
    ajaxGridOptions: { contentType: 'application/json; charset=utf-8' },
    serializeGridData: function (postData) {
      return JSON.stringify(postData);
    },
    jsonReader: { repeatitems: false, root: "d.rows", page: "d.page", total: "d.total", records: "d.records" },
    colModel: [
      { name: 'ID', label: 'ID', key: true, width: 60, sortable: true, align: "center", hidden: false },
      { name: 'FechaPago', label: 'Fecha', width: 80, sortable: true, hidden: false, formatter: 'date', formatoptions: { srcformat: 'm/d/Y h:i:s', newformat: 'd-m-Y'} },
      { name: 'Concepto', label: 'Concepto', width: 180, sortable: true, hidden: false },
      { name: 'Importe', label: 'Importe', width: 180, sortable: true, align: "center", hidden: false, formatter:currencyFmatter },
      { name: 'Tasas', label: 'Tasa', key: true, width: 60, sortable: true, align: "center", hidden: false, formatter: currencyFmatter },
      { name: 'Total', label: 'Total', width: 80, sortable: true, align: "center", hidden: false, formatter: currencyFmatter },
      { name: 'Notas', label: 'Notas', width: 180, sortable: false, hidden: false }
    ],
    rowNum: 10,
    rowList: [10, 20, 30],
    pager: "#gridpager",
    viewrecords: true,
    gridview: true,
    rownumbers: true,
    height: 420, width: 980,
    caption: 'Lista de Pagos'
  }).jqGrid('navGrid', '#gridpager', {
    edit: true, add: true, del: false, search: true
  }).jqGrid('navButtonAdd', '#gridpager', {
    caption: '<span class="ui-pg-button-text">Export</span>',
    buttonicon: "ui-icon-extlink",
    title: "Export To Excel",
    onClickButton: function () {
      window.location = 'Export2Excel.aspx';
    }
  });
});

He marcado en gris el código que se encarga de generar el botón de exportación.
Pues ya solo nos falta probarlo.
Ya tenemos nuestro jqGrid con exportación de datos a Excel.
Como siempre, teneis a vuestra disposición el proyecto para que lo estudieis
Espero que os sea de ayuda, a partir de aquí podéis sofisticar el sistema como queráis.
Saludos.-

ASP.NET + jqGrid + Webservice asmx


Hola a todos.
Recientemente en uno de mis proyectos me he visto en la necesidad de investigar un poco las capacidades de integración entre jqGrid y webservices, motivo por el cual me he decidido a realizar este pequeño tutorial.
Como siempre vamos a empezar creando una solución con nuestro Visual Studio. En este caso un proyecto “Aplicación Web Vacia”
Con nuestro proyecto ya creado, vamos a empezar a diseñar el WebService que nos proveerá la información que ha de consumir nuestro grid.
Para ello agregamos al proyecto un nuevo WebService al que pondremos en nombre que queramos (yo he optado por “WsDataProvider”).
Se nos creara el webservice con su correspondiente WebMethod “HelloWord” que se genera por defecto.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Services;
namespace TestJqGrid
{
/// <summary>
/// Summary description for WsDataProvider
/// </summary>
[WebService(Namespace = "http://tempuri.org/")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
//[System.ComponentModel.ToolboxItem(false)]
// To allow this Web Service to be called from script, using ASP.NET AJAX, uncomment the following line.
//[System.Web.Script.Services.ScriptService]
public class WsDataProvider : System.Web.Services.WebService
{
[WebMethod]
public string HelloWorld()
{
return "Hello World";
}
}
}
Des comentamos la siguiente línea que necesitaremos para retornar los datos en Json.
[System.Web.Script.Services.ScriptService]
Vamos a proceder antes que nada a crear las clases que usaremos para almacenar la información que mostrará en grid.
Como el grid va a mostrar una lista de Pagos, la primera clase que crearemos es la clase “Pago”, la cual tendrá el siguiente código:
public class Pago
{
public int ID { set; get; }
public DateTime FechaPago { set; get; }
public string Concepto { set; get; }
public decimal Importe { set; get; }
public decimal Tasas { set; get; }
public decimal Total {set; get;}
public string Notas { set; get; }
}
Además de esta clase, crearemos una nueva clase que es la que proveerá la información al grid, no solo con la lista de Pagos sino con otra información necesaria para el grid como es el número de página, el número de rows por página, etc, etc.
public class JqGridDataPagos
{
public int total { get; set; }
public int page { get; set; }
public int records { get; set; }
public List<Pago> rows { get; set; }
}
Con las clases creadas, ya podemos proceder a preparar el WebMethod que será consumido por nuestro grid.
[WebMethod, ScriptMethod(ResponseFormat = ResponseFormat.Json)]
public JqGridDataPagos GetGridDataOrder(int page, int rows, string sidx, string sord, bool _search)
{
List<Pago> data = new List<Pago>();
for (int n = 0; n < 30; n++)
{
data.Add(new Pago()
{
ID = n + 1,
FechaPago = DateTime.Today.AddDays(((n + 1) * -1)),
Concepto = "JOHN DOE " + (n + 1).ToString(),
Importe = Convert.ToDecimal(n * 1000),
Tasas = Convert.ToDecimal(n * 100),
Total = Convert.ToDecimal(n * 1100),
Notas = "Nota " + (n + 1).ToString()
});
}
int recordsCount = data.Count;
int startIndex = (page - 1) * rows;
int endIndex = (startIndex + rows < recordsCount) ? startIndex + rows : recordsCount;
List<Pago> gridRows = new List<Pago>(rows);
for (int i = startIndex; i < endIndex; i++)
gridRows.Add(data[i]);
return new JqGridDataPagos()
{
total = (recordsCount + rows - 1) / rows,
page = page,
records = recordsCount,
rows = gridRows
};
}
Es importante destacar que no selo hemos definido el método como “WebMethod”, sino que hemos especificado el formato de salida del mismo por ”ResponseFormat.Json”, ya que de lo contrario jqgrid no será capaz de consumir los datos retornados por el WebMethod.
[WebMethod, ScriptMethod(ResponseFormat = ResponseFormat.Json)]
Como podéis ver este método simplemente crea una lista de 30 objetos Pago que vendrían a simular el resultado de una consultar en base de datos y retorna un objeto JqGridDataPagos con los elementos de la página especificada en los parámetros del método.
Antes de ponernos a crear la página que contendrá nuestro Grid, vamos a proceder a instalar el mismo en nuestro proyecto.
Empezaremos creando una carpeta “Scripts” donde dejaremos los ficheros del componente y una carpeta “Content” donde depositaremos otros archivos como pueden ser distintos css de estilo.
Ahora nos descargamos de la web del fabricante la última versión del grid
Una vez descargado y descomprimido, abrimos la carpeta en un explorador. Encontraremos varias carpetas y archivos. Abrimos la carpeta “js” y copiamos todo su contenido en la carpeta “Scripts” de nuestro proyecto.
Aprovecharemos también para copiar la carpeta “theme” y la carpeta “css” en la carpeta “Content” que creamos anteriormente.
con lo que nuestro proyecta quedara más o menos así
Ahora vamos a proceder a crear ya nuestra página.
Agregamos un nuevo WebForm Default.aspx a la solución.
Empezaremos agregando las referencias a los distintos scripts que vamos a necesitar, agregando las siguientes líneas al “head” de nuestra página
<title>Demonstration how use jqGrid to call ASMX-WebService rows</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<link rel="stylesheet" type="text/css" href="/Content/css/ui-darkness/jquery-ui-1.8.21.custom.css" />
<link rel="stylesheet" type="text/css" href="/Content/themes/ui.jqgrid.css" />
<script type="text/javascript" src="/Scripts/jquery-1.7.2.min.js"></script>
<script type="text/javascript" src="/Scripts/jquery-ui-1.8.21.custom.min.js"></script>
<script type="text/javascript" src="/Scripts/trirand/i18n/grid.locale-en.js"></script>
<script type="text/javascript" src="/Scripts/trirand/jquery.jqGrid.min.js"></script>
Ahora vamos a crear el body que vamos a utilizar
<body>
<table id="contactsList"></table>
<div id="gridpager"></div>
</body>
Ya tenemos los script cargados y el body preparado. Es el memento de crar el código javascript que se encargará de cargar el grid. Para ello vamos a agregar el siguiente script al “head”
<script type="text/javascript">
//<![CDATA[
jQuery(document).ready(function () {
$("#contactsList").jqGrid({
url: '/WsDataProvider.asmx/GetGridDataOrder',
datatype: 'json',
mtype: 'POST',
ajaxGridOptions: { contentType: 'application/json; charset=utf-8' },
serializeGridData: function (postData) {
return JSON.stringify(postData);
},
jsonReader: { repeatitems: false, root: "d.rows", page: "d.page", total: "d.total", records: "d.records" },
colModel: [
{ name: 'ID', label: 'ID', key: true, width: 60, align: "center", hidden: false },
{ name: 'FechaPago', label: 'Fecha', width: 80, sortable: false, hidden: false, formatter: 'date', formatoptions: { srcformat: 'm/d/Y h:i:s', newformat: 'd-m-Y'} },
{ name: 'Concepto', label: 'Concepto', width: 180, sortable: false, hidden: false },
{ name: 'Importe', label: 'Importe', width: 180, sortable: false, align: "center", hidden: false },
{ name: 'Tasas', label: 'Tasa', key: true, width: 60, align: "center", hidden: false },
{ name: 'Total', label: 'Total', width: 80, sortable: false, align: "center", hidden: false },
{ name: 'Notas', label: 'Notas', width: 180, sortable: false, hidden: false }
],
rowNum: 10,
rowList: [10, 20, 300],
pager: "#gridpager",
viewrecords: true,
gridview: true,
rownumbers: true,
height: 230,
caption: 'Lista de Pagos'
}).jqGrid('navGrid', '#gridpager', { edit: true, add: true, del: false, search: true });
});
//]]>
</script>
Ya tenemos nuestra página lista para ser usada. Si hemos agregado correctamente los scripts la página debería funcionar y ejecutarse mostrando un resultado similar a este:
Espero que os sirva de ayuda.
Podeis descargaros el proyecto desde este enlace:
Un Saludo.-