Massimiliano's Blog - My .Net World

Tutto quello che avreste voluto sapere ma non avete mai chiesto......

MVC Paging & Sorting Data

Prima di questa estate avevo visto e seguito con interesse i video su MVC di ASP.NET di Simone Chiaretta sul portale Microsoft http://www.microsoft.com/italy/beit;  sono rimasto davvero incuriosito dall’argomento anche perché mi sono sempre chiesto come mai la cara vecchia Microsoft non avesse sviluppato alcun framework a riguardo, cosa che invece nel mondo java è ampiamente in uso e supportato da tempo( ad esempio “Struts di Apache”).

A questo punto mi ero ripromesso, non appena avessi avuto un po’ di tempo ,di provare le funzionalità del framework MVC e di valutare in linea di massima la fattibilità i pro e contro dell’infrastruttura offerta. Ecco, dopo mesi ho iniziato a “smanettare” sul prodotto e in particolare ho iniziato a convertire una parte banale di visualizzazione e data entry di un applicazione web esistente e sviluppata con ASP.NET 2.0. Non penso sia il caso di spiegare come il framework lavora anzi se fate una ricerca in rete vi è molto materiale esaustivo e sicuramente più dettagliato di quello che posso trasmettere io; comunque da una prima impressione quello che ho sicuramente notato è il fatto che purtroppo (o forse meglio) bisogna scrivere un bel po’ di codice html in più cosa che con le asp.net webform classiche non facevamo. Dimentichiamoci infatti gli utilissimi e immediati controlli presenti che trascinavamo dalla toolbox, il ViewState e il Postback etc.. ; infatti, ad esempio, per effettuare il rendering di un html input textbox dovremmo scrivere la seguente linea di codice all'interno della pagina aspx:

<%=Html.TextBox("txtProduct") %>

In realtà la cosa che è un po’ più “scocciante” è legata alla mancanza di controlli che effettuano binding di liste come il DataGridView e  la ListView, comodissimi in quanto permettevano di effettuare sorting e paging in maniera semplice. Fortunatamente ci sono i template che vengono in aiuto creando e “bindando” in automatico le proprietà del nostro modello sotto forma di tabella html, ma non risolvono tutte le classiche problematiche del caso. Ad esempio il sorting e il paging devono essere implementati via codice. Di seguito quindi riporto un possibile punto di partenza per affrontare la problematica.

