search
Japanese Chinese Nederlands Espanol Italiano Deutsch Francais Twitter Rss Feeds
MicrosoftArticlesForumsFAQs
C# .NET
VB.NET
Visual Studio .NET
ADO.NET
Xml / Xslt
VB 6.0
.NET CF
GDI+
LINQ
Deployment
Security
FoxPro
Silverlight / WPF
Entity Framework
RIA Services

Web ProgrammingArticlesForumsFAQs
JavaScript
ASP
ASP.NET
Web Services

Non-MicrosoftArticlesForumsFAQs
NHibernate
Perl
PHP
Ruby
Java
Linux / Unix
Apple
Open Source

DatabasesArticlesForumsFAQs
SQL Server
Access
Oracle
MySQL
Other Databases

OfficeArticlesForumsFAQs
Excel
Word
Powerpoint
Outlook
Publisher
Money

Operating SystemsArticlesForumsFAQs
Windows 7
Windows Server
Windows Vista
Windows XP
Windows Update
MAC
Linux / UNIX

Server PlatformsArticlesForumsFAQs
BizTalk
Site Server
Exhange Server
IIS

Graphic DesignArticlesForumsFAQs
Macromedia Flash
Adobe PhotoShop
Expression Blend
Expression Design
Expression Web

OtherArticlesForumsFAQs
Subversion / CVS
Ask Dr. Dotnetsky
Active Directory
Networking
Uninstall Virus
Job Openings
Product Reviews
Search Engines
Resumes

 

ASP.NET - Sorting a GridView Bound to a Custom Data Object


By Bo Friesen
Printer Friendly Version
View My Articles
356 Views
    

This article presents a technique for sorting a GridView populated from a list of custom data objects. It relies on the view state and does not require additional calls to the database.


Sorting a GridView Bound to a Custom Data Object

 

The ease of binding an ArrayList of custom data objects to a GridView makes it a very attractive practice.  Of course, creating a grid will often come with the requirement of making the contents sortable.  This article presents a quick and easy way to facilitate sorting of custom data objects without additional calls to the database or data source.  It utilizes ASP .NET 2.0. in C#.  Let’s start with a simple custom data object called HoursBE (Business Entity).  It contains fields for name, hours worked, and date, as outlined below:

 

[Serializable]

public class HoursBE

{

    private string _name;

    private double _hours;

    private DateTime _date;

 

    public HoursBE()

    {

    }

 

    public string Name

    {

        get { return _name; }

        set { _name = value; }

    }

 

    public double Hours

    {

        get { return _hours; }

        set { _hours = value; }

    }

 

    public DateTime Date

    {

        get { return _date; }

        set { _date = value; }

    }

}

 

 

It is important that the class be serializable for storage in the ViewState or Session.  I’ve also included several different data types to illustrate subtle differences in how each is sorted.  The data object is bound to the GridView as shown below in the Page_Load method.  In this sample source code, the data is retrieved from an XML file, but the workings would be the same for a database call.  The hourList variable contains a list of HoursBE objects.

 

 

protected void Page_Load(object sender, EventArgs e)

    {

        HoursDAL hoursDAL = new HoursDAL();

        ArrayList hourList = hoursDAL.GetHours();

        ViewState["HourList"] = hourList;

        gvHours.DataSource = hourList;

        gvHours.DataBind();

    }

 

 

This produces the following GridView on an aspx page.

 

 

 

Here is a representation of the GridView mark up in the aspx page, with some key attributes highlighted in bold.

 

 

<asp:GridView ID="gvHours" runat="server" AllowSorting="True"

     AutoGenerateColumns="False" CellPadding="4" ForeColor="#333333"

     GridLines="None" OnRowCreated="gvHours_RowCreated"

     OnSorting="gvHours_Sorting" Style="z-index: 102; left: 65px;

     position: absolute; top: 84px" Width="513px">

            <FooterStyle BackColor="#507CD1" Font-Bold="True"

                   ForeColor="White" />

            <RowStyle BackColor="#EFF3FB" />

            <Columns>

                <asp:BoundField DataField="Name" HeaderText="Name"

                     SortExpression="Name" />

                <asp:BoundField DataField="Hours" HeaderText="Hours"

                     SortExpression="Hours">

                    <ItemStyle HorizontalAlign="Center" />

                </asp:BoundField>

                <asp:BoundField DataField="Date"

                     DataFormatString="{0:d}" HeaderText="Date"

                     SortExpression="Date">

                    <ItemStyle HorizontalAlign="Center" />

                </asp:BoundField>

            </Columns>

            <PagerStyle BackColor="#2461BF" ForeColor="White"

                        HorizontalAlign="Center" />

            <SelectedRowStyle BackColor="#D1DDF1" Font-Bold="True"

                        ForeColor="#333333" />

            <HeaderStyle BackColor="#507CD1" Font-Bold="True"

                        ForeColor="White" />

            <EditRowStyle BackColor="#2461BF" />

            <AlternatingRowStyle BackColor="White" />

        </asp:GridView>

 

 

