Siguiendo la línea de Scott Mitchell en sus últimos artículos, a lo largo de una serie de posts vamos a ir viendo como podemos mostrar nuestros datos en formato datagrid, y como podemos aplicar las funcionalidades de paginado, filtrado y orden sobre los mismos, dentro de una aplicación ASP.NET MVC. En el artículo de hoy vamos a ver el primero de los casos, para ello creamos un proyecto de tipo ASP.NET MVC 2 Web Application que llamaremos DataGridApp. En el, añadimos la base de datos AdventureWorks, ya que tiene un gran número de tablas y registros con los que poder trabajar, y aplicarles la funcionalidad específica.
A continuación creamos un modelo de tipo LINQ to SQL Classes llamado StoreModel, que contendrá las tablas Customer y Store, de forma que nuestro modelo quede de la siguiente forma:
A continuación creamos un controlador especifico para trabajar con el modelo creado, y al que llamaremos StoreController.
En dicho controlador
1.Añadimos la referencia a nuestro modelo, y la referencia a Linq para poder trabajar con ellos:
- using DataGridApp.Models;
- using System.Data.Linq;
2.Inicializamos el contexto de nuestro modelo:
- StoreModelDataContext context = new StoreModelDataContext();
3.Creamos la acción Index, cuyo código será el siguiente:
- public ActionResult Index()
- {
- return View(context.Stores);
- }
Y su vista correspondiente, de forma que podamos mostrar todos los elementos definidos en nuestro contexto de datos. Para ello hacemos clic con él botón derecho sobre Index y añadimos una vista con las siguientes propiedades:
En dicha vista, cambiamos la propiedad Inherits a:
System.Web.Mvc.ViewPage<IEnumerable<DataGridApp.Models.Store>>
Y dentro del contenedor principal añadimos el siguiente código:
- <table>
- <tr>
- <th>Store Name</th>
- <th>Cust. Acc. Number</th>
- <th>Sales Person ID</th>
- </tr>
- <% foreach (var item in Model) { %>
- <tr>
- <td><%: item.Name %></td>
- <td><%: item.Customer.AccountNumber %></td>
- <td><%: item.SalesPersonID %></td>
- </tr>
- <% } %>
- </table>
A continuación creamos una clase para definir el modelo específico que queremos mostrar mediante la acción Paged. Para ello hacemos clic con el botón derecho sobre la carpeta Models y añadimos una clase llamada StoreGridModel que contendrá el siguiente código:
- public class StoreGridModel
- {
- // Definimos las propiedades
- public IEnumerable<Store> Stores { get; set; }
- public int CurrentPageIndex { get; set; }
- public int PageSize { get; set; }
- public int TotalRecordCount { get; set; }
- public int NumericPageCount { get; set; }
- public int PageCount
- {
- get
- {
- return this.TotalRecordCount / this.PageSize;
- }
- }
- public StoreGridModel()
- {
- // Establecemos los valores por defecto
- this.PageSize = 10;
- this.NumericPageCount = 10;
- }
- }
Por último pasamos a definir la acción Paged, a través de la cual mostraremos nuestros elementos paginados en grupos de 10 en 10, y por los que podremos navegar mediante el uso de vistas parciales. El código que utilizamos para implementar dicha acción es el siguiente:
- public ActionResult Paged(int page = 1, int pageSize = 10)
- {
- var model = new StoreGridModel
- {
- CurrentPageIndex = page,
- PageSize = pageSize
- };
- // Se determina el numero total de «stores» que se muestran en cada página
- model.TotalRecordCount = context.Stores.Count();
- // Se obtiene la página actual de «stores»
- model.Stores = context.Stores
- .Skip((model.CurrentPageIndex – 1) * model.PageSize)
- .Take(model.PageSize);
- return View(model);
- }
Definimos su vista correspondiente, que tendrá las siguientes propiedades:
Y cuyo código será el siguiente:
- <p>
- <i>Estas viendo la página <%: Model.CurrentPageIndex %> de <%: Model.PageCount %>…</i>
- </p>
- <table>
- <tr>
- <th>Store Name</th>
- <th>Cust. Acc. Number</th>
- <th>Sales Person ID</th>
- </tr>
- <% foreach (var item in Model.Stores) { %>
- <tr>
- <td><%: item.Name %></td>
- <td><%: item.Customer.AccountNumber %></td>
- <td><%: item.SalesPersonID %></td>
- </tr>
- <% } %>
- <tr>
- <td colspan=»5″>
- <% Html.RenderPartial(«Pager», Model); %>
- </td>
- </tr>
- </table>
Para que dicha vista funcione correctamente es necesario implementar previamente dos vistas parciales que guardaremos en la carpeta Views/Shared.
La primera de ellas, Pager.ascx nos permitirá realizar el renderizado parcial de los elementos de nuestro modelo, y en ella definiremos los distintos elementos de navegación. Esta vista se crea con las siguientes propiedades:
Y cuyo código es el siguiente:
- <div class=»pager»>
- <%
- // Creamos el Link «Anterior»
- Html.RenderPartial(«PagerLink», Model, new ViewDataDictionary { { «Text», «<< Anterior» }, { «PageIndex», Model.CurrentPageIndex – 1 }, { «Selected», false }, { «Inactive», Model.CurrentPageIndex == 1 } });
- // Creamos los links númericos
- var startPageIndex = Math.Max(1, Model.CurrentPageIndex – Model.NumericPageCount / 2);
- var endPageIndex = Math.Min(Model.PageCount, Model.CurrentPageIndex + Model.NumericPageCount / 2);
- // Mostramos los valores iniciales
- if (Model.PageCount > Model.NumericPageCount / 2)
- {
- if (startPageIndex > 1)
- Html.RenderPartial(«PagerLink», Model, new ViewDataDictionary { { «Text», «1» }, { «PageIndex», 1 }, { «Selected», false }, { «Inactive», false } });
- if (startPageIndex > 2)
- Html.RenderPartial(«PagerLink», Model, new ViewDataDictionary { { «Text», «2» }, { «PageIndex», 2 }, { «Selected», false }, { «Inactive», false } });
- if (startPageIndex > 3)
- Html.RenderPartial(«PagerLink», Model, new ViewDataDictionary { { «Text», «…» }, { «PageIndex», 1 }, { «Selected», false }, { «Inactive», true } });
- }
- // Añadimos el resto de páginas numericas
- for (var i = startPageIndex; i <= endPageIndex; i++)
- Html.RenderPartial(«PagerLink», Model, new ViewDataDictionary { { «Text», i }, { «PageIndex», i }, { «Selected», i == Model.CurrentPageIndex }, { «Inactive», false } });
- // Mostramos los valores finales
- if (Model.PageCount > Model.NumericPageCount / 2)
- {
- if (endPageIndex < Model.PageCount – 2)
- Html.RenderPartial(«PagerLink», Model, new ViewDataDictionary { { «Text», «…» }, { «PageIndex», 1 }, { «Selected», false }, { «Inactive», true } });
- if (endPageIndex < Model.PageCount – 1)
- Html.RenderPartial(«PagerLink», Model, new ViewDataDictionary { { «Text», Model.PageCount – 1 }, { «PageIndex», Model.PageCount – 1 }, { «Selected», false }, { «Inactive», false } });
- if (endPageIndex < Model.PageCount)
- Html.RenderPartial(«PagerLink», Model, new ViewDataDictionary { { «Text», Model.PageCount }, { «PageIndex», Model.PageCount }, { «Selected», false }, { «Inactive», false } });
- }
- // Creamos el link «Siguiente»
- Html.RenderPartial(«PagerLink», Model, new ViewDataDictionary { { «Text», «Siguiente >>» }, { «PageIndex», Model.CurrentPageIndex + 1 }, { «Selected», false }, { «Inactive», Model.CurrentPageIndex == Model.PageCount } });
- %>
- </div>
Y la segunda PagerLink.ascx que nos permitirá navegar por las distintas vistas, y aplicarles los estilos específicos. Esta contará con las siguientes propiedades:
Y su código es el siguiente:
- <%
- if ((bool)ViewData[«Inactive»])
- {
- Response.Write(string.Format(«<span class=»{0}»>{1}</span>», «pagerButtonDisabled», ViewData[«Text»]));
- }
- else
- {
- var routeData = new RouteValueDictionary { { «page», ViewData[«PageIndex»].ToString() }, { «pageSize», Model.PageSize } };
- var htmlAttributes = new Dictionary<string, object>();
- if ((bool)ViewData[«Selected»])
- htmlAttributes.Add(«class», «pagerButtonCurrentPage»);
- else
- htmlAttributes.Add(«class», «pagerButton»);
- Response.Write(
- Html.ActionLink(
- ViewData[«Text»].ToString(), // Link Text
- Html.ViewContext.RouteData.Values[«action»].ToString(), // Acción
- Html.ViewContext.RouteData.Values[«controller»].ToString(), // Controlador
- routeData, // Ruta
- htmlAttributes // Atributos HTML para aplicar a los hipervinculos
- ).ToHtmlString()
- );
- }
- %>
Creamos las clases que vamos a aplicar para personalizar el formato del paginado dentro de la hoja de estilos Site.css:
- /*Customizations*/
- td.pager {
- background-color: #eee;
- }
- td.pager div {
- font-size: smaller;
- font-weight: bold;
- padding: 5px;
- }
- a.pagerButton, a.pagerButton:visited
- {
- border: solid 1px #eee;
- padding: 4px;
- text-decoration: none;
- color: #006;
- margin: 0px 2px 0px 2px;
- }
- a.pagerButton:hover
- {
- border: solid 1px black;
- color: Black;
- }
- a.pagerButtonCurrentPage
- {
- border: solid 1px #00a;
- padding: 4px;
- text-decoration: none;
- color: White;
- background-color: #006;
- margin: 0px 2px 0px 2px;
- }
- .pagerButtonDisabled
- {
- border: none;
- color: #999;
- padding: 4px;
- }
Finalmente, compilamos la aplicación y navegamos a la ruta /Store/Paged donde podemos ver como se obtienen todos los elementos de nuestro modelo estructurados tal y como lo hemos definido en su configuración y como podemos navegar por ellos de forma sencilla.