Search is an important part for many C# .NET web applications, comprising of both the display of search results and a navigational control for scrolling through the pages of results. While traditional WebForms in C# ASP .NET included a variety of paging controls, such as the default paging included with the DataGrid or GridView controls, MVC .NET is limited with regard to built-in solutions. For MVC .NET web applications, we’ll need to create our own paging control.
In this tutorial, we’ll create “Smart Page”, an MVC helper method for displaying smart paging results. Our pager will display a specific number of page indexes adjacent to the active page, along with a set number of page indexes in the middle and end. The MVC search results pager will adjust the number of adjacent page indexes displayed, according to which search results page the user is currently browsing. The Smart Page search results pager is completely customizable and robust for displaying slick and smart, search results paging.
See It In Action
Vanilla Paging
A first try at creating an MVC .NET paging control might appear as follows:
1
1, 2, 3, 4, 5, 6, 7, 8, 9, 10
The above pager displays all available pages of search results (assuming there are 10 pages worth of results to display). While this is a functional solution to search result paging, it can become cumbersome when many search results are required, thus expanding the list of pages across the screen. A better solution, would be an adaptable search results pager, which limits the number of pages displayed to those the user may be interested in.
Smart Paging
An implementation of smart paging, who’s algorithm originally comes from the traditional Digg-style search results paging interface, can appear as follows:
1
1, 2 ... 4, [5], 6 ... 9, 10
Notice in the above, the active search result page would be page #5. The smart pager has adjusted the view to display the first two pages, the last two pages, and one adjacent page next to the active page. This can be a much more powerful solution for many C# ASP .NET MVC web applications. Since Smart Page is fully customizable, we can adjust the settings for the number of adjacent pages in all 3 sections by passing in a variety of parameters.
The Html Helper Method
We’ll implement Smart Page with an MVC Html helper method. This will allow us to simply pass in the required parameters from our search results paging model, to automatically display the pager. The following algorithm is adapted from a variety of “Digg-style” paging solutions in several languages, and custom coded to work with MVC C# .NET and allow passing in the specific parameters to customize the pager. The code is as follows:
public static class Html { /// <summary> /// Displays paging for a list of search results. /// Created by Kory Becker http://www.primaryobjects.com /// Modified code, based upon http://www.davidpirek.com/blog/aspnet-mvc-paging-digg-style /// Example display: Prev 1 2 3 4 5 ... 6 7 8 9 ... 10 11 12 Next /// </summary> ///<param name="helper" />HtmlHelper ///<param name="intCurrentPage" />Current page index ///<param name="intPerPage" />Number of results per page ///<param name="intNumberofItems" />Total number of results ///<param name="pageNumberPrefix" />Text to place in front of page numbers ///<param name="linkUrl" />Link url to insert into a href="X" /// (use [PAGE] to replace in the current page) ///<param name="onClick" />Text to include within the onclick property of the link (use [PAGE] to replace in the current page) ///<param name="previousText" />Text to show for "Previous" link ///<param name="nextText" />Text to show for "Next" link ///<param name="minPagesForPaging" />Minimum number of pages in order for paging to display, otherwise all pages are displayed. ///<param name="adjacentPageCount" />Number of pages to show around active page index (including left + right + index). ///For example: 3 => 1, 2, 3 | 2, [3], 4 | 3, [4], 5 | 48, 49, [50] ///<param name="nonAdjacentPageCount" />Number of pages to show for non-active page indexes (such as right-most numbers, if a left-most number is active) ///<param name="pageCalculation" />Optional anonymous method, allowing you alter the returned page index for each link ///by specifying a function that receives the page index and returns the "modified" page index. /// For example, converting 2 => 21 or converting 272 => 5421. Set to NULL to use the original page index. /// <returns>string html</returns> public static string SmartPage(this HtmlHelper helper, int intCurrentPage, int intPerPage, int intNumberofItems, string pageNumberPrefix, string linkUrl, string onClick, string previousText, string nextText, int minPagesForPaging = 3, int adjacentPageCount = 3, int nonAdjacentPageCount = 1, Func<int, int=""> pageCalculation = null) { string strPreviousText = previousText; string strNextText = nextText;
StringBuilder sb = new StringBuilder();
if (intCurrentPage < 1) { intCurrentPage = 1; }
int number_of_pages = (int)Math.Ceiling((double)intNumberofItems / (double)intPerPage);
int i = 0;
//hide paging if only one page if (number_of_pages > 1) { //previous record if (!(intCurrentPage == 1)) { int page = intCurrentPage - 1; if (pageCalculation != null) { page = pageCalculation((intCurrentPage - 1)); }
if (number_of_pages < minPagesForPaging) { // Display all pages, no paging. for (i = 0; i < number_of_pages; i++) { if (!(i == intCurrentPage - 1)) { int page = i + 1; if (pageCalculation != null) { page = pageCalculation((i + 1)); }
The above code has 3 scenarios: a left-most scenario when the user is active on the first few pages, a right-most scenario when the user is active on the last few pages, and a middle scenario when the user is active somewhere in the middle of the search results. This allows us to determine the placement and draw the MVC search results pager control with the desired settings.
The Search Results View
Since Smart Page is an MVC Html helper method, we can create an MVC partial view to host the search results and pager, as follows:
Notice in the above view, we have two main sections. The top section draws our pager partial view and the bottom section draws our search results. Our model is SearchModel, which contains both the pager settings (so we know what to pass Smart Page, to draw the pager) and our actual search results for rendering in the web page.
The Pager View
Our pager view will call the actual Smart Page Html helper method, as follows:
Notice the above code takes a different model than the search results view. It actually takes a subset of the fields from SearchModel, specifically those required for displaying the smart pager control. We pass the required parameters to our MVC helper method to draw the MVC C# .NET search results paging. Notice we use the parameter [PAGE] to automatically insert the specific page number into our resulting hyperlink for the page link. Smart Page allows using this variable in the href parameter, as well as the onclick parameter (since either event may be used when clicking a paging link. In addition to passing in the current page, total results, page size, and other properties, Smart Page also includes a delegate method for passing an optional function to change the value of [PAGE], if you require. Normally, Smart Page will simply insert the page index into the link, which you would use in your engine to run the search. However, depending on your backing search engine, you may require a different value, such as the actual hit index (rather than the page index). You can use the delegate method to alter the [PAGE] property in this manner.
Our Main Page
We can tie the two partial views together to create our search page, as follows:
<h2>Search Results Paging Example</h2> @using (Ajax.BeginForm("Index", null, new AjaxOptions { UpdateTargetId = "searchResultsDiv" }, new { Id = "searchForm" })) { @Html.Hidden("page", Model.CurrentPage) <div id="searchResultsDiv"> @Html.Partial("/Views/Controls/SearchResults.cshtml") </div> }
Notice the above simply wraps our partial view, for search results, within an ajax-compatible form. This will allow us to click the Smart Page pager links and seamlessly update the search results with an ajax callback to the controller. We’ve setup Smart Page to use the onclick event on page links, which calls our onSmartPage(page) javascript method. When activated, we’ll set the form’s hidden form field for “page” to the clicked page index, and then update our search results in the controller, based upon the page value. Our Main Page Controller Our controller code appears as follows:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
public ActionResult Index(intpage) { // Get search results. SearchModel searchModel = SearchManager.Search(_treasureList, page, pageSize);
// Return results to view. if (Request.IsAjaxRequest()) { return PartialView("/Views/Controls/SearchResults.cshtml", searchModel); } else { return View(searchModel); } }
In the above code, we simply take the page value from our hidden form field and pass this to our search engine to process and return results. We also include a check for ajax vs a regular page load to know which type of view to return.
Creating a Fake Search Engine
In the example project, we’ve implemented a simulation of a search engine. We simply create a static list of Treasure and provide a Search() method that returns results from this list, according to the page selected by the user from the Smart Page control, and the configured page size.
// Set search points. int start = (page * pageSize) - pageSize; intend = start + pageSize; if (end > treasureList.Count) { end = treasureList.Count; }
// Run search. for (int i = start; i < end; i++) { searchModel.TreasureList.Add(treasureList[i]); }
// Return results. return searchModel; }
Here is a screenshot of the example application.
Download @ GitHub
You can download the project source code on GitHub by visiting the project home page.
About the Author
This article was written by Kory Becker, software developer and architect, skilled in a range of technologies, including web application development, machine learning, artificial intelligence, and data science.