ASP.NET MVC – Pagination View User Control

Each website, content management system, blog and so on need "pagination". A never ending site with "foo-entries" is not very professional. Thats why i created for my ASP.NET MVC sample (a kigg / dotnetnuke similar page) a specific"Pagination View User Control"

In almost every "List" View I implemented my control:

image

image

The advantages of such an control:

- look and feel is on each page the same 
- changes are very easy to implement

In my sampel I use this CSS design.

Structure

image 

A typical MVC View User Control

Code

Pagination.ascx

<%@ Control Language="C#" AutoEventWireup="true" CodeBehind="Pagination.ascx.cs" Inherits="Mvc2.Views.Shared.Pagination" %>
<ul class="pagination-clean">
    <% if (ViewData.HasPreviousPage)
        { %>
          <li class="previous"><a href="<%=ViewData.PageActionLink.Replace("%7Bpage%7D", (ViewData.PageIndex - 1).ToString())%>">« Previous</a></li>
     <% }
       else
        { %>
          <li class="previous-off">« Previous</li>
     <% } %>

     <%for (int page = 1; page <= ViewData.TotalPages; page++)
        {
        if (page == ViewData.PageIndex)
            { %>
              <li class="active"><%=page.ToString()%></li>
         <% }
        else
            { %>
              <li><a href="<%=ViewData.PageActionLink.Replace("%7Bpage%7D", page.ToString())%>"><%=page.ToString()%></a></li>
         <% }
        } 

       if (ViewData.HasNextPage)
            { %>
              <li class="next"><a href="<%=ViewData.PageActionLink.Replace("%7Bpage%7D", (ViewData.PageIndex + 1).ToString())%>">Next »</a></li>
         <% }
       else
            { %>
               <li class="next-off">Next »</li>
         <% } %>
</ul> 

Pagination.ascx.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;

namespace Mvc2.Views.Shared
{
    public class PaginationViewData
    {
        public int PageIndex { get; set; }
        public int TotalPages { get; set; }
        public int PageSize { get; set; }
        public int TotalCount { get; set; }
        public string PageActionLink { get; set; }
        public bool HasPreviousPage
        {
            get
            {
                return (PageIndex > 1);
            }
        }

        public bool HasNextPage
        {
            get
            {
                return (PageIndex * PageSize) <= TotalCount;
            }
        }
    }

    public partial class Pagination : System.Web.Mvc.ViewUserControl<PaginationViewData>
    {
        public Pagination()
        {

        }
    }
}

The ViewData is strongly typed by the PaginationViewData class.

How to use – Helper "PagedList"

It is very easy to use, if you are already use the PageList class:

using System;
using System.Data;
using System.Configuration;
using System.Linq;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.HtmlControls;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Xml.Linq;
using System.Collections.Generic;

namespace Mvc2.Helpers
{
    public class PagedList<T> : List<T>
    {
        public PagedList(IQueryable<T> source, int index, int pageSize)
        {
            this.TotalCount = source.Count();
            this.PageSize = pageSize;
            this.PageIndex = index;
            this.AddRange(source.Skip((index - 1) * pageSize).Take(pageSize).ToList());

            int pageResult = 0;
            for (int counter = 1; pageResult < this.TotalCount; counter++)
            {
                pageResult = counter * this.PageSize;
                this.TotalPages = counter;
            }
        }

        public int TotalPages
        {
            get;
            set;
        }

        public int TotalCount
        {
            get;
            set;
        }

        public int PageIndex
        {
            get;
            set;
        }

        public int PageSize
        {
            get;
            set;
        }

        public bool HasPreviousPage
        {
            get
            {
                return (PageIndex > 1);
            }
        }

        public bool HasNextPage
        {
            get
            {
                return (PageIndex * PageSize) <= TotalCount;
            }
        }
    }

    public static class Pagination
    {
        public static PagedList<T> ToPagedList<T>(this IQueryable<T> source, int index, int pageSize)
        {
            return new PagedList<T>(source, index, pageSize);
        }

        public static PagedList<T> ToPagedList<T>(this IQueryable<T> source, int index)
        {
            return new PagedList<T>(source, index, 10);
        }
    }
}

The original code wrote Rob Conery – in my version is the first page the number 1, not 0 – a user could confused by the link to page number 0 ;)

How to use- in the view page

I render the control by using the in-build "Html.RenderUserControl" helper:

    <%=Html.RenderUserControl("~/Views/Shared/Pagination.ascx", new Mvc2.Views.Shared.PaginationViewData()
      {
          PageIndex = ViewData.EntryList.PageIndex,
          TotalPages = ViewData.EntryList.TotalPages,
          PageActionLink = Url.Action("List","Entry", new { category = ViewData.Category, page = "{page}"}),
          TotalCount = ViewData.EntryList.TotalCount,
          PageSize = ViewData.EntryList.PageSize
      }, null)%>

