Unboxed Solutions Blog The frenetic soapbox
MVC 3 - Search Form & Result Paging
Developing a search form should be one of the simplest Web development tasks in the world. I can do it in a few minutes with ASP.NET WebForms, but for some reason with MVC it kicked my ass! Then I remembered back to the good old 90s when contract rates were high, the economy was great, and I was 20lbs lighter.
...oh and classic ASP forced you to create forms where you could either do a POST or a GET. Remember GET? Yeah it's beeen a while if you've been an ASP.NET developer. But welcome back! That is...if you are using MVC.
It is actually pretty simple. If, unlike me, you by chance bump into the blog post instead of trying to find the (IMO) really BAD answers on stackoverflow.com (at least to-date) and various corners of the Interwebz googling things with Bing or binging things with Google....but I digress.
Here's the answer:
- Make your form do an HTTP GET.
- Use Nuget to download PagedList
- Use Model Binding in your controller action method.
- Use RouteValueDictionary in the PagedList Pager HTML helper.
- ???
- Profit!
Here's the code for the ViewModel class, controller, and View (in that order):
using PagedList;
namespace SearchFormResultPagingExample.Models {
public class
SearchViewModel {
public int?
Page { get; set;
}
public string
EmailAddress { get; set;
}
public string
LastName { get; set;
}
public
IPagedList<Contact> SearchResults {
get; set; }
public string
SearchButton { get; set;
}
}
}
using System.Linq;
using System.Web.Mvc;
using SearchFormResultPagingExample.Models;
using PagedList; //NOTE: use Nuget to
reference PagedList
namespace SearchFormResultPagingExample.Controllers {
public class
SearchController :
Controller {
const int
RecordsPerPage = 25;
public
ActionResult Index(SearchViewModel
model) {
if (!string.IsNullOrEmpty(model.SearchButton)
|| model.Page.HasValue) {
var entities =
new AdventureWorksEntities();
var results = entities.Contacts.Where(c
=> c.LastName.StartsWith(model.LastName) &&
c.EmailAddress.StartsWith(model.EmailAddress))
.OrderBy(o => o.LastName);
var pageIndex = model.Page ?? 0;
model.SearchResults = results.ToPagedList(pageIndex, 25);
}
return View(model);
}
}
}
@model
SearchFormResultPagingExample.Models.SearchViewModel
@using
PagedList.Mvc;
@using
(Html.BeginForm("Index",
"Search",
FormMethod.Get)) {
@Html.ValidationSummary(false)
<fieldset>
<legend>Contact
Searchlegend>
<div
class="editor-label">
@Html.LabelFor(model
=> model.EmailAddress)
div>
<div
class="editor-field">
@Html.EditorFor(model
=> model.EmailAddress)
@Html.ValidationMessageFor(model
=> model.EmailAddress)
div>
<div
class="editor-label">
@Html.LabelFor(model
=> model.LastName)
div>
<div
class="editor-field">
@Html.EditorFor(model
=> model.LastName)
@Html.ValidationMessageFor(model
=> model.LastName)
div>
<p>
<input
name="SearchButton"
type="submit"
value="Search"
/>
p>
fieldset>
}
@if
(Model.SearchResults != null &&
Model.SearchResults.Count > 0) {
foreach (var
result in Model.SearchResults) {
<hr
/>
<table
width="100%">
<tr>
<td
valign="top"
width="*">
<div
style="font-weight:
bold; font-size:large;">@result.LastName,
@result.FirstNamediv>
@result.Title<br
/>
@result.Phone<br
/>
@result.EmailAddress
td>
tr>
table>
}
<hr
/>
@Html.PagedListPager(Model.SearchResults,
page => Url.Action("Index",
new
RouteValueDictionary() {
{ "Page", page },
{ "EmailAddress", Model.EmailAddress
},
{ "LastName", Model.LastName }
}),
PagedListRenderOptions.PageNumbersOnly)
The magic is that MVC coerces the querystring back into the model and vice versa.
Hope this helps anyone trying to figure this out.



