In the old days of WebForm development we took for granted all the little things that went on behind the scenes. For example, when you put a DataGridView on a WebForm you could set a few properties, and BOOM, you had sorting, paging and the works.
MVC has kind of brought web development back to its roots. This means that you have to do a little more work to get features like paging and sorting implemented. In order to simplify the sorting problem I made my own extension method using the HtmlHelper class to render the column headers. My code creates the links for the column headers and draws the sort indicators (without using any images!). The method utilizes two hard-coded variable names which are sidx and sord. The sidx variable is the name of the column to be sorted and the sord variable is the sort order (ascending or descending).
public static string SortableColumn(this HtmlHelper htmlHelper, string linkText, string columnName, object routeValues) {
//automatically determine the current action
System.Web.Routing.RouteData data = htmlHelper.ViewContext.Controller.ControllerContext.RouteData;
string actionName = data.GetRequiredString("action");
StringBuilder sb = new StringBuilder();
var vals = new RouteValueDictionary(routeValues);
string sidx = String.Empty;
if (System.Web.HttpContext.Current.Request["sidx"] != null) {
sidx = System.Web.HttpContext.Current.Request["sidx"].ToString();
}
//modify the sidx
if (vals.ContainsKey("sidx") == false) {
vals.Add("sidx", columnName);
}
else {
vals["sidx"] = columnName;
}
//get the sort order from the request variable if it exists
string sord = String.Empty;
if (System.Web.HttpContext.Current.Request["sord"] != null) {
sord = System.Web.HttpContext.Current.Request["sord"].ToString();
}
//add the sord key if needed
if (vals.ContainsKey("sord") == false) {
vals.Add("sord", String.Empty );
}
//if column matches
if (sidx.Equals(columnName, StringComparison.CurrentCultureIgnoreCase) == true) {
if (sord.Equals("asc", StringComparison.CurrentCultureIgnoreCase) == true) {
//draw the ascending sort indicator using the wingdings font.
sb.Append(" <font face='Wingdings 3'>p</font>");
vals["sord"] = "desc";
}
else {
sb.Append(" <font face='Wingdings 3'>q</font>");
vals["sord"] = "asc";
}
}
else {
//if the column does not match then force the next sort to ascending order
vals["sord"] = "asc";
}
//Use the ActionLink to build the link and insert it into the string
sb.Insert(0,System.Web.Mvc.Html.LinkExtensions.ActionLink(htmlHelper, linkText, actionName, vals));
return sb.ToString();
}
Next we need to utilize the method in the markup. The "helper" method really cleans up the code and is very simple to understand and use. Once the header is rendered, and you mouse over the link you should see a href that looks something like this:
/Instance/Assignments?dbaid=109&sidx=SecondaryDBA&sord=desc
<% object routeValues = new { dbaid = Request["dbaid"] }; %>
...
<table id="grid">
<tr>
<th><%=Html.SortableColumn("Instance", "InstanceName", routeValues )%></th>
<th><%=Html.SortableColumn("Primary DBA", "PrimaryDBA", routeValues )%></th>
<th><%=Html.SortableColumn("Secondary DBA", "SecondaryDBA", routeValues)%></th>
<th><%=Html.SortableColumn("Version", "Version", routeValues)%></th>
<th><%=Html.SortableColumn("RDBMS", "RDBMSTypeCD", routeValues)%></th>
<th><%=Html.SortableColumn("Bus. Group", "BusinessGroupCD", routeValues)%></th>
</tr>
<% foreach (var item in Model) { %>
<tr>
<td><%= Html.Encode(item.InstanceName) %></td>
<td><%= Html.Encode(item.PrimaryDBA) %></td>
<td><%= Html.Encode(item.SecondaryDBA) %></td>
<td><%= Html.Encode(item.Version) %></td>
<td><%= Html.Encode(item.RDBMSTypeCD) %></td>
<td><%= Html.Encode(item.BusinessGroupCD) %></td>
</tr>
<% } %>
</table>
Most importantly, the method in your controller that populates the grid must utilize the variables sidx (column to be sorted) and sord (sort order). Without this, you will not get any sorting...
public ActionResult Assignments( int? page, int? dbaid, string sidx, string sord ) {
try {
var query = InstanceRepository.GetAssignments(dbaid);
//sort the data
string sortExpression = (sidx ?? "InstanceName") + " " + (sord ?? "asc");
query = query.OrderBy(sortExpression);
//page the data
return View(query.ToPagedList(page ?? 1, MySession.PageSize));
}
catch {
return View("Error");
}
}
The data is sorted with the help of the Dynamic Linq library. I originally discovered this library after reading one of Scott Gu's posts. I also utilized the famous PagedList class to add pagination to the grid. Finally, here is the result:
