Skip to main content
4 of 5
it is not clear if you are really using MVC.net at all so I removed the Tags
Malachi
  • 28.7k
  • 11
  • 86
  • 188

Stackoverflow Like Pager

I've been working on a stackoverflow 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. Thanks ahead of time!

    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());

    }
  }
Kyle
  • 549
  • 4
  • 8