In particolare ho creato una classe che presa una sorgente come input implementa le logiche di suddivisione delle possibili pagine e che verrà poi utilizzata come modello da visualizzare alla vista in questione.

   1: using System;
   2: using System.Collections.Generic;
   3: using System.Linq;
   4: using System.Linq.Expressions;
   5: using System.Reflection;
   6:  
   7: namespace MVC.MyUtility
   8: {
   9:  
  10:     public class PagedView<T> where T : new()
  11:     {
  12:         private IList<int> _numberList;
  13:         private int _pageSize;
  14:         private int _pageCount;
  15:         private int _totalItemCount;
  16:         private int _pageIndex;
  17:         private bool _isFastNavNextEnabled;
  18:         private bool _isFastNavPrevEnabled;
  19:         private string _sortedProperty;
  20:         private bool _sortedType;
  21:  
  22:         public PagedView(IQueryable<T> source, int pageSize, int index, string sortedProperty, bool sortTypeAscending)
  23:         {
  24:             _numberList = new List<int>();
  25:             _pageSize = pageSize;
  26:             _sortedProperty = sortedProperty;
  27:             _pageIndex = index;
  28:             _sortedType = sortTypeAscending;
  29:             _totalItemCount = source.Count();
  30:             _pageCount = _totalItemCount / _pageSize;
  31:  
  32:             if (_totalItemCount % _pageSize > 0)
  33:                 _pageCount++;
  34:  
  35:             int start = 1;
  36:             int endCycle = pageSize;
  37:  
  38:  
  39:             if (index > 0 && index <= pageSize)
  40:             {
  41:                 //E'il primo blocco di dati
  42:                 start = 1;
  43:                 endCycle = pageSize;
  44:                 _isFastNavNextEnabled = true;
  45:             }
  46:             else
  47:                 if (index % pageSize == 0)
  48:                 {
  49:                     //Si verifica la posizione dell'indice nel range
  50:                     //In questo caso si cambia l'indice
  51:                     start = index;
  52:                     endCycle = index + pageSize;
  53:                     _isFastNavNextEnabled = true;
  54:                     _isFastNavPrevEnabled = true;
  55:                 }
  56:                 else
  57:                 {
  58:                     start = index - (index % pageSize);
  59:                     endCycle = start + pageSize;
  60:                     _isFastNavNextEnabled = true;
  61:                     _isFastNavPrevEnabled = true;
  62:                 }
  63:  
  64:             //Controllo se ho sforato ovvero se 
  65:             //il numero di pagine totali è > di endcycle
  66:             if (endCycle >= PageCount)
  67:             {
  68:                 endCycle = PageCount;
  69:                 _isFastNavNextEnabled = false;
  70:             }
  71:  
  72:  
  73:             for (int i = start; i <= endCycle; i++)
  74:                 _numberList.Add(i);
  75:  
  76:             //if (index >= pageSize)
  77:             //    _isFastNavPrevEnabled = true;
  78:  
  79:             if (sortTypeAscending)
  80:             {
  81:                 if (index > 1)
  82:                     ObjectList = LinqHelper.OrderBy<T>(source, sortedProperty).Skip(pageSize * (index - 1)).Take(pageSize);
  83:                 else
  84:                     ObjectList = LinqHelper.OrderBy<T>(source, sortedProperty).Take(pageSize);
  85:             }
  86:             else
  87:             {
  88:                 if (index > 1)
  89:                     ObjectList = LinqHelper.OrderByDescending<T>(source, sortedProperty).Skip(pageSize * (index - 1)).Take(pageSize);
  90:                 else
  91:                     ObjectList = LinqHelper.OrderByDescending<T>(source, sortedProperty).Take(pageSize);
  92:             }
  93:  
  94:         }
  95:  
  96:         public IQueryable<T> ObjectList
  97:         {
  98:             get;
  99:             set;
 100:         }
 101:  
 102:         public IList<int> PageList
 103:         {
 104:             get
 105:             {
 106:                 return _numberList;
 107:             }
 108:         }
 109:  
 110:         public int PageSize
 111:         {
 112:             get
 113:             {
 114:                 return _pageSize;
 115:             }
 116:         }
 117:  
 118:         public int PageCount
 119:         {
 120:             get
 121:             {
 122:                 return _pageCount;
 123:             }
 124:         }
 125:  
 126:         public int PageIndex
 127:         {
 128:             get
 129:             {
 130:                 return _pageIndex;
 131:             }
 132:         }
 133:  
 134:         public bool IsNextAvailable
 135:         {
 136:             get
 137:             {
 138:                 return ((_pageIndex * _pageSize) < _totalItemCount);
 139:             }
 140:         }
 141:  
 142:         public bool IsPrevAvailable
 143:         {
 144:             get
 145:             {
 146:                 return !(_pageIndex == 1 && ObjectList.Count() > 0);
 147:             }
 148:         }
 149:  
 150:         public bool IsFastNavNextAvailable
 151:         {
 152:             get
 153:             {
 154:                 return _isFastNavNextEnabled;
 155:             }
 156:         }
 157:  
 158:         public bool IsFastNavPrevAvailable
 159:         {
 160:             get
 161:             {
 162:                 return _isFastNavPrevEnabled;
 163:             }
 164:         }
 165:  
 166:         public string SortedProperty
 167:         {
 168:             get
 169:             {
 170:                 return _sortedProperty;
 171:             }
 172:         }
 173:  
 174:         public bool IsSortTypeAscending
 175:         {
 176:             get
 177:             {
 178:                 return _sortedType;
 179:             }
 180:         }
 181:  
 182:     }
 183:  
 184:     public static class LinqHelper
 185:     {
 186:         public static IOrderedQueryable<T> OrderBy<T>(this IQueryable<T> source, string property)
 187:         {
 188:             return ApplyOrder<T>(source, property, "OrderBy");
 189:         }
 190:         public static IOrderedQueryable<T> OrderByDescending<T>(this IQueryable<T> source, string property)
 191:         {
 192:             return ApplyOrder<T>(source, property, "OrderByDescending");
 193:         }
 194:         public static IOrderedQueryable<T> ThenBy<T>(this IOrderedQueryable<T> source, string property)
 195:         {
 196:             return ApplyOrder<T>(source, property, "ThenBy");
 197:         }
 198:         public static IOrderedQueryable<T> ThenByDescending<T>(this IOrderedQueryable<T> source, string property)
 199:         {
 200:             return ApplyOrder<T>(source, property, "ThenByDescending");
 201:         }
 202:  
 203:         private static IOrderedQueryable<T> ApplyOrder<T>(IQueryable<T> source, string property, string methodName)
 204:         {
 205:             string[] props = property.Split('.');
 206:             Type type = typeof(T);
 207:             ParameterExpression arg = Expression.Parameter(type, "x");
 208:             Expression expr = arg;
 209:             foreach (string prop in props)
 210:             {
 211:                 // use reflection (not ComponentModel) to mirror LINQ            
 212:                 PropertyInfo pi = type.GetProperty(prop);
 213:                 expr = Expression.Property(expr, pi);
 214:                 type = pi.PropertyType;
 215:             }
 216:             Type delegateType = typeof(Func<,>).MakeGenericType(typeof(T), type);
 217:             LambdaExpression lambda = Expression.Lambda(delegateType, expr, arg);
 218:             object result =
 219:                 typeof(Queryable).GetMethods()
 220:                 .Single(method => method.Name == methodName &&
 221:                                   method.IsGenericMethodDefinition &&
 222:                                   method.GetGenericArguments().Length == 2 &&
 223:                                   method.GetParameters().Length == 2).
 224:                 MakeGenericMethod(typeof(T), type).Invoke(null, new object[] { source, lambda });
 225:             return (IOrderedQueryable<T>)result;
 226:         }
 227:     }
 228: }

 Il tipo T non è altro che il tipo dell’oggetto da “bindare” nella nostra griglia, nel mio caso come modello dati ho utilizzato Entity Framework per cui nell’esempio e un tipo “Product”. Il costruttore ha come input i seguenti parametri :

