Sorting an ASP.NET GridView with a custom data source

The other day, I had to present some data in an ASP.NET GridView control and I had to allow sorting of some of the columns. This is easy if the data source is based on e.g. an SQL data source but not particularly easy when using a custom data source. In my case, I had a simple array of custom objects containing invoice data.

There are many solutions for this problem on the web but none of them really seemed to do the trick in my case. Many of the solutions I could find rely on converting the data source into a DataTable object but this is not a good idea when sorting needs to be applied to many fields because the DataTable needs to have these fields added manually.

So without further ado, here is a simple solution for the problem. First, let’s say our custom data source consists of an array of Person objects, defined like this:

class Person {
  string Name { get; set; }
  DateTime BirthDay { get; set; }
}

The GridView to bind the persons to is very simple:

<asp:GridView ID="PersonGridView" runat="server"
  AutoGenerateColumns="false"
  AllowSorting="true"
  OnSorting="personGridView_Sorting">
  <Columns>
    <asp:BoundField DataField="Name" SortExpression="Name" />
    <asp:BoundField DataField="BirthDay" SortExpression="BirthDay" />
  </Columns>
</asp:GridView>

The SortExpression has to be exactly the same name as the field. The sort direction and sort expression are stored in the ViewState and accessed by two fields in code-behind:

private SortDirection CurrentSortDirection {
  get {
    if (ViewState["SortDirection"] == null)
      ViewState["SortDirection"] = SortDirection.Ascending;
    return (SortDirection)ViewState["SortDirection"];
  }
  set { ViewState["SortDirection"] = value; }
}

private string CurrentSortExpression {
  get {
    if (ViewState["SortExpression"] == null)
      ViewState["SortExpression"] = "Name";
    return (string)ViewState["SortExpression"];
  }
  set { ViewState["SortExpression"] = value; }
}

Notice that I have chosen to have Name sorted in ascending order by default. This is of course not necessary but convenient for later. Next up is the OnSorting event:

protected void personGridView_Sorting(object sender, GridViewSortEventArgs e) {
  CurrentSortExpression = e.SortExpression;

  // Ignore e.SortDirection since it is always Ascending, an apparent bug in .NET
  if (CurrentSortDirection == SortDirection.Ascending)
    CurrentSortDirection = SortDirection.Descending;
  else
    CurrentSortDirection = SortDirection.Ascending;

  BindPersonData();
}

Finally, we can write our binding method.

private void BindPersonData() {
  Person[] persons = GetPersonArrayFromSomewhere();

  // Sort the persons, if sorting is applied.
  if (!string.IsNullOrEmpty(CurrentSortExpression)) {
    System.Reflection.PropertyInfo personInfo = (typeof(Person)).GetProperty(CurrentSortExpression);
    if (CurrentSortDirection == SortDirection.Ascending) {
      persons = persons
        .OrderBy(person => personInfo.GetValue(person, null))
        .ToArray();
    }
    else {
      persons = persons
        .OrderByDescending(person => personInfo.GetValue(person, null))
        .ToArray();
    }
  }

  PersonGridView.DataSource = persons;
  PersonGridView.DataBind();
}

The sorting can also be applied with a different sorting method. I happen to like Linq so that is why I have used it here. The key thing to notice here is how the correct field for sorting is found with the use of the reflection framework:

System.Reflection.PropertyInfo personInfo = (typeof(Person)).GetProperty(CurrentSortExpression);

That is why it is essential that the SortExpression value in the GridView is the same as the field’s name.

Oh and by the way, paging with a custom data source is very easy. Add this to your GridView:

AllowPaging="true"
OnPageIndexChanging="personGridView_PageIndexChanging"

and this method to the code-behind:

protected void personGridView_PageIndexChanging(object sender, GridViewPageEventArgs e) {
  PersonGridView.PageIndex = e.NewPageIndex;
  BindPersonData();
}

Leave a comment

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.