ASP.NET: Sorting a GridView with custom objects

This article builds upon the code started in GridView and ObjectDataSource with custom objects.

An ObjectDataSource really only works with two types of data, DataTables and object that implement IEnumerable.  If you pass it any other type of object, it is converted into an IEnumerable object.  DataTables have sorting abilities built-in, custom objects are required to use the SortParameterName of the ObjectDataSource.

The idea is pretty simple, taking our ObjectDataSource tag from our previous version we add SortParameterName, and enable sorting on the GridView:

<asp:ObjectDataSource ID="ObjectDataSource1" runat="server" 
    DataObjectTypeName="Person" TypeName="PersonAdapter" 
    DeleteMethod="DeletePerson" InsertMethod="InsertPerson"
    SelectMethod="GetAll" UpdateMethod="UpdatePerson" 
    SortParameterName="sortExpression" />
    
<asp:GridView ID="GridView1" runat="server" AllowPaging="True" 
    DataKeyNames="Name" DataSourceID="ObjectDataSource1" 
    AutoGenerateDeleteButton="True" AutoGenerateEditButton="True"
    AutoGenerateColumns="True" AllowSorting="true" />

Next, we add a method to our TableAdapter to handle this parameter:

public PersonList GetAll(String sortExpression) {
    PersonList people = GetAll();

    switch (sortExpression) {
        case "Name":
        case "Name ASC":
            people.Sort(PersonComparer.CompareByName);
            break;

        case "Name DESC":
            people.Sort(PersonComparer.CompareByNameDesc);
            break;

        case "Title":
        case "Title ASC":
            people.Sort(PersonComparer.CompareByTitle);
            break;

        case "Title DESC":
            people.Sort(PersonComparer.CompareByTitleDesc);
            break;
    }

    return people;
}

The SortParameterName property is passed a string of comma-separated field names, optionally followed by "ASC" or "DESC" to indicate direction.  For this example, I'm not handling multiple field sorts.  Also, notice I did not add an Attribute to this method as I did in the previous methods - this is because I do not want this method listed in the ObjectDataSource configure wizard, and the sortExpression will be passed in automatically anytime SortParameterName is set.

The PersonComparer class is a helper class I created to manage the sort logic.  Since my PersonList is really a List<>, I can use the Sort method to do most the work, I just need to tell it how to compare objects.  Here is the code for the class:

using System;
using System.Collections.Generic;

sealed class PersonComparer
{
    private static IComparer<Person> _compareByName = new _sortName(false);
    public static IComparer<Person> CompareByName { get { return _compareByName; } }

    private static IComparer<Person> _compareByNameDesc = new _sortName(true);
    public static IComparer<Person> CompareByNameDesc { get { return _compareByNameDesc; } }

    private class _sortName : IComparer<Person> {
        bool _reverse;
        public _sortName(bool reverse) {
            this._reverse = reverse;
        }

        #region IComparer<Person> Members

        public int Compare(Person x, Person y) {
            if (_reverse) return y.Name.CompareTo(x.Name);
            else return x.Name.CompareTo(y.Name);
        }

        #endregion
    }

    private static IComparer<Person> _compareByTitle = new _sortTitle(false);
    public static IComparer<Person> CompareByTitle { get { return _compareByTitle; } }

    private static IComparer<Person> _compareByTitleDesc = new _sortTitle(true);
    public static IComparer<Person> CompareByTitleDesc { get { return _compareByTitleDesc; } }

    private class _sortTitle : IComparer<Person> {
        bool _reverse;
        public _sortTitle(bool reverse) {
            this._reverse = reverse;
        }

        #region IComparer<Person> Members

        public int Compare(Person x, Person y) {
            if (_reverse) return y.Title.CompareTo(x.Title);
            else return x.Title.CompareTo(y.Title);
        }

        #endregion
    }
}

The class is sealed just to be on the safe side - no one should be deriving anything from this class.  In fact, everything is static so there is no need to create an instance of the class to use it.  The List.Sort method has an overload to take an object that supports IComparer<>, and this is used for the actual comparison.  A boolean is used to control the sort direction, but to hide this implementation detail from the user a set of public static members are exposed.

If you think this is starting to add up to a good deal of code, well I agree.  Some of this could be trimmed down using reflection, but I tend to shy away from reflection given it's reputation for performance problems.  If I'm working with a small set of fields (like managing role providers), this is my preferred method, but when it's time to scale up there is another option: DataTables.  DataTables aren't tied down to XSD's and TableAdapters, they are free to date other people, and in the next article (in what is quickly becoming a series) we will look at moving this code from a List generic to a DataTable.