IQueryable<T> source //sorgente dati 
int pageSize //Dimensione della pagina ovvero numero di righe della griglia 
int index //Indice della Pagina corrente
string sortedProperty //Proprietà dell’oggetto di tipo T su cui effettuare il sorting
bool sortTypeAscending //true se il sorting è Ascending

L’oggetto infine espone le seguenti proprietà:

IList<int> PageList //List dei numeri di pagina da linkare per il paginig
int PageSize //Dimensione della pagine ovvero numero di item da mostrare
int PageCount //Dimensione totale delle pagine
int PageIndex //Indice della pagina corrente
bool IsNextAvailable //Flag che indica se esiste una pagina successiva
bool IsPrevAvailable //Flag che indica se esiste una pagina precedente
bool IsFastNavNextAvailable //Flag che indica se è esiste una pagina successiva al valore di PageIndex + PageSize
bool IsFastNavPrevAvailable//Flag che indica se è esiste una pagina precedente  al valore di PageIndex – PageSize
string SortedProperty //Proprietà dell’oggetto di tipo T su cui effettuare il sorting
bool IsSortTypeAscending //true se il sorting è Ascending
IQueryable<T> ObjectList //lista degli oggetti manipolata in base ai parametri del costruttore

 Quindi la Action del nostro Controller non fa altro che istanziare un oggetto di tipo PagedView<Product> e passarlo alla vista .

   1: public ActionResult Index()
   2: {
   3:     PagedView<Product> obj = new PagedView<Product>(GetProductList(),_nrItemPage, 1, "CodProduct", true);
   4:     return View(obj);
   5: }

 A questo punto manca la creazione della vista che sarà di tipo strongly-typed e che effettuerà il render dell’oggetto di tipo PagedView<Product>.

   1: <%@ Page Title="" Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage<MVC.MyUtility.PagedView<WebSellingMVC.Models.Product>>" %>
   2:  
   3: <asp:Content ID="Content1" ContentPlaceHolderID="TitleContent" runat="server">
   4:     Product List
   5: </asp:Content>
   6: <asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server">
   7:  
   8:     <script src="/Scripts/MicrosoftAjax.js" type="text/javascript" />
   9:     <script src="/Scripts/jquery-1.3.2.js" type="text/javascript"/>
  10:     <script src="/Scripts/MicrosoftMvcAjax.js" type="text/javascript"/>
  11:  
  12:       <h2>Product List</h2>
  13:       <br />
  14:       <%Html.RenderPartial("ProductCtrl", Model); %>
  15: </asp:Content>

