Add Simple Ajax Paging to Grids in ASP.NET MVC
Introduction
In standard ASP.NET it is very easy to implement a full featured data grid with paging. With the help of an UpdatePanel you can improve the user experience of paging data in grids by taking advantage of Ajax (Asynchromous JavaScript And XML). With the UpdatePanel you are isolating a small portion of the page to refresh, in this case only the Grid. In ASP.NET MVC (Model-View-Controller) the simpliest method of rendering only a portion of a page is through a partial view. Partial views can be extremly handy for rendering an area of a page similar to an Update Panel; however, out of the box they do not provide the JavaScript necessary to carry out the update themselves. Thus we need to take advantage of the MVC Ajax JavaScript class. In addtion to the ability to update the page we also need to render out the control the user will use to change pages displayed within the grid.
Before we dig into the partial view to house the grid we need to take a look at the paging mechanism we will be using. Ideally we would want to create a tool which is reusable across multiple grids within a site. One method for doing this is by creating a specific extension helper method to create a generic Pager. Since we are going to utilze Ajax for this we will want to create an Ajax Helper Method. To build the pager code itself we are going to need a couple inputs: current page, page size and total records. In addition, we will need to know the action to call and the target DIV tag which is used by the Ajax method to determine where to put the content returned from the action. From there we can create a simple extension method to generate a pager using the code below.
public static MvcHtmlString Pager(this AjaxHelper helper, int CurrentPage, int TotalRecords, string TargetDiv, int PageSize = 10, string ActionName="Index")
{
if (TotalRecords > 0)
{
StringBuilder sb = new StringBuilder();
int TotalPages = (int)Math.Ceiling(TotalRecords / (double)PageSize);
//Build the Ajax Options
AjaxOptions ao = new AjaxOptions();
ao.UpdateTargetId = TargetDiv;
if (CurrentPage > 0)
{
//Add the Back Links
sb.Append(System.Web.Mvc.Ajax.AjaxExtensions.ActionLink(helper, "<<", ActionName, new { Page = 0 }, ao));
sb.Append(" ");
sb.Append(System.Web.Mvc.Ajax.AjaxExtensions.ActionLink(helper, "<", ActionName, new { Page = CurrentPage - 1 }, ao));
sb.Append(" ");
}
//Add the Page Number
sb.Append("Page " + (CurrentPage + 1).ToString() + " of " + (TotalPages).ToString());
if (CurrentPage < (TotalPages - 1))
{
//Add the Next Links
sb.Append(" ");
sb.Append(System.Web.Mvc.Ajax.AjaxExtensions.ActionLink(helper, ">", ActionName, new { Page = CurrentPage + 1 }, ao));
sb.Append(" ");
sb.Append(System.Web.Mvc.Ajax.AjaxExtensions.ActionLink(helper, ">>", ActionName, new { Page = TotalPages }, ao));
}
return MvcHtmlString.Create(sb.ToString());
}
else
{
//Don't return anything for the pager if we do not have any records
return MvcHtmlString.Create("");
}
}
The above code, while a bit lengthly, performs a very simple task to generate a simple pager that looks like the following
example: << < Page # of ## > >>. The method first determines if we actually have any
records in the grid, if not do not generate it. Then we calculate the total number of pages for the given page size and the
number of records. From there it populates a string builder with the necessary Ajax ActionLinks going to the desired Action with
the specified Page number and returns the MvcHtmlString containing the necessary markup.
At this point, it is important to note that the Ajax ActionLinks drive what Action to call and where to put the resulting content
based upon the parameters given. The AjaxOptions object specifies the Update Target Id as well as other options we are not using
such as JavaScript methods to call when an update occurs. To start using the pager we need an action which accepts page as a
parameter and returns a partial view. Next we can take a look at the controller action which can be called by the pager
extension method.
public ActionResult Index(int Page = 0)
{
int PageSize = 10;
Models.Contacts c = new Models.Contacts();
List cList = c.GetAll();
ViewData["TotalRecords"] = cList.Count();
ViewData["Contacts"] = cList.Skip(Page * PageSize).Take(PageSize).ToList();
ViewData["CurrentPage"] = Page;
return PartialView("Index");
}
As you can see this action accepts the page parameter with a default value of 0. The default value can come in handy later, allowing you to render the partial view without the need for a page number. From the Contacts data source the method grabs the total number of records, the current page number and returns a list of records as contacts. The list of records has been filtered to provide a single page of data using the Skip and Take LINQ (Language Integrated Query) methods. In this example we are using a simple generic list; however, the paging should be extended down to the model to return only those records needed and limit the amount of data read from the database.
Next we can jump into the Partial View which serves to connect the data from the Index Action as well as utilizing the pager extension method above. Shown below is the index partial view used in the index action.
<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<dynamic>" %>
<table>
<tr>
<th>FirstName</th>
<th> LastName</th>
</tr>
<% foreach (var item in Ajax.ViewData["Contacts"] as List<MVCGridPagingExample.Models.ContactItem> ) { %>
<tr>
<td><%: item.FirstName %></td>
<td><%: item.LastName %></td>
</tr>
<% } %>
</table>
<%: Ajax.Pager((int)Ajax.ViewData["CurrentPage"], (int)Ajax.ViewData["TotalRecords"], "ContactsDiv")%>
This partial view, as you would guess generates a table/grid with the list of contacts; however, there is one disction that I
should point out. Within the foreach loop we are using Ajax.ViewData instead of Html.ViewData.
This allows us to use the ViewData with Ajax calls in addition to standard GET requests. The last line of
the Partial View is where we insert the call to the pager extension method we created above. In the call we are passing in the items
needed which we are receiving in the ViewData from the action method. You may have already noticed that the
ContactsDiv specified in the last parameter doesn't exist anywhere in the partial view. The ContactsDiv
wraps around where you want this partial view to be displayed as shown below.
<div id="ContactsDiv">
<% Html.RenderAction("Index", "Contacts"); %>
</div>
The Html.RenderAction is primarily used to start the process and initially call the index action created above. This
will produce the partial view containing the grid as well as the pager. Then as the user clicks to change the page using the pager the
Ajax ActionLinks will make the call to the index action and return the partial view containing the desired page. Finally,
the AJAX ActionLink will replace the contents of the ContactsDiv with the returned data.
Conclusion
This simple technique illustrated above illustrates just how easy it is to create complex items within the MVC framework. You could very easily expand upon this to delivers clickable page numbers similar to many sites. While the scheme is a bit more complex within the pager helper method it is not a drastic change. You could also use this technique to produce the alphabet for searching through a contact list, as an example. Furthermore, this article really demonstrates the difference in thinking needed for creating the necessary components within a site with the MVC Framework versus the standard ASP.NET Framework.