First, the AllowSorting attribute must be set to true.  Do not set the EnableSortingAndPagingCallbacks attribute to true, or the sorting will not work.  You will also need OnSorting and OnRowCreatedMethods event handlers in the code behind.  Finally, the SortExpression must be set to the name of the data field on which you want to sort for each bound field.  In this simple case, it will be the fields themselves.

 

All of the code that follows is contained in the page code behind.  Now let’s take a look at the OnSorting event handler method, to examine the nuts and bolts of the sorting mechanism. 

 

 

    private const string ASCENDING = " ASC";

    private const string DESCENDING = " DESC";

 

    protected void gvHours_Sorting(object sender,

                                   GridViewSortEventArgs e)

    {

        string sortExpression = e.SortExpression;

        ViewState["SortExpression"] = sortExpression;

 

        if (GridViewSortDirection == SortDirection.Ascending)

        {

            GridViewSortDirection = SortDirection.Descending;

            SortGridView(sortExpression, DESCENDING);

        }

        else

        {

            GridViewSortDirection = SortDirection.Ascending;

            SortGridView(sortExpression, ASCENDING);

        }

    }

 

 

This method obtains the sort expression (data field name) from the GridViewSortEventArgs, and stores it in the view state for future reference.  It then performs a simple flip to determine whether the sort should be ascending, or descending.  The sort direction is contained within the following, small field.

 

 

    private SortDirection GridViewSortDirection

    {

        get

        {

            if (ViewState["sortDirection"] == null)

                ViewState["sortDirection"] = SortDirection.Ascending;

            return (SortDirection)ViewState["sortDirection"];

        }

        set { ViewState["sortDirection"] = value; }

    }

 

 

The actual sorting work is performed by the SortGridView method shown below.  This method basically creates a DataTable object that contains the same columns as the GridView and populates this table from data in the ArrayList that we stored in the view state earlier.  Note that the default data type for DataTable columns is a string, so it need to be explicitly set to Double for the hours and DateTime for the date, or those columns will not sort correctly.  It then creates a new sortable DataView object from the DataTable and sets the sort property to the sort expression and sort order.  After that, it is only a matter of setting the GridView’s data source property to the DataView and then binding it.

 

 

    private void SortGridView(string sortExpression, string direction)

    {

        ArrayList hourList = (ArrayList)ViewState["HourList"];

        DataTable dt = new DataTable();

        dt.Columns.Add("Name");

        dt.Columns.Add("Hours");

        dt.Columns["Hours"].DataType =

                        System.Type.GetType("System.Double");

        dt.Columns.Add("Date");

        dt.Columns["Date"].DataType =

                        System.Type.GetType("System.DateTime");

 

        foreach (HoursBE hours in hourList)

        {

            DataRow dr = dt.NewRow();

            dr["Name"] = hours.Name;

            dr["Hours"] = hours.Hours;

            dr["Date"] = hours.Date;

            dt.Rows.Add(dr);

        }

 

        DataView dv = new DataView(dt);

        dv.Sort = sortExpression + direction;

        gvHours.DataSource = dv;

        gvHours.DataBind();

    }

 

 

As an additional touch, we can add an up or down arrow to the column header to indicate whether the sort is ascending or descending.  We can do this by using the RowCreated event handler method we added to the GridView.  It is shown below.  It uses two methods to retrieve the current sort index (column name) and then set the image within that column.  This method fires ever time there is a new DataBind call on the GridView.

 

  

    protected void gvHours_RowCreated(object sender,

                                      GridViewRowEventArgs e)

    {

        if (e.Row.RowType == DataControlRowType.Header)

        {

            int sortColumnIndex = GetSortColumnIndex();

            if (sortColumnIndex != -1)

            {

                AddSortImage(sortColumnIndex, e.Row);

            }

        }

    }

 

 