Come avrete potuto notare sono stati aggiunti dei riferimenti a javascript  (sono inclusi automaticamente dal template della soluzione MVC)  e ad uno User Control; la motivazione è semplice , vogliamo implementare sia il sorting che il paging attraverso l’utilizzo di Ajax. Big Smile Quindi la View index.aspx non fa altro che da contenitore al controllo ProductCtrl.ascx ( il seguente blocco di codice) il quale realizza graficamente la griglia degli oggetti Product e che viene popolata attraverso richieste Ajax.

   1: <%@ Control Language="C#" 
   2: Inherits="System.Web.Mvc.ViewUserControl<MVC.MyUtility.PagedView<WebSellingMVC.Models.Product>>" %>
   3:  
   4: <div id="sortContainer">
   5:     <table width="100%">
   6:         <tr>
   7:             <td colspan="10">
   8:                 <% if (Model.IsFastNavPrevAvailable)
   9:                    {%>
  10:                 <%= Ajax.ActionLink("<< ", "MovePrevFast", new { pageIndex = Model.PageIndex, propertyToSort = Model.SortedProperty, sortType = Model.IsSortTypeAscending }, new AjaxOptions()
  11: {
  12:     UpdateTargetId = "sortContainer",
  13:     InsertionMode = InsertionMode.Replace    
  14: })%>
  15:                 <% }%>
  16:                 <% foreach (int item in Model.PageList)
  17:                    { %>
  18:                 <%= Ajax.ActionLink(item.ToString() + " ", "MovePage", new { pageIndex = item, propertyToSort = Model.SortedProperty, sortType = Model.IsSortTypeAscending }, new AjaxOptions()
  19: {
  20:     UpdateTargetId = "sortContainer",
  21:     InsertionMode = InsertionMode.Replace
  22: })%>
  23:                 <% }%>
  24:                 <% if (Model.IsFastNavNextAvailable)
  25:                    {%>
  26:                 <%= Ajax.ActionLink(">> ", "MoveNextFast", new { pageIndex = Model.PageIndex, propertyToSort = Model.SortedProperty, sortType = Model.IsSortTypeAscending }, new AjaxOptions()
  27: {
  28:     UpdateTargetId = "sortContainer",
  29:     InsertionMode = InsertionMode.Replace
  30: })%>
  31:                 <% }%>
  32:             </td>
  33:         </tr>
  34:         <tr>
  35:             <th>
  36:                 <%= Ajax.ActionLink("CodProduct", "Sort", new { pageIndex = Model.PageIndex, propertyToSort = "CodProduct", sortType = !Model.IsSortTypeAscending }, new AjaxOptions()
  37: {
  38:     UpdateTargetId = "sortContainer",
  39:     InsertionMode = InsertionMode.Replace
  40: })%>
  41:             </th>
  42:             <th>
  43:                 <%= Ajax.ActionLink("NameIT", "Sort", new { pageIndex = Model.PageIndex, propertyToSort = "NameIT", sortType = !Model.IsSortTypeAscending }, new AjaxOptions()
  44: {
  45:     UpdateTargetId = "sortContainer",
  46:     InsertionMode = InsertionMode.Replace
  47: })%>
  48:             </th>
  49:             <th>
  50:                 <%= Ajax.ActionLink("NameEN", "Sort", new { pageIndex = Model.PageIndex, propertyToSort = "NameEN", sortType = !Model.IsSortTypeAscending }, new AjaxOptions()
  51: {
  52:     UpdateTargetId = "sortContainer",
  53:     InsertionMode = InsertionMode.Replace
  54: })%>
  55:             </th>
  56:             <th>
  57:                 <%= Ajax.ActionLink("CategoryIT", "Sort", new { pageIndex = Model.PageIndex, propertyToSort = "CategoryIT", sortType = !Model.IsSortTypeAscending }, new AjaxOptions()
  58: {
  59:     UpdateTargetId = "sortContainer",
  60:     InsertionMode = InsertionMode.Replace
  61: })%>
  62:             </th>
  63:             <th>
  64:                 <%= Ajax.ActionLink("CategoryEN", "Sort", new { pageIndex = Model.PageIndex, propertyToSort = "CategoryEN", sortType = !Model.IsSortTypeAscending }, new AjaxOptions()
  65: {
  66:     UpdateTargetId = "sortContainer",
  67:     InsertionMode = InsertionMode.Replace
  68: })%>
  69:             </th>
  70:             <th>
  71:                 <%= Ajax.ActionLink("TeamLine", "Sort", new { pageIndex = Model.PageIndex, propertyToSort = "TeamLine", sortType = !Model.IsSortTypeAscending }, new AjaxOptions()
  72: {
  73:     UpdateTargetId = "sortContainer",
  74:     InsertionMode = InsertionMode.Replace
  75: })%>
  76:             </th>
  77:             <th>
  78:                 <%= Ajax.ActionLink("Brand", "Sort", new { pageIndex = Model.PageIndex, propertyToSort = "Brand", sortType = !Model.IsSortTypeAscending }, new AjaxOptions()
  79: {
  80:     UpdateTargetId = "sortContainer",
  81:     InsertionMode = InsertionMode.Replace
  82: })%>
  83:             </th>
  84:             <th>
  85:                 <%= Ajax.ActionLink("UM", "Sort", new { pageIndex = Model.PageIndex, propertyToSort = "UM", sortType = !Model.IsSortTypeAscending }, new AjaxOptions()
  86: {
  87:     UpdateTargetId = "sortContainer",
  88:     InsertionMode = InsertionMode.Replace
  89: })%>
  90:             </th>
  91:             <th>
  92:                 <%= Ajax.ActionLink("Quantity", "Sort", new { pageIndex = Model.PageIndex, propertyToSort = "Quantity", sortType = !Model.IsSortTypeAscending }, new AjaxOptions()
  93: {
  94:     UpdateTargetId = "sortContainer",
  95:     InsertionMode = InsertionMode.Replace
  96: })%>
  97:             </th>
  98:             <th>
  99:                 <%= Ajax.ActionLink("Price", "Sort", new { pageIndex = Model.PageIndex, propertyToSort = "Price", sortType = !Model.IsSortTypeAscending }, new AjaxOptions()
 100: {
 101:     UpdateTargetId = "sortContainer",
 102:     InsertionMode = InsertionMode.Replace
 103: })%>
 104:             </th>
 105:         </tr>
 106:         <% foreach (var item in Model.ObjectList)
 107:            {%>
 108:         <tr>
 109:             <td>
 110:                 <%= Html.Encode(item.CodProduct) %>
 111:             </td>
 112:             <td>
 113:                 <%= Html.Encode(item.NameIT) %>
 114:             </td>
 115:             <td>
 116:                 <%= Html.Encode(item.NameEN) %>
 117:             </td>
 118:             <td>
 119:                 <%= Html.Encode(item.CategoryIT) %>
 120:             </td>
 121:             <td>
 122:                 <%= Html.Encode(item.CategoryEN) %>
 123:             </td>
 124:             <td>
 125:                 <%= Html.Encode(item.TeamLine) %>
 126:             </td>
 127:             <td>
 128:                 <%= Html.Encode(item.Brand) %>
 129:             </td>
 130:             <td>
 131:                 <%= Html.Encode(item.UM) %>
 132:             </td>
 133:             <td>
 134:                 <%= Html.Encode(item.Quantity) %>
 135:             </td>
 136:             <td>
 137:                 <%= Html.Encode(String.Format("{0:F}", item.Price)) %>
 138:             </td>
 139:         </tr>
 140:         <% }%>
 141:         <tr>
 142:             <td colspan="8">
 143:                 Page
 144:                 <%= Html.Encode(Model.PageIndex) %>
 145:                 of
 146:                 <%= Html.Encode(Model.PageCount) %>
 147:             </td>
 148:             <td colspan="2" align="right">               
 149:                 <%if (Model.IsPrevAvailable)
 150:                   { %>
 151:                 <%= Ajax.ActionLink("Prev", "MovePage", new { pageIndex = Model.PageIndex - 1, propertyToSort = Model.SortedProperty, sortType = Model.IsSortTypeAscending }, new AjaxOptions()
 152: {
 153:     UpdateTargetId = "sortContainer",
 154:     InsertionMode = InsertionMode.Replace
 155: })%>
 156:                 <% }%>
 157:                  <%if (Model.IsNextAvailable)
 158:                   { %>
 159:                 <%= Ajax.ActionLink("Next", "MovePage", new { pageIndex = Model.PageIndex + 1, propertyToSort = Model.SortedProperty, sortType = Model.IsSortTypeAscending }, new AjaxOptions()
 160: {
 161:     UpdateTargetId = "sortContainer",
 162:     InsertionMode = InsertionMode.Replace
 163: })%>
 164:                 <% }%>
 165:             </td>
 166:         </tr>
 167:     </table>
 168: </div>

 Nella prima riga  renderizziamo i pulsanti che realizzano la navigazione delle pagine in base alla dimensione della lista sorgente. Infatti con la seguente istruzione valutiamo se è abilitata la navigazione veloce ovvero spostiamo l’indice di più pagine alla volta, in caso affermativo viene creato il codice html per invocare una Action via Ajax del Controller in questione denominata "MovePrevFast" passando come parametri :

