How do I do pagination in ASP.NET MVC?

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)
{
   //...
}

Answers:

Thank you for visiting the Q&A section on Magenaut. Please note that all the answers may not help you solve the issue immediately. So please treat them as advisements. If you found the post helpful (or not), leave a comment & I’ll get back to you as soon as possible.

Method 1

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 }) %>

Method 2

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(" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener"Index", new { page = ViewBag.Page - 1 })" 
       class="btn btn-default">
        &laquo; Prev
    </a>
}
@if (ViewBag.Page < ViewBag.MaxPage)
{
    <a href="@Url.Action(" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener"Index", new { page = ViewBag.Page + 1 })" 
       class="btn btn-default">
        Next &raquo;
    </a>
}

Method 3

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>

Method 4

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 }))

Method 5

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>
        }
    }

Method 6

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

Method 7

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="<a href="https://getridbug.com/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="a996d9c8cecc94e9">[email protected]</a>(@<a href="https://getridbug.com/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="5b16343f3e37750b293e2d32342e28701b16343f3e37750a2e3e2922">[email protected]</a>)" rel="nofollow noreferrer noopener">&laquo;</a></li>
            @foreach (var item in @Model.Pages)
            {
                <li class="page-item @item.Class"><a class="page-link" href="<a href="https://getridbug.com/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="241b544543411964">[email protected]</a>(<a href="https://getridbug.com/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="355c4150581b655452501e75785a5150591b644050474c">[email protected]</a>)" rel="nofollow noreferrer noopener">@item.Page</a></li>
            }
            <li class="page-item @Model.NextClass"><a class="page-link" href="<a href="https://getridbug.com/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="0b347b6a6c6e364b">[email protected]</a>(@<a href="https://getridbug.com/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="75381a1110195b3b100d015e35381a1110195b240010070c">[email protected]</a>)" rel="nofollow noreferrer noopener">&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);
}

Method 8

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(" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener"Paging", "Home",new { pageno=1 })">First  page</a> 
    <a href="@Url.Action(" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener"Paging", "Home", new { bwd =true })"><<</a> 
    @for(int itmp =1; itmp< Convert.ToInt32(ViewBag.pages)+1;itmp++)
   {
       <a href="@Url.Action(" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener"Paging", "Home",new { pageno=itmp   })">@itmp.ToString()</a>
   }
    <a href="@Url.Action(" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener"Paging", "Home", new { fwd = true })">>></a> 
    <a href="@Url.Action(" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener" rel="nofollow noreferrer noopener"Paging", "Home", new { pageno =                                                                               Convert.ToInt32(ViewBag.pages) })">Last page</a> 
</div>
   </body>
  </html>

Method 9

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!

Method 10

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 }))


All methods was sourced from stackoverflow.com or stackexchange.com, is licensed under cc by-sa 2.5, cc by-sa 3.0 and cc by-sa 4.0

0 0 votes
Article Rating
Subscribe
Notify of
guest

0 Comments
Inline Feedbacks
View all comments
0
Would love your thoughts, please comment.x
()
x