Skip to content

Instantly share code, notes, and snippets.

@takemyoxygen
Created July 5, 2013 09:46
Show Gist options
  • Save takemyoxygen/5933406 to your computer and use it in GitHub Desktop.
Save takemyoxygen/5933406 to your computer and use it in GitHub Desktop.
using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Linq;
using System.Text;
using System.Web;
using System.Web.Mvc;
namespace MvcPager.Html
{
public static class PagingEx
{
private class PageLinkInformation
{
public string Text { get; private set; }
public int Index { get; private set; }
public PageLinkInformation(string text, int index)
{
Index = index;
Text = text;
}
}
/// <summary>
/// Renders a set of links represenging a paging control
/// </summary>
/// <param name="htmlHelper"><see cref="HtmlHelper"/> instance</param>
/// <param name="itemsPerPage">Number of items that are displayed on one page</param>
/// <param name="totalItems">Total items that are displayed across all the pages</param>
/// <param name="currentPage">1-based index of the current page</param>
/// <param name="linkCssClass">CSS class that will be applied to all paging links. No class if empty</param>
/// <param name="numericListsCount">Max. count of numeric links that will be displyaed</param>
/// <param name="lastPageText">Text of the link to the last page. If null or empty then link to the last page won't be displayed</param>
/// <param name="firstPageText">Text of the link to the first page. If null or empty then link to the first page won't be displayed</param>
/// <param name="nextPageText">Text of the link to the next page which is not presented as numeric link</param>
/// <param name="previousPageText">Text of the link to the previous page which is not presented as numeric link</param>
/// <param name="pageIndexQueryStringParameter">Name of the query string parameter that stores current page index</param>
/// <returns></returns>
public static MvcHtmlString Paging(
this HtmlHelper htmlHelper,
int itemsPerPage,
int totalItems,
int currentPage = 0,
string linkCssClass = null,
int numericListsCount = 10,
string lastPageText = null,
string firstPageText = null,
string nextPageText = ">",
string previousPageText = "<",
string pageIndexQueryStringParameter = "Page")
{
int pagesCount = totalItems/itemsPerPage;
if (totalItems%itemsPerPage != 0) pagesCount++;
if (currentPage == 0)
{
if (!int.TryParse(HttpContext.Current.Request.QueryString[pageIndexQueryStringParameter],
out currentPage))
{
currentPage = 1;
}
}
int[] numericLinks = NumericLinks(currentPage, pagesCount, numericListsCount);
bool insertFirstPageLink = !string.IsNullOrEmpty(firstPageText) &&
numericLinks[0] != 1;
bool insertLastPageLink = !string.IsNullOrEmpty(lastPageText) &&
numericLinks.Last() != pagesCount;
bool insertNextPageLink = numericLinks.Last() < pagesCount - 1;
bool insertPrevPageLink = numericLinks[0] > 2;
var pages = new List<PageLinkInformation>(numericLinks.Length + 4); // 4 additional links are "First", "<<", ">>" and "Last"
if (insertFirstPageLink) pages.Add(new PageLinkInformation(firstPageText, 1));
if (insertPrevPageLink) pages.Add(new PageLinkInformation(previousPageText, numericLinks[0] - 1));
pages.AddRange(numericLinks.Select(i => new PageLinkInformation(i.ToString(), i)));
if (insertNextPageLink) pages.Add(new PageLinkInformation(nextPageText, numericLinks.Last() + 1));
if (insertLastPageLink) pages.Add(new PageLinkInformation(lastPageText, pagesCount));
return MvcHtmlString.Create(RenderHtml(pages, linkCssClass, currentPage, pageIndexQueryStringParameter));
}
private static int[] NumericLinks(int currentPage, int pagesCount, int desiredLinksCount)
{
int actualLinksCount = Math.Min(desiredLinksCount, pagesCount);
int desiredNumberOfLinksAfterCurrent = (actualLinksCount + 1)/2;
int actualNumberOfLinksAfterCurrent = Math.Min(pagesCount, desiredNumberOfLinksAfterCurrent + currentPage) -
pagesCount;
int desiredNumberOfLinksBeforeCurrent = actualLinksCount - actualNumberOfLinksAfterCurrent - 1;
int actualNumberOfLinksBeforeCurrent = Math.Min(desiredNumberOfLinksBeforeCurrent, currentPage - 1);
int firstNumericLink = currentPage - actualNumberOfLinksBeforeCurrent;
return Enumerable
.Range(firstNumericLink, actualLinksCount)
.ToArray();
}
private static string RenderHtml(
IEnumerable<PageLinkInformation> links,
string cssClass,
int currentPage,
string pageIndexQueryStringParameter)
{
return string.Concat(links
.Select(link =>
{
if (currentPage == link.Index) return link.Text;
var a = new TagBuilder("a") {InnerHtml = link.Text};
a.MergeAttribute("href", GetPageUrl(link.Index, pageIndexQueryStringParameter));
if (!string.IsNullOrEmpty(cssClass)) a.AddCssClass(cssClass);
return a.ToString();
}));
}
private static string GetPageUrl(int pageIndex, string pageQueryStringParameter)
{
var queryString = new NameValueCollection(HttpContext.Current.Request.QueryString);
queryString.Set(pageQueryStringParameter, pageIndex.ToString());
var sb = new StringBuilder(HttpContext.Current.Request.Path);
sb.Append("?");
for (int i = 0; i < queryString.Count; i++)
{
if (i > 0)
{
sb.Append("&");
}
sb.Append(HttpUtility.UrlEncode(queryString.Keys[i]));
sb.Append("=");
sb.Append(HttpUtility.UrlEncode(queryString[i]));
}
return sb.ToString();
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment