8
\$\begingroup\$

I've been working on a Stack Overflow like pager for my personal use and I've gotten everything working pretty good. Some of my logic is a bit suspect though, especially the logic in the Pager class itself. I thought I'd try posting this code on here just to see what sort of responses I got. I included my test cases as well. Some of the test names might be weird, but I'll go back and fix that later.

  public class Pager {
    public int TotalPages { get; private set; }
    public int SelectedItem { get; private set; }
    public int PageNumbersToShow { get; set; }
    public string CssClass { get; set; }

    public Pager(int selectedItem, int totalPages, int pageNumbersToShow = 5) {
      this.SelectedItem = selectedItem;
      this.TotalPages = totalPages;
      this.PageNumbersToShow = pageNumbersToShow;
      this.CssClass = "pager fl";
    }

    public override string ToString() {
      var finalString = new StringBuilder();
      var pagerDiv = new TagBuilder("div");
      pagerDiv.AddCssClass(CssClass);

      //prev 1 2 (3)
      //prev 1 (2) 3 next
      // (1) 2 3 next
      if (TotalPages <= PageNumbersToShow) {

        for (int i = 1; i <= TotalPages; i++) {
          finalString.Append(new PageNumber(i, (SelectedItem == i)).ToString());
        }

      }

      //Greater than pagesize and selected page is less than pagesize
      // prev (1) 2 3 4 5 ... 11 next
      // prev  1 (2) 3 4 5 ... 11 next
      if (SelectedItem < PageNumbersToShow && TotalPages > PageNumbersToShow) {

        for (int i = 1; i <= PageNumbersToShow; i++) {
          finalString.Append(new PageNumber(i, (SelectedItem == i)).ToString());
        }
        finalString.Append(new PageNumber().ToString());
        finalString.Append(new PageNumber(TotalPages).ToString());

      }

      //prev 1 ... 7 8 9 10 (11)
      //prev 1 ... 7 (8) 9 10 11 next
      if (SelectedItem > PageNumbersToShow && TotalPages - (PageNumbersToShow - 1) < SelectedItem) {

        finalString.Append(new PageNumber(1).ToString());
        finalString.Append(new PageNumber().ToString());
        for (int i = (TotalPages - PageNumbersToShow) + 1; i <= TotalPages; i++) {
          finalString.Append(new PageNumber(i, (SelectedItem == i)).ToString());
        }

      }

      //prev 1 ... 4 5 (6) 7 8 ... 11 next
      if (SelectedItem >= PageNumbersToShow && TotalPages - (PageNumbersToShow - 1) >= SelectedItem) {

        finalString.Append(new PageNumber(1).ToString());
        finalString.Append(new PageNumber().ToString());
        var middle = (PageNumbersToShow / 2);
        for (int i = SelectedItem - middle; i <= SelectedItem + middle; i++) {
          finalString.Append(new PageNumber(i, (SelectedItem == i)).ToString());
        }
        finalString.Append(new PageNumber().ToString());
        finalString.Append(new PageNumber(TotalPages).ToString());

      }

      /* Add Previous and Next Link */
      if (SelectedItem != 1) {
        finalString.Insert(0, new PageNumber(SelectedItem - 1, "prev").ToString());
      }
      if (SelectedItem != TotalPages) {
        finalString.Append(new PageNumber(SelectedItem + 1, "next").ToString());
      }

      /* Copy final string into div inner HTML */
      pagerDiv.InnerHtml = finalString.ToString();

      return pagerDiv.ToString();

    }

    private class PageNumber {
      private const string cssClass = "page-numbers";
      private const string href = "?page={0}";
      private TagBuilder link = new TagBuilder("a");
      private TagBuilder span = new TagBuilder("span");
      public bool Current { get; private set; }
      public int Page { get; private set; }


      public PageNumber(int Page, bool Current = false) {
        this.Current = Current;
        this.Page = Page;
        span.AddCssClass(cssClass);
        span.SetInnerText(Page.ToString());
      }

      public PageNumber(int Page, string spanText) {
        this.Page = Page;
        span.AddCssClass(cssClass + " " + spanText);
        span.SetInnerText(spanText + " ");
      }

      public PageNumber() {
        span.AddCssClass(cssClass + " " + "dots");
        span.SetInnerText("...");
      }

      public override string ToString() {
        if (Current) {
          span.AddCssClass("current");
          return span.ToString();
        } else {

          if (span.ToString().Contains("dots")) {
            return span.ToString();
          }

          link.MergeAttribute("href", string.Format(href, Page));
          link.MergeAttribute("title", string.Format("go to page {0}", Page));
          link.InnerHtml = span.ToString();
          return link.ToString();
        }
      }
    }
  }

  [TestClass]
  public class PagerTests {
    // (1)
    [TestMethod]
    public void Should_Only_Print_Out_One_Span_If_Number_Of_Pages_Is_One() {

      //Arrange
      var pager = new Pager(1, 1);

      //Act
      var result = pager.ToString();

      //Assert
      result.ShouldEqual(@"<div class=""pager fl""><span class=""current page-numbers"">1</span></div>");
    }

    // (1) 2 3 next
    [TestMethod]
    public void Should_Not_Print_Out_Previous_If_Selected_Page_Is_First_In_List_Smaller_Than_Five() {

      //Arrange
      StringBuilder expectedValue = new StringBuilder();
      var pager = new Pager(1, 3);

      //Act
      var result = pager.ToString();

      //Assert
      expectedValue.Append(@"<div class=""pager fl"">");
      expectedValue.Append(@"<span class=""current page-numbers"">1</span>");
      expectedValue.Append(@"<a href=""?page=2"" title=""go to page 2""><span class=""page-numbers"">2</span></a>");
      expectedValue.Append(@"<a href=""?page=3"" title=""go to page 3""><span class=""page-numbers"">3</span></a>");
      expectedValue.Append(@"<a href=""?page=2"" title=""go to page 2""><span class=""page-numbers next"">next </span></a>");
      expectedValue.Append(@"</div>");
      result.ShouldEqual(expectedValue.ToString());
    }

    // prev 1 2 (3)
    [TestMethod]
    public void Should_Not_Print_Out_Next_Link_When_Selected_Page_Is_Last_In_List_Smaller_Than_Five() {

      //Arrange
      StringBuilder expectedValue = new StringBuilder();
      var pager = new Pager(3, 3);

      //Act
      var result = pager.ToString();

      //Assert
      expectedValue.Append(@"<div class=""pager fl"">");
      expectedValue.Append(@"<a href=""?page=2"" title=""go to page 2""><span class=""page-numbers prev"">prev </span></a>");
      expectedValue.Append(@"<a href=""?page=1"" title=""go to page 1""><span class=""page-numbers"">1</span></a>");
      expectedValue.Append(@"<a href=""?page=2"" title=""go to page 2""><span class=""page-numbers"">2</span></a>");
      expectedValue.Append(@"<span class=""current page-numbers"">3</span>");
      expectedValue.Append(@"</div>");
      result.ShouldEqual(expectedValue.ToString());
    }

    // prev 1 (2) 3 next
    [TestMethod]
    public void Should_Print_Out_Both_Next_And_Previous_When_Selected_Is_Not_First_Or_Last_In_List_Smaller_Than_Five() {

      //Arrange
      StringBuilder expectedValue = new StringBuilder();
      var pager = new Pager(2, 3);

      //Act
      var result = pager.ToString();

      //Assert
      expectedValue.Append(@"<div class=""pager fl"">");
      expectedValue.Append(@"<a href=""?page=1"" title=""go to page 1""><span class=""page-numbers prev"">prev </span></a>");
      expectedValue.Append(@"<a href=""?page=1"" title=""go to page 1""><span class=""page-numbers"">1</span></a>");
      expectedValue.Append(@"<span class=""current page-numbers"">2</span>");
      expectedValue.Append(@"<a href=""?page=3"" title=""go to page 3""><span class=""page-numbers"">3</span></a>");
      expectedValue.Append(@"<a href=""?page=3"" title=""go to page 3""><span class=""page-numbers next"">next </span></a>");
      expectedValue.Append(@"</div>");
      result.ShouldEqual(expectedValue.ToString());
    }

    // (1) 2 3 4 5 ... 11 next
    [TestMethod]
    public void List_Greater_Than_Five_Pages_Generates_Dot_Dot_Dot_Last_Link_With_Page_One_Selcted() {

      //Arrange
      StringBuilder expectedValue = new StringBuilder();
      var pager = new Pager(1, 11);

      //Act
      var result = pager.ToString();

      //Assert
      expectedValue.Append(@"<div class=""pager fl"">");
      expectedValue.Append(@"<span class=""current page-numbers"">1</span>");
      expectedValue.Append(@"<a href=""?page=2"" title=""go to page 2""><span class=""page-numbers"">2</span></a>");
      expectedValue.Append(@"<a href=""?page=3"" title=""go to page 3""><span class=""page-numbers"">3</span></a>");
      expectedValue.Append(@"<a href=""?page=4"" title=""go to page 4""><span class=""page-numbers"">4</span></a>");
      expectedValue.Append(@"<a href=""?page=5"" title=""go to page 5""><span class=""page-numbers"">5</span></a>");
      expectedValue.Append(@"<span class=""page-numbers dots"">...</span>");
      expectedValue.Append(@"<a href=""?page=11"" title=""go to page 11""><span class=""page-numbers"">11</span></a>");
      expectedValue.Append(@"<a href=""?page=2"" title=""go to page 2""><span class=""page-numbers next"">next </span></a>");
      expectedValue.Append(@"</div>");
      result.ShouldEqual(expectedValue.ToString());

    }

    // prev 1 ... 3 4 (5) 6 7 ... 11 next
    [TestMethod]
    public void List_Greater_Than_Five_Give_Prev_One_Dot_Dot_Dot_Four_Numbers_Dot_Dot_Dot_Last_Next() {

      //Arrange
      StringBuilder expectedValue = new StringBuilder();
      var pager = new Pager(5, 11);

      //Act
      var result = pager.ToString();

      //Assert
      expectedValue.Append(@"<div class=""pager fl"">");
      expectedValue.Append(@"<a href=""?page=4"" title=""go to page 4""><span class=""page-numbers prev"">prev </span></a>");
      expectedValue.Append(@"<a href=""?page=1"" title=""go to page 1""><span class=""page-numbers"">1</span></a>");
      expectedValue.Append(@"<span class=""page-numbers dots"">...</span>");
      expectedValue.Append(@"<a href=""?page=3"" title=""go to page 3""><span class=""page-numbers"">3</span></a>");
      expectedValue.Append(@"<a href=""?page=4"" title=""go to page 4""><span class=""page-numbers"">4</span></a>");
      expectedValue.Append(@"<span class=""current page-numbers"">5</span>");
      expectedValue.Append(@"<a href=""?page=6"" title=""go to page 6""><span class=""page-numbers"">6</span></a>");
      expectedValue.Append(@"<a href=""?page=7"" title=""go to page 7""><span class=""page-numbers"">7</span></a>");
      expectedValue.Append(@"<span class=""page-numbers dots"">...</span>");
      expectedValue.Append(@"<a href=""?page=11"" title=""go to page 11""><span class=""page-numbers"">11</span></a>");
      expectedValue.Append(@"<a href=""?page=6"" title=""go to page 6""><span class=""page-numbers next"">next </span></a>");
      expectedValue.Append(@"</div>");
      result.ShouldEqual(expectedValue.ToString());

    }

    // prev 1 ... 7 8 9 10 (11)
    [TestMethod]
    public void When_List_Is_Greater_Than_Five_And_Selected_Is_The_Last_Item() {

      //Arrange
      StringBuilder expectedValue = new StringBuilder();
      var pager = new Pager(11, 11);

      //Act
      var result = pager.ToString();

      //Assert
      expectedValue.Append(@"<div class=""pager fl"">");
      expectedValue.Append(@"<a href=""?page=10"" title=""go to page 10""><span class=""page-numbers prev"">prev </span></a>");
      expectedValue.Append(@"<a href=""?page=1"" title=""go to page 1""><span class=""page-numbers"">1</span></a>");
      expectedValue.Append(@"<span class=""page-numbers dots"">...</span>");
      expectedValue.Append(@"<a href=""?page=7"" title=""go to page 7""><span class=""page-numbers"">7</span></a>");
      expectedValue.Append(@"<a href=""?page=8"" title=""go to page 8""><span class=""page-numbers"">8</span></a>");
      expectedValue.Append(@"<a href=""?page=9"" title=""go to page 9""><span class=""page-numbers"">9</span></a>");
      expectedValue.Append(@"<a href=""?page=10"" title=""go to page 10""><span class=""page-numbers"">10</span></a>");
      expectedValue.Append(@"<span class=""current page-numbers"">11</span>");
      expectedValue.Append(@"</div>");
      result.ShouldEqual(expectedValue.ToString());

    }

    // prev 1 ... 7 (8) 9 10 11 next
    [TestMethod]
    public void When_List_Is_Greater_Than_Five_And_Within_PageSize_Of_End_Of_List_And_Last_Item_Is_Not_Selected() {

      //Arrange
      StringBuilder expectedValue = new StringBuilder();
      var pager = new Pager(8, 11);

      //Act
      var result = pager.ToString();

      //Assert
      expectedValue.Append(@"<div class=""pager fl"">");
      expectedValue.Append(@"<a href=""?page=7"" title=""go to page 7""><span class=""page-numbers prev"">prev </span></a>");
      expectedValue.Append(@"<a href=""?page=1"" title=""go to page 1""><span class=""page-numbers"">1</span></a>");
      expectedValue.Append(@"<span class=""page-numbers dots"">...</span>");
      expectedValue.Append(@"<a href=""?page=7"" title=""go to page 7""><span class=""page-numbers"">7</span></a>");
      expectedValue.Append(@"<span class=""current page-numbers"">8</span>");
      expectedValue.Append(@"<a href=""?page=9"" title=""go to page 9""><span class=""page-numbers"">9</span></a>");
      expectedValue.Append(@"<a href=""?page=10"" title=""go to page 10""><span class=""page-numbers"">10</span></a>");
      expectedValue.Append(@"<a href=""?page=11"" title=""go to page 11""><span class=""page-numbers"">11</span></a>");
      expectedValue.Append(@"<a href=""?page=9"" title=""go to page 9""><span class=""page-numbers next"">next </span></a>");
      expectedValue.Append(@"</div>");
      result.ShouldEqual(expectedValue.ToString());

    }

    //prev 1 ... 5 6 (7) 8 9 ... 11 next
    [TestMethod]
    public void When_Selected_Is_One_Less_Than_Page_Size() {

      //Arrange
      StringBuilder expectedValue = new StringBuilder();
      var pager = new Pager(7, 11);

      //Act
      var result = pager.ToString();

      //Assert
      expectedValue.Append(@"<div class=""pager fl"">");
      expectedValue.Append(@"<a href=""?page=6"" title=""go to page 6""><span class=""page-numbers prev"">prev </span></a>");
      expectedValue.Append(@"<a href=""?page=1"" title=""go to page 1""><span class=""page-numbers"">1</span></a>");
      expectedValue.Append(@"<span class=""page-numbers dots"">...</span>");
      expectedValue.Append(@"<a href=""?page=5"" title=""go to page 5""><span class=""page-numbers"">5</span></a>");
      expectedValue.Append(@"<a href=""?page=6"" title=""go to page 6""><span class=""page-numbers"">6</span></a>");
      expectedValue.Append(@"<span class=""current page-numbers"">7</span>");
      expectedValue.Append(@"<a href=""?page=8"" title=""go to page 8""><span class=""page-numbers"">8</span></a>");
      expectedValue.Append(@"<a href=""?page=9"" title=""go to page 9""><span class=""page-numbers"">9</span></a>");
      expectedValue.Append(@"<span class=""page-numbers dots"">...</span>");
      expectedValue.Append(@"<a href=""?page=11"" title=""go to page 11""><span class=""page-numbers"">11</span></a>");
      expectedValue.Append(@"<a href=""?page=8"" title=""go to page 8""><span class=""page-numbers next"">next </span></a>");
      expectedValue.Append(@"</div>");
      result.ShouldEqual(expectedValue.ToString());

    }

    //prev 1 ... 8 9 (10) 11 12 ... 15 next
    [TestMethod]
    public void When_Selected_Is_Exactly_Total_Pages_Minus_Page_Size() {

      //Arrange
      StringBuilder expectedValue = new StringBuilder();
      var pager = new Pager(10, 15);

      //Act
      var result = pager.ToString();

      //Assert
      expectedValue.Append(@"<div class=""pager fl"">");
      expectedValue.Append(@"<a href=""?page=9"" title=""go to page 9""><span class=""page-numbers prev"">prev </span></a>");
      expectedValue.Append(@"<a href=""?page=1"" title=""go to page 1""><span class=""page-numbers"">1</span></a>");
      expectedValue.Append(@"<span class=""page-numbers dots"">...</span>");
      expectedValue.Append(@"<a href=""?page=8"" title=""go to page 8""><span class=""page-numbers"">8</span></a>");
      expectedValue.Append(@"<a href=""?page=9"" title=""go to page 9""><span class=""page-numbers"">9</span></a>");
      expectedValue.Append(@"<span class=""current page-numbers"">10</span>");
      expectedValue.Append(@"<a href=""?page=11"" title=""go to page 11""><span class=""page-numbers"">11</span></a>");
      expectedValue.Append(@"<a href=""?page=12"" title=""go to page 12""><span class=""page-numbers"">12</span></a>");
      expectedValue.Append(@"<span class=""page-numbers dots"">...</span>");
      expectedValue.Append(@"<a href=""?page=15"" title=""go to page 15""><span class=""page-numbers"">15</span></a>");
      expectedValue.Append(@"<a href=""?page=11"" title=""go to page 11""><span class=""page-numbers next"">next </span></a>");
      expectedValue.Append(@"</div>");
      result.ShouldEqual(expectedValue.ToString());

    }
  }
