Simple paging
Paging HtmlHelper
Very simple version (list all pages)
public static class PagingHelpers
{
public static MvcHtmlString PagingLinks(this HtmlHelper html,
PagingData pagingData,
Func<int, string> pageUrl)
{
var result = new StringBuilder();
for (int i = 1; i <= pagingData.TotalPages; i++)
{
var tag = new TagBuilder("a");
tag.MergeAttribute("href", pageUrl(i));
tag.InnerHtml = i.ToString(CultureInfo.InvariantCulture);
if (i == pagingData.CurrentPage)
tag.AddCssClass("selected");
result.Append(tag + " ");
}
return MvcHtmlString.Create(result.ToString());
}
}
More elaborate version- first/last/current page and current+/-4. This one writes buttons, not links, so we can post if it's in a form (e.g. with search filters).
public static class PagingHelpers
{
/// <summary>
/// Paging links (up to 10 links, copes with 1000s of pages)
/// </summary>
/// <param name="html">The HTML.</param>
/// <param name="pagingData">The paging data.</param>
/// <param name="pageUrl">The page URL. Eg x => Url.Action("Index", new { page = x })</param>
/// <returns></returns>
public static MvcHtmlString PagingLinks(this HtmlHelper html,
IPagingData pagingData,
Func<int, string> pageUrl)
{
var totalPages = pagingData.TotalPages;
if (totalPages == 1) return null;
var result = new StringBuilder();
var currentPage = pagingData.CurrentPage;
//record the current page in form
AddHiddenField(result, currentPage.ToString(CultureInfo.InvariantCulture));
var start = 1;
var end = totalPages;
const int maxPrevNext = 4;
//where to start
if (currentPage > maxPrevNext)
{
start = currentPage - maxPrevNext + 1;
if (currentPage + maxPrevNext >= end)
{
//if near end, move back even more
start = start - maxPrevNext + 1;
}
//unless we overshot
if (start <= 2) start = 2;
WritePageLink(0, 1, result);
if (start > 2)
result.Append(" ... ");
}
//where to end
var showMore = currentPage + maxPrevNext;
if (showMore < totalPages)
{
end = showMore;
}
//draw the pages
for (var i = start; i <= end; i++)
{
WritePageLink(currentPage, i, result);
}
//draw the end page if required
if (totalPages > end)
{
if (totalPages != end + 1)
result.Append(" ... "); //if there is a gap
WritePageLink(currentPage, totalPages, result);
}
//wrap up and write
var wrapper = new TagBuilder("div");
wrapper.AddCssClass("paging");
wrapper.InnerHtml = result.ToString();
return MvcHtmlString.Create(wrapper.ToString());
}
private static void WritePageLink(int currentPage, int pageNumber, StringBuilder result)
{
var pageText = pageNumber.ToString(CultureInfo.InvariantCulture);
var tag = new TagBuilder("input");
tag.Attributes.Add("type", "button");
tag.Attributes.Add("value", pageText);
if (pageNumber == currentPage)
tag.AddCssClass("selected");
result.Append(tag + " ");
}
private static void AddHiddenField(StringBuilder result, string value)
{
var hidden = new TagBuilder("input");
hidden.Attributes.Add("type", "hidden");
hidden.Attributes.Add("name", "page");
hidden.Attributes.Add("id", "page");
hidden.Attributes.Add("value", value);
result.AppendLine(hidden.ToString(TagRenderMode.Normal));
}
}
View:
@Html.PagingLinks(Model.PagingData, x => Url.Action("Index", new { page = x }))
The form-buttons version requires the links to be in a form (with the search filter inputs), and this javascript.
$('.paging input').click(function() {
var gotoPage = $(this).val();
$("#page").val(gotoPage);
var form = $(this).parents("form");
form.submit();
});
Controller:
public ActionResult Index(int page = 1)
{
IEnumerable<CountryModel> countryCollection = _context.CountryCollection;
var pagedModel = new PagedCountriesViewModel
{
Countries = countryCollection.OrderBy(p => p.Code)
.Skip((page - 1) * PageSize)
.Take(PageSize),
PagingData = new PagingData
{
CurrentPage = page,
ItemsPerPage = PageSize,
TotalItems = countryCollection.Count()
}
};
return View(pagedModel);
}
Paging class:
public class PagingData
{
public int TotalItems { get; set; }
public int ItemsPerPage { get; set; }
public int CurrentPage { get; set; }
public int TotalPages
{
get { return (int)Math.Ceiling((decimal)TotalItems / ItemsPerPage); }
}
}