92

What is the most preferred and easiest way to do pagination in ASP.NET MVC? I.e. what is the easiest way to break up a list into several browsable pages.

As an example lets say I get a list of elements from a database/gateway/repository like this:

public ActionResult ListMyItems() { List<Item> list = ItemDB.GetListOfItems(); ViewData["ItemList"] = list; return View(); } 

For simplicity's sake I'd like to specify just a page number for my action as parameter. Like this:

public ActionResult ListMyItems(int page) { //... } 

10 Answers 10

109

Well, what is the data source? Your action could take a few defaulted arguments, i.e.

ActionResult Search(string query, int startIndex, int pageSize) {...} 

defaulted in the routes setup so that startIndex is 0 and pageSize is (say) 20:

 routes.MapRoute("Search", "Search/{query}/{startIndex}", new { controller = "Home", action = "Search", startIndex = 0, pageSize = 20 }); 

To split the feed, you can use LINQ quite easily:

var page = source.Skip(startIndex).Take(pageSize); 

(or do a multiplication if you use "pageNumber" rather than "startIndex")

With LINQ-toSQL, EF, etc - this should "compose" down to the database, too.

You should then be able to use action-links to the next page (etc):

<%=Html.ActionLink("next page", "Search", new { query, startIndex = startIndex + pageSize, pageSize }) %> 
Sign up to request clarification or add additional context in comments.

4 Comments

That's an interesting routing example, so I'll upvote it. I haven't gotten into the groove of using LINQ yet, so Skip and Take is new to me. But that's definately what I need. And that's why I'll mark this as answer.
Using MVC2, your ActionLink syntax is giving me a Compilation Error when requesting the page. CS0103: The name 'startIndex' does not exist in the current context. Is this technique not possible with MVC2?
@comecme do you mean the last line? you need to provide those values (or variables). What is that start-index / page-size?
Yeah, I mean the last line. I thought you are using the current startIndex and add pageSize to it. I'd hoped it would automatically be using the values of the last call to Search. How would I use the startIndex from the last Search in my ActionLink?
22

I wanted to cover a simple way of doing this with the front end too:

Controller:

public ActionResult Index(int page = 0) { const int PageSize = 3; // you can always do something more elegant to set this var count = this.dataSource.Count(); var data = this.dataSource.Skip(page * PageSize).Take(PageSize).ToList(); this.ViewBag.MaxPage = (count / PageSize) - (count % PageSize == 0 ? 1 : 0); this.ViewBag.Page = page; return this.View(data); } 

View:

@* rest of file with view *@ @if (ViewBag.Page > 0) { <a href="@Url.Action("Index", new { page = ViewBag.Page - 1 })" class="btn btn-default"> &laquo; Prev </a> } @if (ViewBag.Page < ViewBag.MaxPage) { <a href="@Url.Action("Index", new { page = ViewBag.Page + 1 })" class="btn btn-default"> Next &raquo; </a> } 

3 Comments

var data = this.dataSource.Skip(page * PageSize).Take(PageSize).ToList(); Requires a orderBy(o => o.Id) before you can use skip() || Besides that this is a great answer that deserves a lot more upvotes.
Great Answer! If you want to make the page start at 1 instead of 0, use this: var data = this.dataSource.Skip((page - 1) * PageSize).Take(PageSize).ToList();
the simplest answer in understanding and applying
16

I had the same problem and found a very elegant solution for a Pager Class from

http://blogs.taiga.nl/martijn/2008/08/27/paging-with-aspnet-mvc/

In your controller the call looks like:

return View(partnerList.ToPagedList(currentPageIndex, pageSize)); 

and in your view:

<div class="pager"> Seite: <%= Html.Pager(ViewData.Model.PageSize, ViewData.Model.PageNumber, ViewData.Model.TotalItemCount)%> </div> 

2 Comments

It's for ASP.NET MVC Preview 5. Will it work for ASP.NET MVC Beta?
Link changed, code updated to RC1 (guess it will work with 1.0 too, will try out now). blogs.taiga.nl/martijn/2008/08/27/paging-with-aspnet-mvc
7

Here's a link that helped me with this.

It uses PagedList.MVC NuGet package. I'll try to summarize the steps

  1. Install the PagedList.MVC NuGet package

  2. Build project

  3. Add using PagedList; to the controller

  4. Modify your action to set page public ActionResult ListMyItems(int? page) { List list = ItemDB.GetListOfItems(); int pageSize = 3; int pageNumber = (page ?? 1); return View(list.ToPagedList(pageNumber, pageSize)); }

  5. Add paging links to the bottom of your view @*Your existing view*@ Page @(Model.PageCount < Model.PageNumber ? 0 : Model.PageNumber) of @Model.PageCount @Html.PagedListPager(Model, page => Url.Action("Index", new { page, sortOrder = ViewBag.CurrentSort, currentFilter = ViewBag.CurrentFilter }))

1 Comment

I know this is an old question which is why this answer doesn't have many upvotes. But it should since this is the best modern solution. PagedList Demo
2

Controller

 [HttpGet] public async Task<ActionResult> Index(int page =1) { if (page < 0 || page ==0 ) { page = 1; } int pageSize = 5; int totalPage = 0; int totalRecord = 0; BusinessLayer bll = new BusinessLayer(); MatchModel matchmodel = new MatchModel(); matchmodel.GetMatchList = bll.GetMatchCore(page, pageSize, out totalRecord, out totalPage); ViewBag.dbCount = totalPage; return View(matchmodel); } 

BusinessLogic

 public List<Match> GetMatchCore(int page, int pageSize, out int totalRecord, out int totalPage) { SignalRDataContext db = new SignalRDataContext(); var query = new List<Match>(); totalRecord = db.Matches.Count(); totalPage = (totalRecord / pageSize) + ((totalRecord % pageSize) > 0 ? 1 : 0); query = db.Matches.OrderBy(a => a.QuestionID).Skip(((page - 1) * pageSize)).Take(pageSize).ToList(); return query; } 

View for displaying total page count

 if (ViewBag.dbCount != null) { for (int i = 1; i <= ViewBag.dbCount; i++) { <ul class="pagination"> <li>@Html.ActionLink(@i.ToString(), "Index", "Grid", new { page = @i },null)</li> </ul> } } 

Comments

2

I think the easiest way to create pagination in ASP.NET MVC application is using PagedList library.

There is a complete example in following github repository. Hope it would help.

public class ProductController : Controller { public object Index(int? page) { var list = ItemDB.GetListOfItems(); var pageNumber = page ?? 1; var onePageOfItem = list.ToPagedList(pageNumber, 25); // will only contain 25 items max because of the pageSize ViewBag.onePageOfItem = onePageOfProducts; return View(); } } 

Demo Link: http://ajaxpagination.azurewebsites.net/

Source Code: https://github.com/ungleng/SimpleAjaxPagedListAndSearchMVC5

Comments

1

Entity

public class PageEntity { public int Page { get; set; } public string Class { get; set; } } public class Pagination { public List<PageEntity> Pages { get; set; } public int Next { get; set; } public int Previous { get; set; } public string NextClass { get; set; } public string PreviousClass { get; set; } public bool Display { get; set; } public string Query { get; set; } } 

HTML

<nav> <div class="navigation" style="text-align: center"> <ul class="pagination"> <li class="page-item @Model.NextClass"><a class="page-link" href="?page=@(@[email protected])">&laquo;</a></li> @foreach (var item in @Model.Pages) { <li class="page-item @item.Class"><a class="page-link" href="?page=@([email protected])">@item.Page</a></li> } <li class="page-item @Model.NextClass"><a class="page-link" href="?page=@(@[email protected])">&raquo;</a></li> </ul> </div> </nav> 

Paging Logic

public Pagination GetCategoryPaging(int currentPage, int recordCount, string query) { string pageClass = string.Empty; int pageSize = 10, innerCount = 5; Pagination pagination = new Pagination(); pagination.Pages = new List<PageEntity>(); pagination.Next = currentPage + 1; pagination.Previous = ((currentPage - 1) > 0) ? (currentPage - 1) : 1; pagination.Query = query; int totalPages = ((int)recordCount % pageSize) == 0 ? (int)recordCount / pageSize : (int)recordCount / pageSize + 1; int loopStart = 1, loopCount = 1; if ((currentPage - 2) > 0) { loopStart = (currentPage - 2); } for (int i = loopStart; i <= totalPages; i++) { pagination.Pages.Add(new PageEntity { Page = i, Class = string.Empty }); if (loopCount == innerCount) { break; } loopCount++; } if (totalPages <= innerCount) { pagination.PreviousClass = "disabled"; } foreach (var item in pagination.Pages.Where(x => x.Page == currentPage)) { item.Class = "active"; } if (pagination.Pages.Count() <= 1) { pagination.Display = false; } return pagination; } 

Using Controller

public ActionResult GetPages() { int currentPage = 1; string search = string.Empty; if (!string.IsNullOrEmpty(Request.QueryString["page"])) { currentPage = Convert.ToInt32(Request.QueryString["page"]); } if (!string.IsNullOrEmpty(Request.QueryString["q"])) { search = "&q=" + Request.QueryString["q"]; } /* to be Fetched from database using count */ int recordCount = 100; Place place = new Place(); Pagination pagination = place.GetCategoryPaging(currentPage, recordCount, search); return PartialView("Controls/_Pagination", pagination); } 

1 Comment

What is "Place" class?
1
public ActionResult Paging(int? pageno,bool? fwd,bool? bwd) { if(pageno!=null) { Session["currentpage"] = pageno; } using (HatronEntities DB = new HatronEntities()) { if(fwd!=null && (bool)fwd) { pageno = Convert.ToInt32(Session["currentpage"]) + 1; Session["currentpage"] = pageno; } if (bwd != null && (bool)bwd) { pageno = Convert.ToInt32(Session["currentpage"]) - 1; Session["currentpage"] = pageno; } if (pageno==null) { pageno = 1; } if(pageno<0) { pageno = 1; } int total = DB.EmployeePromotion(0, 0, 0).Count(); int totalPage = (int)Math.Ceiling((double)total / 20); ViewBag.pages = totalPage; if (pageno > totalPage) { pageno = totalPage; } return View (DB.EmployeePromotion(0,0,0).Skip(GetSkip((int)pageno,20)).Take(20).ToList()); } } private static int GetSkip(int pageIndex, int take) { return (pageIndex - 1) * take; } @model IEnumerable<EmployeePromotion_Result> @{ Layout = null; } <!DOCTYPE html> <html> <head> <meta name="viewport" content="width=device-width" /> <title>Paging</title> </head> <body> <div> <table border="1"> @foreach (var itm in Model) { <tr> <td>@itm.District</td> <td>@itm.employee</td> <td>@itm.PromotionTo</td> </tr> } </table> <a href="@Url.Action("Paging", "Home",new { pageno=1 })">First page</a> <a href="@Url.Action("Paging", "Home", new { bwd =true })"><<</a> @for(int itmp =1; itmp< Convert.ToInt32(ViewBag.pages)+1;itmp++) { <a href="@Url.Action("Paging", "Home",new { pageno=itmp })">@itmp.ToString()</a> } <a href="@Url.Action("Paging", "Home", new { fwd = true })">>></a> <a href="@Url.Action("Paging", "Home", new { pageno = Convert.ToInt32(ViewBag.pages) })">Last page</a> </div> </body> </html> 

Comments

1

It is possible to create a reusable pagination. What we need is:

  • partial view to store number of pages and links to them
  • reusable methods which contains pagination logic for IQueryable

The complete example with source code can be seen here.

So our code would like this:

The person table. I've used SQL Server:

IF NOT EXISTS ( SELECT 1 FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = 'Person' AND TABLE_SCHEMA = 'dbo' ) BEGIN CREATE TABLE dbo.Person ( ID INT IDENTITY(1, 1) NOT NULL PRIMARY KEY , CreateDate DATETIME NOT NULL DEFAULT GETDATE() , Creator VARCHAR(100) NOT NULL , ModifyDate DATETIME NULL , Modifier VARCHAR(20) NULL , FirstName VARCHAR(150) NOT NULL , LastName VARCHAR(1000) NOT NULL ) ON [PRIMARY] END GO 

I've used DatabaseFirst approach. This is a generated class by Entity Framework.

public partial class Person { public int ID { get; set; } public System.DateTime CreateDate { get; set; } public string Creator { get; set; } public string FirstName { get; set; } public string LastName { get; set; } public Nullable<System.DateTime> ModifyDate { get; set; } public string Modifier { get; set; } } 

This is a class which contains data and pagination information:

public class DataResultViewModel<T> { /// <summary> /// Data items /// </summary> public IEnumerable<T> Items { get; set; } /// <summary> /// Pagination /// </summary> public Pagination Pagination { get; set; } } 

This is class which contains information about persons.

public class PersonViewModel { public int ID { get; set; } public string FirstName { get; set; } public string LastName { get; set; } } /// <summary> /// What route should be requested while paging /// </summary> public class RouteInfo { /// <summary> /// Name of controller /// </summary> public string ControllerName { get; set; } /// <summary> /// Name of action /// </summary> public string ActionName { get; set; } } 

and Pagination class:

public class Pagination { /// <summary> /// Total count of items /// </summary> public int TotalItems { get; set; } /// <summary> /// Count of items at the page /// </summary> public int PageSize { get; set; } = 5; /// <summary> /// Current page /// </summary> public int Page { get; set; } /// <summary> /// Sorted by field name /// </summary> public string SortBy { get; set; } /// <summary> /// Is this an ascending sort? /// </summary> public bool IsSortAscending { get; set; } /// <summary> /// Information about what page should be requested /// </summary> public RouteInfo RouteInfo { get; set; } } 

And the class which makes pagination. Basically, Skip() and Take() are methods which do a pagination, so we need to use these methods for all IQueryable. And C# has a very neat feature called extension methods. Extension methods allow to reuse code:

public static class IQueryableExtension { public static IQueryable<T> UseOrdering<T, TResultSelector>(this IQueryable<T> query, Pagination pagination, Expression<Func<T, TResultSelector>> field) { if (string.IsNullOrWhiteSpace(pagination.SortBy) || string.IsNullOrEmpty(pagination.SortBy)) return query; return pagination.IsSortAscending ? query.OrderBy(field) : query.OrderByDescending(field); } public static IQueryable<T> UsePagination<T>(this IQueryable<T> query, Pagination pagination) { if (pagination.Page <= 0) pagination.Page = 1; if (pagination.PageSize <= 0) pagination.PageSize = 10; return query.Skip((pagination.Page - 1) * pagination.PageSize) .Take(pagination.PageSize); } } 

This is a class of service layer:

public class PersonService { public DataResultViewModel<PersonViewModel> GetWithPagination(Pagination pagination, Expression<Func<Person, DateTime>> fieldName) { var result = new DataResultViewModel<PersonViewModel>(); using (var db = new MiscellaneousEntities()) { var persons = db.Person.AsQueryable(); result.Pagination = pagination; result.Pagination.TotalItems = persons.Count(); result.Pagination.RouteInfo = new RouteInfo() { ActionName = "Index", ControllerName = "Person" }; if (pagination.SortBy == null) pagination.SortBy = "CreateDate"; persons = persons.UseOrdering(pagination, fieldName); persons = persons.UsePagination(pagination); result.Items = persons .Select(s => new PersonViewModel() { ID = s.ID, FirstName = s.FirstName, LastName = s.LastName }) .ToList(); return result; } } } 

And controller of person:

public class PersonController : Controller { PersonService _personService; public PersonController() { _personService = new PersonService(); } // GET: Person public ActionResult Index(int? page, int? pageSize, string sortBy, bool? isSortAscending) { return View (_personService.GetWithPagination(new Pagination() { Page = page ?? 1, PageSize = pageSize ?? 3, SortBy = sortBy, IsSortAscending = isSortAscending ?? false }, v => v.CreateDate ) ); } } 

Then we should create views.

This is a person view that should be paginated:

@model OnlyPagination.ViewModel.DataResultViewModel<OnlyPagination.ViewModel.PersonViewModel> <div class="d-flex justify-content-center"> <div> @foreach (var item in Model.Items) { <div> <p>Id is @item.ID</p> <p>FirstName is @item.FirstName</p> <p>LastName is @item.LastName</p> </div> <hr /> } </div> </div> <div class="d-flex justify-content-center"> @{ @Html.Partial("Pagination", Model.Pagination) } </div> 

And it is a reusable pagination partial view:

@model OnlyPagination.Extensions.Query.Model.Pagination @{ var pagesCount = Math.Ceiling((decimal)Model.TotalItems / (decimal)Model.PageSize); var pages = new List<int>(); for (var i = 1; i <= pagesCount; i++) { pages.Add(i); } } <div> <p class="d-flex justify-content-center">Page @Model.Page of @pagesCount</p> <ul class="pagination"> <li class="page-item @( Model.Page == 1 ? "disabled" : "" )"> <a aria-label="Previous" class="page-link" href="@Url.Action (Model.RouteInfo.ActionName, Model.RouteInfo.ControllerName, new { page = Model.Page - 1, pageSize = Model.PageSize })"> <span aria-hidden="true">&laquo;</span> </a> </li> @for (int pageNumber = 1; pageNumber <= pages.Count; pageNumber++) { <li class="page-item @( Model.Page == pageNumber ? "active" : "" )"> <a class="page-link" href="@Url.Action(Model.RouteInfo.ActionName, Model.RouteInfo.ControllerName, new { page = pageNumber, pageSize = Model.PageSize })"> @pageNumber </a> </li> } <li class="page-item @(Model.Page == pages.Count ? "disabled" : "")"> <a aria-label="Next" class="page-link" href="@Url.Action (Model.RouteInfo.ActionName, Model.RouteInfo.ControllerName, new { page = Model.Page + 1, pageSize = Model.PageSize })"> <span aria-hidden="true">&raquo;</span> </a> </li> </ul> </div> 

That's all!

Comments

0

In the ASP .NET application to insert page pagination, first need to install Paged List and PagedList.MVC from the NuGet packages for the project.

Then I have included sample method which return the list of books from database. And I included the Page pagination to show 4 items in each page.

public ActionResult Books(int? i) { IEnumerable<Books> BooksList; HttpResponseMessage response = GlobalVariables.webApiClient.GetAsync("Tbl_Books").Result; BooksList = response.Content.ReadAsAsync<IEnumerable<Books>>().Result; return View(BooksList.ToList().ToPagedList(i ?? 1, 4)); } 

In the View page, this should be included.

@Html.PagedListPager(Model, i => Url.Action("Books", "Library", new { i })) 

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.