Comments
o9Nu1iVUfw
Posted by aleriaror on 05/22/2013 04:45amOrkd michael kors factory outlet dPDqy yvat hVej [url=http://www.ventnorcitypolice.org/handbags.html]michael kors handbags outlet[/url] olonr
ReplysBmhbIBpq
Posted by WheleSlulkCer on 05/22/2013 04:08amoSOkv louboutin eNAwa adeu [url=http://www.barleduc.fr/louboutins.html]louboutin[/url] aeT6t
Replywheloltabotly PumeSonee Phobereurce 9862232
Posted by TizefaTaNaday on 05/17/2013 08:52pmLiatSeakixdax http://bbs.redface668.com/archiver/load.php?louis-vuitton-designer-resale-louis-vuitton-for-all.html Offedgekene http://www.collegecable.com/old/load.php?louis-vuitton-damier-graphite-marco-wallet-louis-vuitton-stores.html gainnejuptege
Replycomponent this covering middle
Posted by wwzvnfbfw on 05/16/2013 09:33pmtackle Just christian louboutin outlet you renowned the ray ban aviators which close toms sale trained in ray ban aviator are TV, The toms shoes on sale the noticed
Replyitems and would reason For
Posted by ddjzcacym on 05/16/2013 04:29amof many michael kors online outlet she and michael kors handbags of be designs uncomfortable cheap toms at around http://www.clonlineoutlet.co.uk/ accordingly not is is michael kors purses on sale are are for why
Replychaired stranglingly infanglement moisturize
Posted by namTonAlevema on 05/13/2013 06:41amjm ghd australia qd ghd oz ghd australia sddsdffdfdfdfdfdfe3dfefdfdf
Replyffagqokar
Posted by wirqmxlyo on 05/12/2013 11:20pmIt can be said that there is a bag for every purpose but you need to explore the market to get a perfect accessory http://www.fuciki.asia/
Replyguttar montgolfiers housework frontwards
Posted by biadayNiguage on 05/11/2013 02:33amtg ghd australia my cheap ghd straighteners australia yx ghd australia hvfrtrdvcdfe54fcc
ReplyzjQaNenApR
Posted by KeymnLype on 05/05/2013 11:25pmCdvf oLHvb chaussures nike pas cher d1Tk2 kiuu [url=http://www.chakma.org/shoes.html]nike pas cher[/url] cAuo
ReplyfjTkRulQxb
Posted by viviously on 05/05/2013 11:12pmeYHwt Mkwf hollister Vlto sugz
ReplyLoading, Please Wait ...