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:
Last Updated:
Posted by:
Total Views to date: 27263

3 Comments

- 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.

- Prabhu Somasundaram

I followed your steps for the sorting the webgrid. First approach worked well. But for the second and third approach, setArrowImages javascript function of ajaxUpdateCallback didnt have access to the form fields; in this case it is hidden fields. By the way my webgrid in partial view and render in the parent view using Ajax.

- Mike

@Prabhu Somasundaram

I have no idea what the problem is, and this isn't the right place to solve it. You should post a question to forums.asp.net or www.stackoverflow.com

Recent Comments

Gayan 7/3/2015 6:20 AM
In response to 7 C# 6.0 Features That Every ASP.NET Developer Should Know About
Great Article thanks...

Semil 7/1/2015 7:03 AM
In response to iTextSharp - Drawing shapes and Graphics
I have created a rectangle using above methode. Now I want to add a text in the center of this How I...

Satyabrata Mohapatra 6/30/2015 6:12 PM
In response to Reading Excel Files Without Saving To Disk In ASP.NET
Ahh.....this is awesome. Happy to see after a long time you wrote a article on web form :D...

Marty 6/30/2015 7:16 AM
In response to Posting Data With jQuery AJAX In ASP.NET Razor Web Pages
Mike, you're the Man! Another great article. So incredibly helpful. I'm definitely going to buy your...

Rohan 6/30/2015 5:32 AM
In response to ASP.NET MVC 5 with EF 6 - Working With Files
Very good and helpful tutorial. Thanks. Just wanted to know what would be the max file size limit we...

Fernando 6/30/2015 1:59 AM
In response to Programmatically accessing data from DataSource controls
What if I want to pass parameters natively using the DataSourceSelectArguments object, instead of be...

pankaj 6/29/2015 3:13 PM
In response to How to retain carriage returns or line breaks in an ASP.NET web page
very nice i'm use this in my code thank you.... ...

Mike 6/29/2015 2:22 AM
In response to MVC 5 with EF 6 in Visual Basic - Sorting, Filtering and Paging
This is the first example that I have found that works....

Marty 6/28/2015 4:57 AM
In response to Posting Data With jQuery AJAX In ASP.NET Razor Web Pages
Mike, what if I don't want to render back the text to the browser, but I want to send it some other...

Mike 6/27/2015 4:00 PM
In response to Migrating Classic ASP To ASP.NET Razor Web Pages Part One- Razor Syntax And Visual Basic
have you used any of the code converters to convert classic asp to c#? If so, which one do you have...