static void

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