The first method called by the event handler, GetSortColumnIndex, will check each column in the GridView against the sort index stored in the view state, as shown below.  It returns the column index when it finds a match, or a -1 if no match is found.

 

 

    private int GetSortColumnIndex()

    {

        foreach (DataControlField field in gvHours.Columns)

        {

            if (field.SortExpression ==

                         (string)ViewState["SortExpression"])

            {

                return gvHours.Columns.IndexOf(field);

            }

        }

        return -1;

    }

 

                                                      

The second method called by the event handler, AddSortImage, is shown below.  It creates an arrow up or down image based upon the sorting direction and then adds it to the controls of the appropriate column header.

 

 

private void AddSortImage(int columnIndex, GridViewRow headerRow)

    {

        // Create the sorting image based on the sort direction.

        Image sortImage = new Image();

        if (GridViewSortDirection == SortDirection.Ascending)

        {

            sortImage.ImageUrl = "~/images/uparrow.gif";

            sortImage.AlternateText = "Ascending Order";

        }

        else

        {

            sortImage.ImageUrl = "~/images/downarrow.gif";

            sortImage.AlternateText = "Descending Order";

        }

        // Add the image to the appropriate header cell.

        headerRow.Cells[columnIndex].Controls.Add(sortImage);

    }

 

 

The sort direction arrow is depicted below with the hours column sorted in descending order.

 


 

Source code for the slolution is available for download here.


Biography - Bo Friesen
Bo Friesen is a Senior .NET Developer with USFI Marketing Communications in Dallas, Texas. He has worked in the IT field for 16 years including the areas of new application development, consulting, enterprise computing, banking, and financial industries. A former paratrooper and cavalry officer, Bo still likes to devote time to military topics when possible.

button
Article Discussion: ASP.NET - Sorting a GridView Bound to a Custom Data Object
Bo Friesen posted at Tuesday, February 12, 2008 1:17 PM
Original Article
 

You should really use a comparer to do sorting
Ben Kubicek replied to Bo Friesen at Tuesday, March 04, 2008 2:54 PM

I wrote a similar article on the codeproject site.  Using objects with a generic list and binding them with an object datasource. If you create a comparer you can use that for sorting.

http://www.codeproject.com/KB/aspnet/GridViewObjectDataSource.aspx

 

Ben

 

Thank You
Bo Friesen replied to Ben Kubicek at Tuesday, March 04, 2008 5:55 PM

Many thanks, Ben.  I will check out that solution.  Always looking to improve.

Bo

 

Duplicate columns
James Bailey replied to Bo Friesen at Friday, January 02, 2009 6:13 PM

Code pieces below.  Thanks for the article, I was so relieved to find a solution to sorting a programmatically built GridView rather than implementing GridView in another class.  Thanks so much.  However, when I attempted to utilize this functionality my columns became duplicated, but yours are not.  If <BoundField...SortExpression...> is necessary for sort capability, and dataTable is the basis for GridView through which the columns are added, and data added to those columns, how then do I not duplicate the columns.  I'm sure I'm overlooking something.  If I remove the BoundField from the GridView, how then is e.SortExpression found.

Thanks so much for your article and any assitance you might offer for my question,

Sincerely,

James Bailey

ERDC, USACE

<asp:BoundField DataField="username" HeaderText="Username" SortExpression="username" />

dataTable1.Columns.Add("Username", System.Type.GetType("System.String"));

 

...creates two columns for Username.  The only one I can remove is the ASP column for BoundField, but that is where sortExpression is set.

 

Get automatic sort feature
Binu Thayamkery replied to Bo Friesen at Tuesday, April 07, 2009 2:25 PM

Nice article! You can get the automatic sort features, if the datasource (objectdatasource) is bound to a DataTable/DataView. So if you are dealing with a custom object or a generic list of custom object, you make a datatable from it and use it to bind to gridview. Its not a dirty trick, with help of reflection you can get some generic code written, see my post here -> http://dotnetarchitect.wordpress.com/2009/04/07/gridview-sorting-trick-when-using-object-datasource-with-custom-objects/

 

 

Good but a faster way with no DataTables
Justin LeCheminant replied to Bo Friesen at Friday, October 23, 2009 4:50 PM
Great article.

However there is no need to convert a generic List into a datatable.  The great thing about the List class is it has a Sort method that you can pass a delegate method into that will sort the List in the order you want.  So in your grid Sorting event handler you simply need to do code like the following:

List<T>.Sort( delegate compare code/method)

Then take your list and bind it to your grid.  This will save a lot of time over creating datatables.