· l’indice della pagina attuale;

· la proprietà dell’oggetto Product su cui effettuare sorting;

· se il sorting è di tipo Ascending.

Inoltre con l’ultimo parametro indichiamo che l’obiettivo della chiamata è il container con id=”sortContainer” e che i dati ricevuti dalla chiamata Ajax dovranno sostituire il contenuto di tale container.

   1: <% if (Model.IsFastNavPrevAvailable) {%>
   2: <%= Ajax.ActionLink("<< ", 
   3:     new { 
   4:         PageIndex, 
   5:         propertyToSort = Model.SortedProperty, 
   6:         sortType = Model.IsSortTypeAscending 
   7:         },
   8:     new AjaxOptions()
   9:         {
  10:             Id = "sortContainer",
  11:             e = InsertionMode.Replace    
  12:         })%>
  13: <% }%>

 Tutto questo lato Controller si sviluppa così :

   1: public ActionResult MovePrevFast(int pageIndex, string propertyToSort, bool sortType)
   2: {
   3:    PagedView<Product> obj = new PagedView<Product>(GetProductList(), _nrItemPage, pageIndex - _nrItemPage, propertyToSort, sortType);
   4:  
   5:    if (!Request.IsAjaxRequest())
   6:        return View("Index", obj);
   7:    else
   8:        return PartialView("ProductCtrl", obj);
   9: }

