As promised in my last blog post about creating a reusable NHibernate paging extension method, here is some code that uses the IPagedList interface we created to render a nice looking pager control in an ASP.NET MVC site.
- public class Pager
- {
- public enum PagerMode
- {
- NextPrev,
- Numbers,
- All
- }
- public string QueryStringParam { get; set; }
- public PagerMode Mode { get; set; }
- public string NextText { get; set; }
- public string PreviousText { get; set; }
- public string FirstText { get; set; }
- public string LastText { get; set; }
- public IPagedList List { get; set; }
- public bool ShowTotalRecords { get; set; }
- public string TotalRecordsText { get; set; }
- public RequestContext Context { get; set; }
- public bool EnableAjax { get; set; }
- public int MaxNumberOfNextPages { get; set; }
- public int MaxNumberOfPreviousPages { get; set; }
- private StringBuilder _builder;
- private UrlHelper _urlHelper;
- public Pager(IPagedList list, RequestContext context)
- {
- List = list;
- Context = context;
- _builder = new StringBuilder();
- _urlHelper = new UrlHelper(Context);
- // Defaults
- QueryStringParam = "page";
- Mode = PagerMode.All;
- NextText = "Next »";
- PreviousText = "« Previous";
- FirstText = "« First";
- LastText = "Last »";
- ShowTotalRecords = false;
- TotalRecordsText = "{0} total records";
- EnableAjax = true;
- MaxNumberOfNextPages = 4;
- MaxNumberOfPreviousPages = 5;
- }
- public string Render()
- {
- if (List.TotalCount == 0 || List.TotalPages == 1)
- return String.Empty;
- _builder.AppendLine("<div class=\"pagination\">");
- if (Mode == PagerMode.NextPrev || Mode == PagerMode.All)
- {
- WriteFirstLink();
- WritePreviousLink();
- }
- if (Mode == PagerMode.Numbers || Mode == PagerMode.All)
- {
- WritePageNumbers();
- }
- if (Mode == PagerMode.NextPrev || Mode == PagerMode.All)
- {
- WriteNextLink();
- WriteLastLink();
- }
- if (ShowTotalRecords)
- {
- WriteTotalRecords();
- }
- _builder.AppendLine("</div>");
- return _builder.ToString();
- }
- private void WriteTotalRecords()
- {
- var span = new TagBuilder("span");
- span.AddCssClass("total");
- span.SetInnerText(String.Format(TotalRecordsText, String.Format("{0:n0}", this.List.TotalCount)));
- _builder.AppendLine(span.ToString());
- }
- private void WriteLastLink()
- {
- if (!List.HasNextPage)
- return;
- WriteLink(GenerateUrl(List.TotalPages - 1), LastText);
- }
- private void WriteNextLink()
- {
- if (!List.HasNextPage)
- return;
- WriteLink(GenerateUrl(List.PageIndex + 1), NextText);
- }
- private void WritePageNumbers()
- {
- int start = 0;
- int end = List.TotalPages - 1;
- int maxPages = (MaxNumberOfPreviousPages + MaxNumberOfNextPages) + 1; // the + 1 is for the current page
- if (List.TotalPages > maxPages)
- {
- start = List.PageIndex - MaxNumberOfPreviousPages;
- end = List.PageIndex + MaxNumberOfNextPages;
- if (start < 0)
- start = 0;
- if (end > List.TotalPages - 1)
- end = List.TotalPages - 1;
- }
- for (int i = start; i <= end; i++)
- {
- WriteLink(GenerateUrl(i), i + 1);
- }
- }
- private void WritePreviousLink()
- {
- if (!List.HasPreviousPage)
- return;
- WriteLink(GenerateUrl(List.PageIndex - 1), PreviousText);
- }
- private void WriteFirstLink()
- {
- if (!List.HasPreviousPage)
- return;
- WriteLink(GenerateUrl(0), FirstText);
- }
- private void WriteLink(string url, string text)
- {
- var tag = new TagBuilder("a");
- tag.Attributes["href"] = url;
- tag.Attributes["title"] = text;
- tag.SetInnerText(text);
- _builder.Append(tag.ToString());
- }
- private void WriteLink(string url, int pageNumber)
- {
- var tag = new TagBuilder("a");
- tag.Attributes["href"] = url;
- tag.Attributes["title"] = pageNumber.ToString();
- tag.AddCssClass("number");
- if ((pageNumber - 1) == List.PageIndex)
- tag.AddCssClass("current");
- tag.SetInnerText(pageNumber.ToString());
- _builder.Append(tag.ToString());
- }
- private string GenerateUrl(int pageIndex)
- {
- if (this.EnableAjax)
- {
- return "#" + QueryStringParam + "=" + pageIndex;
- }
- else
- {
- Context.RouteData.Values[QueryStringParam] = pageIndex;
- return _urlHelper.RouteUrl(Context.RouteData.Values);
- }
- }
- }
The Pager class contains the rendering logic for the pager control and makes use of the TagBuilder class supplied in the System.Web.Mvc namespace. Makes generating well-formatted HTML code a bit easier, in my opinion. The class itself is pretty boring, just pass in the IPagedList instance and BAM! There are some customization options as well like changing the text of the Next/Previous/First/Last links, showing or hiding the total record count, etc. One of the more interesting options is the EnableAjax property which, when set to true, renders the URL of each pager link as a hash link (/list#page=2) instead of a hard link (/list?page=2). This allows you to intercept the hash changed event using jQuery and dynamically update the page content using Ajax.
In typical MVC fashion, I created a couple extension methods that make calling the Pager class easy from a View. You can customize the settings however you like. Call any of these methods from your view and just pass in the IPagedList instance.
- public static string Pager(this HtmlHelper html, IPagedList list)
- {
- return Pager(html, list, "page", false);
- }
- public static string AjaxPager(this HtmlHelper html, IPagedList list)
- {
- return Pager(html, list, "page", true);
- }
- public static string Pager(this HtmlHelper html, IPagedList list, string queryStringParam, bool enableAjax)
- {
- var pager = new Pager(list, html.ViewContext.RequestContext)
- {
- QueryStringParam = queryStringParam,
- EnableAjax = enableAjax,
- ShowTotalRecords = true
- };
- return pager.Render();
- }
Here’s what the rendered pager markup looks like, pretty clean if I do say so myself.
- <div class="pagination">
- <a title="First" href="/list?page=0"><< First</a>
- <a title="Previous" href="/list?page=0"><< Previous</a>
- <a class="number" title="1" href="/list?page=0">1</a>
- <a class="current number" title="2" href="/list?page=1">2</a>
- <a class="number" title="3" href="/list?page=2">3</a>
- <a title="Next" href="/list?page=2">Next >></a>
- <a title="Last" href="/list?page=2">Last >></a>
- <span class="total">37 total records</span>
- </div>
It doesn’t take much to make this look nice with some CSS classes. Here’s a simple example.
- a
- {
- color: #003366;
- }
- .pagination
- {
- margin: 0 0 5px;
- text-align: center;
- }
- .pagination a, .pagination a:visited
- {
- margin: 0 5px 0 0;
- padding: 3px 6px;
- text-decoration: none;
- }
- .pagination a.number
- {
- border: 1px solid #DDDDDD;
- }
- .pagination a.current
- {
- color: #000000;
- border-color: #DDDDDD;
- background-color: #DDDDDD;
- }
- .pagination .total
- {
- display: block;
- margin-top: 4px;
- color: #666666;
- }
And, finally, here’s what the pager looks like on the, err, page.
No comments:
Post a Comment