Update: If you are curious to see another approach, Dylan has started a set of posts on GridView sorting using reflection. Well worth the read.

Posted By Mike On Tuesday, April 17, 2007
Filed under asp.net gridview | Comments (11)

Submit this story to DotNetKicks   

Dave Johnson - Thursday, July 19, 2007 11:47:37 PM

Thanks so much for posting this. Very clearly explained and it solved my sorting problems!

Cheers

Brent - Wednesday, December 05, 2007 5:34:36 PM

Hi Mike,
Great article!
I am really interested in the next article you advertise as moving this code from a List to a Datatable, but I can't seem to find it. Is it up on your site?
Thanks

Mike - Thursday, December 06, 2007 2:05:02 AM

Hi Brent - I never did post the next article, doh! Look for a post soon to complete the trilogy!

Mojtaba Vali - Thursday, May 01, 2008 7:12:21 AM

what about a 3tier program and bind dynamically?(only a dataset)

pradnya - Friday, February 20, 2009 1:17:16 AM

m trying to filter teleric grid column using isnull fitering option in .net 3.0 version but it dispalys following error message

Index was out of range. Must be non-negative and less than the size of the collection.
Parameter name: index

please help regarding this.
Thanks

Mike - Friday, February 20, 2009 9:06:11 AM

I've not used Telerik's Grid control, you should check the support section of their site, and forums at http://www.telerik.com/support.aspx

Ammar - Monday, April 27, 2009 9:43:03 AM

I can't speak enough about your nice article.

It helped me out so much, and I am happy to post this recommendation.

Anybody looking for sorting prototype, just get in this article without seeing the others, I already get tuff of seeing other !


Thank you once again

Cobi - Tuesday, June 02, 2009 7:48:43 AM

Hi Mike,
Nice article!
I have implemented your solution way in my project an i'm very happy for the solution.
But I need a solution for filtering the gridview?
When I try to use the objectdatasource1.filterExpression = String.Format("(NAME LIKE '{0}')", txtSearchByName.Text); I got a error meesage it's only possible when the Select method returns DataSet or DataTable.

Thank you once again

Spammer who tried to fake a comment for link bait - Friday, June 26, 2009 7:58:33 AM

I've been trying for over a month to get custom paging working with a gridview with no success, but I got yours working on my data quickly and easily. Thanks very much for the great article. The only addition I need is how to reduce the page numbers from being spread across the page when there is a large number of records returned, for example 1,2,3, 99,100, etc I would like 1-10... 11-20, etc. Can you please provide the code to do this?

Sim - Monday, October 18, 2010 10:50:09 AM

Exellent article. Thank you so much!!!

anjali - Friday, March 04, 2011 3:08:47 AM

Thanks a lot Mike you saved my time as i was searchign how to implement ,it is workign fine for me . i am using Gridview with List<Class> .

Anjali

Leave a comment



Your name:
 

Your email (not shown):
 
Will display your Gravatar image.

Your website (optional):



About Michael

Michael C. Neel, born 1976 in Houston, TX and now live in Knoxvile, TN. Software developer, currently .Net focused. Board member of ETNUG and organizes CodeStock, East Tennessee's annual developers conference. .Net speaker, a Microsoft ASP.NET MVP and ASPInsider. Co-Founder of FuncWorks, LLC and GameMarx.

Proud father of two amazing girls, Rachel and Hannah, and loving husband to Cicelie who inflates and pops his ego as necessary.

 Subscribe to ViNull.com |  Comments

Follow me on Twitter | Contact Me

Related Posts

ASP.NET: GridView and ObjectDataSource with custom objects

The GridView control is one of the great time savers of ASP.NET.  Sorting, paging, editing, deleting, and even AJAX (set EnableSortingAndPagingCallbacks ... Read more

ASP.NET: Totaling a GridView part 2, the SQL

Not long ago, I wrote a quick guide on adding a total to a GridView.  It was based upon building a cumulative total while looping though the GridView ... Read more

ASP.NET: Adding a total to gridview

One difference between learning the framework and using the framework is rarely anything ever done the way you learned it. The GridView control is a perfect ... Read more

ASP.NET: Cannot use a leading .. to exit above the top directory

This is a really quick post, mostly for myself so the next time I have this issue I can find the answer (yes, I often search my blog before google). The ... Read more

ASP.NET: Creating a UserControl with Child Content

I love ASP.NET User Controls, aka “ascx” files.  These little guys are great for reusable content and dividing up the components of a website.  ... Read more

XNA 3D Primer by Michael C. Neel

XNA 3D Primer by Michael C. Neel
Buy Now: [ Amazon ] [ Wrox ]

GameMarx

CodeStock

ASPInsiders Member

ETNUG Member