One of the great things about the new ASP.NET GridView control is that it allows you to implement column field sorting with relative ease, at least in comparison to what you would have had to do to attain similar functionality in earlier versions. There are a plethora of articles and tutorials out there that lead you through this process, but one thing that most, if not all, of these are lacking is some kind of hint of how to override the default sorting order of your sortable columns. Since this was a problem that I had to solve recently and given the number of times I’ve seen the question being asked (and unanswered) across various forums and newsgroups, I figured I’d take some time to share my solution with the community.

This entry assumes that you have a good understanding of how sorting works in the DataGrid. If you don’t, I would suggest that you take some time to do so before continuing. There is no download, but I provide enough code for you to be able to implement this solution on your own grid.

The GridView control raises two sort-related events when the user clicks on a link button in the header of any columns that has its CommandName property set to Sort. The Sorting event is raised before the GridView handles the sort operation and the Sorted event is raised after the sorting has completed and data has been bound to the grid.

The Sorting event is of particular interest to us. By handling this event, we have the opportunity to manipulate the sort data before the grid hands it to the underlying data store (usually managed by an instance of ObjectDataSource) and asks for the data to be manipulated in a form that conforms to the sorting rules that it has established.

When the event is handled, we receive a reference to a GridViewSortEventArgs object which contains properties that contain values that we are interested in. The first is the SortExpression property, which, in the simplest of situations, contains the name of the field that is to be sorted. The second is the read-only SortDirection property which defines the order in which the data will be sorted. This actually serves no purpose in this space other than to provide assistance in identifying the eventuating sort direction.

Once the handler has completed its scope of execution, if the sort has not been cancelled by setting the Cancel property of the GridViewSortEventArgs object to true, an instance of DataSourceSelectArguments is created and the final sort expression is defined. The pertinent value is essentially a concatenation of the SortExpression value and the SQL equivalent of the SortDirection value if it is defined as SortDirection.Descending. In other words, given a column named (and sort expression defined as) Rating and a SortDirection value of SortDirection.Descending, the ultimate sort expression will be “Rating DESC.” If the SortDirection value is SortDirection.Ascending, the expression would be restricted to only the defined SortExpression, which would be “Rating.”

Now that we have attained this understanding, let’s take a look at how this can be manipulated to reverse the default sorting order.

protected void SearchResultsView_OnSorting( object sender, GridViewSortEventArgs e ) {
  e.SortExpression = e.SortExpression + " DESC"
}

In the above code block, I’ve overridden the SortExpression value that was captured at the time that the event was raised. Instead of the values in the column being sorted in ascending order, which, again, is the default, the expression has been defined as a concatenation of the original sort expression and DESC. Once the event has been handled, the updated SortExpression will be parsed and the SortDirection property on the grid will be updated as appropriate to match the order that you specified in the expression. What this means is that if the next sort is done on the same column, the SortDirection value will be SortDirection.Ascending instead of SortDirection.Descending.

The code would work great if you wanted the default (and only) sorting order of each of the sortable columns in your grid to be descending. This is clearly not the desirable outcome. Since we only want this exception to apply to the Rating expression, we’ll add some code that checks for this expression before overriding its default sort.

protected void SearchResultsView_OnSorting( ... ) {
  if ( e.SortExpression.Equals( "Rating" ) ) {
    e.SortExpression = e.SortExpression + " DESC";         
  }
}

We still want the contents of the Rating column to be sorted in ascending order, but only after the column has previously been sorted in descending order. To clarify, there are two different scenarios that we need to cover:

  1. Sorting the Rating column when it's already in descending order should result in an ascending sort.
  2. If a different column is sorted after the Rating column has been sorted, regardless of its order, the first sort of the Rating column afterwards should be descending.

The implementation is actually relatively simple. All we need to do is stash away the most recent expression in a state bag (ViewState) and load and compare it when the next Sorting event is handled. The following code demonstrates how we first check to make sure that the current sort expression is the Rating expression, then we check the value stored in the PreviousSortExpression key in ViewState to see if it was used in the previous sort. If it was, then the sorting is done based on the current SortDirection, otherwise it's forced to descending order.

protected void SearchResultsView_OnSorting( ... ) {

  string originalExpression = e.SortExpression;

  if ( e.SortExpression.Equals( "Rating" ) ) {

      if ( ViewState[ "PreviousSortExpression" ] == null || 
           !ViewState[ "PreviousSortExpression" ].ToString().Equals( originalExpression ) ) {
            
        e.SortExpression = e.SortExpression + " DESC";
        
      } 

   }

  ViewState[ "PreviousSortExpression" ] = originalExpression;

}

That does it. Happy Sorting!