The properties of the control and of the PagedList are almost the same – only the "PageActionLink" is a specific property. The "PageActionLink" is the action Link to your controller – "/Management/Tag/2" route to your "Management"-Controller and "Tag"-ActionMethod with the page parameter. The "page = {page}" parameter is only a template for the real page number, which will be replaced in my control:

<li><a href="<%=ViewData.PageActionLink.Replace("%7Bpage%7D", page.ToString())%>"><%=page.ToString()%></a></li>

The URL helper method encode the { and } to %7B and %7D. I can´t explain it very well in english – one quick look in the source code (pagination.ascx) is very helpful ;)

Feel free to use it.

PS: English blogging is hard :/ ;)

If you enjoyed this post, please consider leaving a comment or subscribing to the RSS feed to have future articles delivered to your feed reader.

About the author

Written by Code Inside Team

Currently there is no additional info about this author.

44 Responses

  1. Nice work, Robert. This is something I (and I’m sure many other MVC developers) will use often.

    Oh, and your English was fine. =)

    Reply
  2. Oh, and on another note, coincidentally I have made the same changes to Rob’s PagedList class as you have, but I think this is a bit better of a calculation for TotalPages:

    (excuse the formatting please)

    if( this.TotalCount > 0 )
    {
    this.TotalPages = (int)Math.Ceiling( (double)this.TotalCount / (double)this.PageSize );
    }
    else
    {
    this.TotalPages = 0;
    }

    Reply
  3. Can you post your controller code to complete this blog post so we can see how you are interacting with that. Sure, I can “imagine” sort of what you’re doing such as adding a page param in your action like you said, and imagine up your controller based on your route but would like to see the actual action, so I have a little more clear context.

    Would be a big help.

    Reply
  4. Just wanted to say thanks – I’m new to MVC and this was a great help for paging! Cheers! :)

    Reply
  5. The controller code:
    You just have to pass a PagedList to your Viewdata like that:
    ListViewData list = new ListViewData();
    list.EntryList = EntryManager.LoadEntryCollectionByCategory(category).ToPagedList(page ?? 1);

    Hope this helps :)

    Reply
  6. I think that when you changed your page numbers to have a base of one rather than zero you need to change HasNextPage to be
    get { return (PageIndex * PageSize) < TotalCount; }

    Eg change <= to <

    Reply
  7. I’m trying with MVC Preview 3 but at the view form the system do not find ViewData.EntryList. Where is it?

    Reply
  8. You need a strongly typed ViewData class – EntryList is just a sample property in this ViewDataClass.
    Here is a nice blogpost about strongly typed ViewData classes:
    http://blog.codeville.net/2008/02/21/aspnet-mvc-making-strongly-typed-viewpages-more-easily/

    Reply
  9. Hi there, great code!!! Just want to address one small issue, when pages exceeds the default total page and next is clicked, it won’t show the next set of pages.

    Here is what I did. Hope if someone else has the same problem can make use of it.

    public int StartIndex
    {
    get
    {
    if (PageIndex > TotalPages)
    {
    return PageIndex – (PageIndex % TotalPages) + 1;
    }
    else
    return 1;
    }
    }

    and then, change the for loop to
    for (int page = ViewData.Model.StartIndex; page <= (ViewData.Model.StartIndex + ViewData.Model.TotalPages – 1); page++)

    If it is still not cleared. Email me royheh@comcast.net

    Reply
  10. Nice Blog. :)

    Reply
  11. Hi, I’m having a hard time trying to find the RenderUserControl function.

    It’s not in HtmlHelper class

    Cheers

    Reply
  12. The “RenderUserControl” was in the Beta 3 days – maybe it has be renamed or you can find it in the MVCFuture bits (download @ codeplex.com/aspnet ).

    Reply
  13. I finally found the solution. It’s called RenderPartial now. It takes the control name and ViewData as arguments.

    Thanks for answering!

    Gonzalo

    Reply
  14. PagedList can use IEnumerable instead of IQueryable (more generic). Cheers!!

    Reply
  15. Nevermind, I see the benefit of having an IQueryable ctor, but IEnumerable is still a good option to have ;-)

    Reply
  16. Love the advice. Thank you.

    Reply
  17. Hello, I have a question about what my view page should inherit from. Im getting the following error:
    Parser Error Message: ‘Mvc2.Views.Shared.Pagination’ is not allowed here because it does not extend class ‘System.Web.Mvc.ViewPage’.

    Source Error:


    Line 1: <%@ Page Title="List Checkups" Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="Mvc2.Views.Shared.Pagination" %>
    Line 2:
    Line 3: <asp:Content ID="Content1" ContentPlaceHolderID="TitleContent" runat="server">

    Is there something I am leaving out? Ive added the Inherits=”Mvc2.Views.Shared.Pagination” At the top of my page, yet I still get this error. I am new to MVC. I would really like to get this working. Thanks for any help!

    Reply
  18. can anybody post the sample downloadble code for testing? please?
    thanks.

    Reply
  19. please … link a downloadble example thanks

    Reply
  20. Hello,

    This is very useful stuff.

    Thanks.

    Reply
  21. I’m very new to mvc.  do you have a “test” solution that you can post? thanks!

    Reply
  22. hello,

    i m not getting the viewdata on the user control,please tell me from where can I get this,but the post is good

    Reply
  23. Everybody for me source sample of this topic?
    I very need source sample.
    Email: tavtho@gmail.com
    please?
    thank!!!

    Reply
  24. Leiber Robert – Vielen Dank fuer deine Poste! Jawohl ist Blogging auf Englisch sehr schwer!

    Reply
  25. Leaning ASP.NET MVC 2. Will appreciate if you post a link of working solution for download.

    Reply
  26. I wrote a free asp.net mvc paging component called MvcPager,it support both basic url paging and Ajax paging using jQuery or MicrosoftAjax script library,you can view online demo and download a copy from http://en.webdiyer.com .

    Reply
  27. Thank you, I will be using this in my next project :)
    Vielen Dank und gruss aus Israel
    Moshe

    Reply
  28. I wrote a free asp.net mvc paging component called MvcPager,it has more features and support Ajax paging using MicrosoftAjax or jQuery script library,you can view online demo and download it from http://en.webdiyer.com/mvcpager

    Reply
  29. I’m learning mvc, it very nice tutorial, thanks!

    Reply
  30. I dont understand how it works in controller.
    Can you please explain what you meen with:

    ListViewData list = new ListViewData();
    list.EntryList = EntryManager.LoadEntryCollectionByCategory(category).ToPagedList(page ?? 1);

    Thanks a lot and take care,
    Ragims

    Reply
  31. Hi Robert,
    please share your code !

    Reply
  32. Don’t forget the css please , some people will like to have the css too.
    But this is great stuff by the way ;) .

    Reply
  33. Do you send for me example code ? Thanks you

    Reply
  34. You have hit the mark. In it something is also to me your idea is

    pleasant. I suggest to take out for the general discussion.

    Reply
  35. Thank you I’m new to MVC and this was a great help for paging . I have a problem when I try to realize a sample program. It like this:

    Compilation Error

    Description: An error occurred during the compilation of a resource required to service this request. Please review the following specific error details and modify your source code appropriately.

    Compiler Error Message: CS0570: ‘System.Web.Mvc.UserControlExtensions.RenderUserControl()’ is not supported by the language

    Source Error:

    Line 9:
    Line 10:
    Line 11: <%=Html.RenderUserControl("~/Views/Shared/Pagination.ascx", new MVC2.Views.Shared.PaginationViewData() {
    Line 12: PageIndex = Model.PageIndex,
    Line 13: TotalPages = Model.ToTalPages,

    Source File: c:\Documents and Settings\Administrator\桌面\MVC2\MVC2\Views\My\Index.aspx Line: 11

    Do you know what 's wrong with this?

    Reply

Comment on this post

Recent Posts

  • Automated Security Analyser for ASP.NET websites

    Evil Hackers are lurking everywhere and many Web-applications are delicately and share “too much” with the attacker. A quick (first!) overview offers the Tool “ASafaWeb”. All the website does is making a few requests and writing an Analyses including problem solving’s. There are no permanent disadvantages (bad requests/ DoS attacks and so on). Example: KnowYourStack.com ...

  • image1489-570x194.png
    „Sign in with Twitter“ for your own ASP.NET WebApp

      “Sign in with Twitter” is a popular practice to authenticate the users on your website. One advantage compared to an own registration is the lower inhibition for the user. But on the other hand Twitter doesn’t fess up with all the information’s and you will get into a kind of addiction. At the end ...

  • image1485-570x194_thumb.png
    CodePlex is going to be updated

      CodePlex the Microsoft Open Source Project Hosting Plattform hasn’t changed that much in the last few years and for a few times I thought Microsoft stopped the whole developing process. But now I found out that there is still life in the project. Maybe it is because of the success of GitHub or because ...

  • image1474_thumb.png
    What does Adobe in the flash-free web? Magazine-Style Layouts with CSS Regions!

      Adobe is well known for Photoshop and Flash but of course there is a lot more. According to the “Future Post” from Google Adobe declared one of their big subjects on a Blogpost. I’m talking about the W3C Working Draft to CSS Regions. Adobe cooperates with the WebKit Team and W3C on this. What ...

  • image1471-523x194.png
    HTML 5 Games, Tooling & 3D

      Game Developing is an interesting subject for all kind of software developer. But as a web developer without any Flash-skills there aren’t that much starting points. With HTML5 and the combination between Javascript, CSS3 and fast browsers there are the first “robust” HTML5 games. HTML5 games? Is this real? Neowin created a “Top 10” ...

Support us