Ovvero si crea l’oggetto PagedView<Product> , poi nell’eventualità che la richiesta sia di tipo Ajax come nel nostro caso allora si passa tale oggetto come PartialView cioè allo User Control "ProductCtrl", altrimenti se ad esempio il browser non supporta richieste Ajax o i javascript sono disabilitati ci comportiamo come se stessimo effettuando una normale richiesta http renderizzando tutta la view come accade per la prima volta che viene effettuata la richiesta della pagina stessa. Un discorso simile è fatto sugli action link numerici che indicano l'indice delle pagine, con la differenza che l'action invocata è "MovePage" che mouve l'indice della pagina in base alla scelta dell'utente.

Allo stesso modo implementiamo il sorting; infatti nello User Control subito dopo la riga che contiene la navigazione vi è l’intestazione delle colonne della griglia che sono degli Action Link per scatenare appunto l’evento di sorting. Come per il precedente caso la seguente istruzione genera il codice html che permette di effettuare richieste via Ajax alla Action "Sort" del Controller Product passando gli stessi parametri del caso precedente e comportandosi nello stesso modo.

   1: <%= Ajax.ActionLink(
   2:         "CodProduct",
   3:         "Sort",
   4:          new { 
   5:             pageIndex = Model.PageIndex, 
   6:             propertyToSort = "CodProduct", 
   7:             sortType = !Model.IsSortTypeAscending 
   8:             }, 
   9:             new AjaxOptions()
  10:             {
  11:                 UpdateTargetId = "sortContainer",
  12:                 InsertionMode = InsertionMode.Replace
  13:             })%>

Lato Controller invece implementiamo la Action in questione nel seguente modo simile al caso precedente:

   1: public ActionResult Sort(int pageIndex, string propertyToSort, bool sortType)
   2: {
   3:         PagedView<Product> obj = new PagedView<Product>(
   4:                                             GetProductList(),
   5:                                             _nrItemPage, 
   6:                                             pageIndex, 
   7:                                             propertyToSort, 
   8:                                             sortType);
   9:                                             
  10:         if (!Request.IsAjaxRequest())
  11:             return View("Index", obj);
  12:         else
  13:             return PartialView("ProductCtrl", obj);
  14: }

Tutto si traduce nel seguente risultato che è ampiamente personalizzabile:

Result

Naturalmente questo è solo un punto d’inizio il tutto può essere adattato e migliorato i base alle vostre esigenze, ad esempio al posto di una PartialView potremmo restituire un JSON result formattando quindi i dati. A voi la scelta…..

See you soon…

Posted: 13 nov 2009 15:56 da Massimiliano | con no comments
Inserito sotto: , ,