\$\endgroup\$
7
\$\begingroup\$

It's a little wonky to have a

public int PageNumbersToShow { get; set; }

Then do all dis bizness in the toString() method

if (TotalPages <= PageNumbersToShow) {

    for (int i = 1; i <= TotalPages; i++) {
      finalString.Append(new PageNumber(i, (SelectedItem == i)).ToString());
    }

  }

You've created a class to encapsulate a PageNumber. It would make more sense to have the Pager hold a list of these.

If you're thinking along these lines already, then a Pager is really just a Composite of PageNumbers.

Don't do so much business logic processing in your toString() methods and constructors. Most of that logic should be in utility methods devoted to their particular tasks. For example

 if (Current) {
      span.AddCssClass("current");
      return span.ToString();
    } else {

      if (span.ToString().Contains("dots")) {
        return span.ToString();
      }
  1. What if I wanna add another CSS class later?
  2. What if I decide to change from to
  3. like a reasonable person?
  4. What if I wanna add javascript events?
\$\endgroup\$
4
\$\begingroup\$

instead of this malarky....

finalString.Append(new PageNumber(TotalPages).ToString());

why not something that manages the pagenumbering so you have a simple interface...

pages.AddPage(pageNumber);

and

pages.AddPage(2).Select();

then at the end...

finalString = pages.ToString();
\$\endgroup\$
3
\$\begingroup\$

I'm using something like below paired with a separate html rendering helper method. I thought it was pretty clean but I'm sure it's not perfect.

#region IPagedList

/// <remarks>
/// Represents a subset of a collection of objects that can be individually accessed by index and containing metadata about the superset collection of objects this subset was created from.
/// </remarks>
/// <typeparam name="T">The type of object the collection should contain.</typeparam>
/// <seealso cref="IList{T}"/>
public interface IPagedList
{
    /// <summary>
    /// Total number of subsets within the superset.
    /// </summary>
    /// <value>
    /// Total number of subsets within the superset.
    /// </value>
    int PageCount { get; }

    /// <summary>
    /// Total number of objects contained within the superset.
    /// </summary>
    /// <value>
    /// Total number of objects contained within the superset.
    /// </value>
    int TotalItemCount { get; }

    /// <summary>
    /// Zero-based index of this subset within the superset.
    /// </summary>
    /// <value>
    /// Zero-based index of this subset within the superset.
    /// </value>
    int PageIndex { get; }

    /// <summary>
    /// Maximum size any individual subset.
    /// </summary>
    /// <value>
    /// Maximum size any individual subset.
    /// </value>
    int PageSize { get; }
}

#endregion

#region PagedList

/// <summary>
/// Represents a subset of a collection of objects that can be individually accessed by index and containing metadata about the superset collection of objects this subset was created from.
/// </summary>
/// <typeparam name="T">The type of object the collection should contain.</typeparam>
/// <seealso cref="IPagedList{T}"/>
/// <seealso cref="List{T}"/>
public class PagedList<T> : List<T>, IPagedList
{
    /// <summary>
    /// Default CTOR
    /// </summary>
    public PagedList()
        : this(new List<T>(), 0, 0, 20)
    { }


    /// <summary>
    /// Initializes a new instance of a type deriving from <see cref="BasePagedList{T}"/> and sets properties needed to calculate position and size data on the subset and superset.
    /// </summary>
    /// <param name="index">The index of the subset of objects contained by this instance.</param>
    /// <param name="pageSize">The maximum size of any individual subset.</param>
    /// <param name="totalItemCount">The size of the superset.</param>
    public PagedList(IEnumerable<T> list, int totalItemCount, int pageIndex, int pageSize)
        : base(list == null ? new List<T>() : list)      
    {
        if (pageIndex < 0)
            throw new ArgumentOutOfRangeException("index", pageIndex, "PageIndex cannot be below 0.");
        if (pageSize < 1)
            throw new ArgumentOutOfRangeException("pageSize", pageSize, "PageSize cannot be less than 1.");

        // set source to blank list if superset is null to prevent exceptions
        this.PageCount = CalcNumberOfPages(totalItemCount, pageSize);
        this.TotalItemCount = totalItemCount;
        this.PageSize = pageSize;
        this.PageIndex = pageIndex;
    }


    /// <summary>
    /// Calculates the number of pages in a set.
    /// </summary>
    /// <param name="totalItemCount">Total number of items in the set</param>
    /// <param name="pageSize">Number of items in a page</param>
    /// <returns>Number of pages (int).</returns>
    protected int CalcNumberOfPages(int totalItemCount, int pageSize)
    {
        return (totalItemCount % pageSize == 0) ? (totalItemCount / pageSize) : totalItemCount / pageSize + 1;
    }


    #region IPagedList<T> Members

    /// <summary>
    /// Total number of subsets within the superset.
    /// </summary>
    public int PageCount { get; protected set; }

    /// <summary>
    /// Total number of objects contained within the superset.
    /// </summary>
    public int TotalItemCount { get; protected set; }

    /// <summary>
    /// Zero-based index of this subset within the superset.
    /// </summary>
    public int PageIndex { get; protected set; }

    /// <summary>
    /// Maximum size any individual subset.
    /// </summary>
    public int PageSize { get; protected set; }

    /// <summary>
    /// Returns true if this is the first subset within the superset.
    /// </summary>
    public bool IsFirstPage
    {
        get { return PageIndex < 1; }
    }

    /// <summary>
    /// Returns true if this is the last subset within the superset.
    /// </summary>
    public bool IsLastPage
    {
        get { return PageIndex + 1 == PageCount; }
    }

    #endregion
}

#endregion

#region Extensions

/// <summary>
/// Container for extension methods designed to simplify the creation of instances of <see cref="PagedList{T}"/>.
/// </summary>
/// <remarks>
/// Container for extension methods designed to simplify the creation of instances of <see cref="PagedList{T}"/>.
/// </remarks>
public static class PagedListExtensions
{
    /// <summary>
    /// Creates a subset of this collection of objects that can be individually accessed by index and containing metadata about the collection of objects the subset was created from.
    /// </summary>
    /// <typeparam name="T">The type of object the collection should contain.</typeparam>
    /// <param name="superset">The collection of objects to be divided into subsets. If the collection implements <see cref="IQueryable{T}"/>, it will be treated as such.</param>
    /// <param name="index">The index of the subset of objects to be contained by this instance.</param>
    /// <param name="pageSize">The maximum size of any individual subset.</param>
    /// <returns>A subset of this collection of objects that can be individually accessed by index and containing metadata about the collection of objects the subset was created from.</returns>
    /// <seealso cref="PagedList{T}"/>
    public static PagedList<T> ToPagedList<T>(this IEnumerable<T> list, int totalItemCount, int pageIndex, int pageSize)
    {
        return new PagedList<T>(list, totalItemCount, pageIndex, pageSize);
    }
}

public static class PageQuery
{
    public static PagedList<T> Paginate<T>(this IQueryable<T> query, int pageIndex, int pageSize, int totalItemCount)
    {
        return query.Skip(Math.Max(pageSize * pageIndex, 0)).Take(pageSize).ToPagedList(totalItemCount, pageIndex, pageSize);
    }
}

#endregion

Then to page a dataset with Linq to SQL you call it like:

        int rowCount = (from o in dc.Table
                        select o).Count();

        return (from o in dc.Table
                orderby o.LastName
                select o).Paginate(pageNum, pageSize, rowCount);

Then to render the pager html something like this:

    /// <summary>
    /// Renders a paging control.  This has to be used in strongly typed pages that use PagedList<Entity>
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="page"></param>
    /// <param name="routeName"></param>
    /// <param name="routeValues"></param>
    public static void Pager<T>(this HtmlHelper<T> page, string routeName, object routeValues) where T : class, IPagedList
    {
        RouteValueDictionary routeDictionary = (routeValues == null) ? new RouteValueDictionary() : new RouteValueDictionary(routeValues);

        HtmlTextWriter writer = new HtmlTextWriter(page.ViewContext.HttpContext.Response.Output);

        if (writer != null)
        {
            int lowerBound = (page.ViewData.Model.PageIndex) - 10;
            lowerBound = lowerBound < 0 ? 0 : lowerBound;

            int upperBound = (page.ViewData.Model.PageIndex) + 10;
            upperBound = upperBound > page.ViewData.Model.PageCount ? page.ViewData.Model.PageCount : upperBound;

            writer.Write("<div class=\"pager\">");

            string pageNumberParam;

            for (int pageNum = lowerBound + 1; pageNum <= upperBound; pageNum++)
            {
                pageNumberParam = string.Empty;

                if (pageNum != page.ViewData.Model.PageIndex + 1)
                {
                    VirtualPathData vPathData = RouteTable.Routes.GetVirtualPath(page.ViewContext.RequestContext, routeName, routeDictionary);

                    pageNumberParam += vPathData.VirtualPath.Contains('?') ? "&pageNum=" : "?pageNum=";
                    pageNumberParam += (pageNum - 1).ToString();

                    writer.AddAttribute(HtmlTextWriterAttribute.Href, vPathData.VirtualPath + pageNumberParam);
                    writer.AddAttribute(HtmlTextWriterAttribute.Alt, "Page " + pageNum);
                    writer.RenderBeginTag(HtmlTextWriterTag.A);
                }

                writer.AddAttribute(
                    HtmlTextWriterAttribute.Class,
                    pageNum == page.ViewData.Model.PageIndex + 1 ? "pageLinkCurrent" : "pageLink"
                );

                writer.RenderBeginTag(HtmlTextWriterTag.Span);
                writer.Write(pageNum);
                writer.RenderEndTag();

                if (pageNum != page.ViewData.Model.PageIndex + 1)
                {
                    writer.RenderEndTag();
                }
                writer.Write(" ");
            }

            writer.Write("<br /> (");
            writer.Write(page.ViewData.Model.TotalItemCount);
            writer.Write(" items in ");
            writer.Write(page.ViewData.Model.PageCount);
            writer.Write(" pages) ");

            writer.Write("</div>");
        }

    }

I think a similar pattern was in the 2nd last issue of MSDN mag too...

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.