Enhancing The WebGrid With Sort Arrows

The Web Pages WebGrid offers sorting capability out of the box. However, it's not always obvious to the user on which column the grid data is being sorted at any one time, nor the direction in which it is being sorted. The convention is to provide arrows in the column header to act as a clear visual cue. This article looks at a three ways in which you can enhance your grids with sorting arrows.

The first approach uses a helper method to set the column header value:

@functions {
    public static string Sorter(string columnName, string columnHeader, WebGrid grid){
        return string.Format("{0} {1}", columnHeader, grid.SortColumn == columnName ? 
            grid.SortDirection == SortDirection.Ascending ? "▲" :
            "▼" : string.Empty);
    }
}

This function takes the column name, the column header value, and an instance of the WebGrid class. Here is how the function is used to set the column header value:

@{
    Page.Title = "Sorting Arrows";
    var db = Database.Open("Northwind");
    var sql = "SELECT CustomerID, CompanyName, ContactName, Address, City, Country, Phone FROM Customers";
    var data = db.Query(sql);
    var grid = new WebGrid(data, ajaxUpdateContainerId: "grid");
}
<h1>Sorting arrows</h1>
<div id="gridContainer">
    <div id="grid">
        @grid.GetHtml(    
            tableStyle : "table",
            alternatingRowStyle : "alternate",
            headerStyle : "header",
            columns: grid.Columns(
                grid.Column("CustomerID", Sorter("CustomerID", "ID", grid)),
                grid.Column("CompanyName", Sorter("CompanyName", "Company Name", grid)),
                grid.Column("ContactName", Sorter("ContactName", "Contact Name", grid)),
                grid.Column("Address", Sorter("Address", "Address", grid)),
                grid.Column("City", Sorter("City", "City", grid)),
                grid.Column("Country", Sorter("Country", "Country", grid)),
                grid.Column("Phone", Sorter("Phone", "Phone", grid))
            )
        )
    </div>
</div>

The WebGrid class has a SortDirection property which returns the direction for any sorting operation currently in use. It also has a SortColumn property which returns the name of the column which the data is being sorted on. The Sorter helper method inspects whether the current column is the one being sorted on, and if it is, it checks the current SortDirection value of the grid. Based on that, the column header value will be decorated with a triangle character indicating the direction. This method is simple to implement but it suffers from two main drawbacks: it has to be applied to every column individually, and it can only work with strings. There is no way to set apply an image as a direction indicator.

The next approach uses jQuery to solve the first of the drawbacks - it allows for centralisation of the code. The grid.GetHtml() method is slightly different, in that the function is not needed for the column names:

<div id="gridContainer">
    <div id="grid">
        @grid.GetHtml(    
            tableStyle : "table",
            alternatingRowStyle : "alternate",
            headerStyle : "header",
            columns: grid.Columns(
                grid.Column("CustomerID", "ID"),
                grid.Column("CompanyName", "Company Name"),
                grid.Column("ContactName", "Contact Name"),
                grid.Column("Address"),
                grid.Column("City"),
                grid.Column("Country"),
                grid.Column("Phone")
            )
        )
        @Html.Hidden("dir", grid.SortDirection) @Html.Hidden("col", grid.SortColumn)
    </div>
</div>

In addition, two hidden fields have been added to the page. One will contain the current sort direction, and the other the current sort column. Finally, the WebGrid constructor includes a value for the ajaxUpdateCallback parameter:

var grid = new WebGrid(data, ajaxUpdateContainerId: "grid", ajaxUpdateCallback: "setArrows");

The ajaxUpdateCallback value is the name of a JavaScript function that will execute whenever the grid is updated via AJAX. Here is all of the script that will set the arrows:

<script type="text/javascript">
    
    function setArrows() {
        var dir = $('#dir').val();
        var col = $('#col').val();
        var header = $('th a[href*=' + col + ']');
        if (dir == 'Ascending') {
            header.text(header.text() + ' ▲');
        }
        if (dir == 'Descending') {
            header.text(header.text() + ' ▼');
        }
    };

</script>

The sort direction and column are retrieved from the hidden fields, and the column header for the sort column is identified using jQuery's attribute contains selector. Then the correct arrow is added to the column's text depending on the sort direction.

The final example provides the ability to set your own arrows through images rather than text. It is only a minor deviation from the previous example, in that the ajaxUpdateCallback method sets the html of the header rather than the text:

<script type="text/javascript">
    
    function setArrowImages() {
        var dir = $('#dir').val();
        var col = $('#col').val();
        var header = $('th a[href*=' + col + ']');
        if (dir == 'Ascending') {
            header.html(header.html() + ' <img src="/images/arrow-up.png" alt="Ascending" />');
        }
        if (dir == 'Descending') {
            header.html(header.html() + ' <img src="/images/arrow-down.png" alt="Descending" />');
        }
    };
</script>

The name of this callback function differs from the preceding one, so if you want to try this out for yourself using the code that accompanies this article, you should make sure that you amend the WebGrid constructor call to reference setArrowImages in the ajaxUpdateCallback parameter instead. The code itself is on GitHub.

 

Date Posted: Friday, December 14, 2012 7:24 PM
Last Updated:
Posted by: Mikesdotnetting
Total Views to date: 19729

1 Comment

Wednesday, May 8, 2013 2:07 AM - Eric Sassaman

A CSS based variation, which alllows total control over the arrows (size, color, alignment, etc.) and one spot to tweak your arrow formatting:

1) Instead of header.text(header.text() + ' ▲'), set a CSS class: header.addClass("asc") and header.addClass("desc").

2) Then, some CSS:

th
{
paddding-right: 2em; /* make enough room for the arrow */
}

th a.asc:after, th a.desc:after
{
position: relative;
left: .4em; /* spacing between header and arrow */
bottom: .1em; /* raise it just a tad */
font-size: 0.75em; /* I like my arrows a tad smaller */
color: red;
}

th a.asc:after
{
content: '▲';
}

th a.desc:after
{
content: '▼';
}

The nice thing about position: relative is that you can tweak the left, bottom, top, and right arrow settings and the parent header margin will stay the same. Just make sure it's wide enough for your narrow columns too.
Add your comment

If you have any comments to make about this article, please use this form to do so. Make sure that your comment relates specifically to the article above. More general comments can be posted through the form on the Contact page.

Please note, all comments are moderated, and some may not be published. The kind of things that will ensure your comment is deleted without ever seeing the light of day are as follows:

  • Not relevant to the article
  • Gratuitous links to your own site or product
  • Anything abusive or libellous
  • Spam
  • Anything in a language I don't understand including gibberish.

I do not pass email addresses on to spammers, so a valid one will assist me in responding to you personally if required.