How To: Improve Column Sort Presentation for an ASP.NET GridView

I was recently browsing through some of the samples on the Ajax Rain site.  I came across what I thought was a nice presentation for an HTML Table that supports sorting by clicking on the column headers.  There are a 3 visual features that I like about this presentation that I am not using in the current web application I am working on:

1. Support for 3 different state icons within the column headers (not sorted, sorted ascending and sorted descending)

2. Allowing the user to sort by clicking anywhere within the column header instead of just on the header text

3. Change the header cell's background color (light blue in the screen shot below)

Live Demo | Download

[Update 9/17/2007] Download link was broken.  It is now fixed. Thanks Diego for pointing this out.

[Update 9/5/2007] Dave Ward pointed out a bug in my css that prevents this from working in FireFox and Safari.  In FireFox and Safari, the background-repeat attribute needs to be set for my .sortascheaderstyle and .sortdescheaderstyle classes.  I have updated the posting code samples, the live demo and download with this fix.  Thanks Dave!

Adding these features to an ASP.NET GridView is pretty easy to do.  Here is how you can incorporate it into your application ...

Add the GridView and the DataSource to the Page

The first step is to add the GridView and its corresponding DataSource to the page.  For my example, I am using an ObjectDataSource for fetching the customer rows.  Additionally, I have placed my GridView inside an UpdatePanel so only the GridView is refreshed when the user sorts on the different column headers.  Here is the markup for this piece.  The definitions for the CssClasses are at the end of the listing. 

<asp:UpdatePanel runat="server" UpdateMode="Conditional">
    <ContentTemplate>
        <asp:GridView 
            ID="gvCustomers" runat="server" CssClass="tablestyle" 
            AllowSorting="true" DataSourceID="odsCustomers" 
            OnRowDataBound="GvCustomers_RowDataBound" AutoGenerateColumns="false">
            <AlternatingRowStyle CssClass="alternatingrowstyle" />
            <HeaderStyle CssClass="headerstyle" />
            <RowStyle CssClass="rowstyle" />
            <Columns>
                <asp:BoundField HeaderText="ID" DataField="customerid" SortExpression="customerid" />
                <asp:BoundField HeaderText="Company" DataField="companyname" SortExpression="companyname" />
                <asp:BoundField HeaderText="Contact Name" DataField="contactname" SortExpression="contactname" />
                <asp:BoundField HeaderText="Contact Title" DataField="contacttitle" SortExpression="contacttitle" />
                <asp:BoundField HeaderText="Country" DataField="country" SortExpression="country" />
                <asp:BoundField HeaderText="Phone" DataField="phone" SortExpression="phone" />
            </Columns>
        </asp:GridView>
    </ContentTemplate>
</asp:UpdatePanel>

Handle the RowDataBound GridView Event

Next, in order to apply the different sort icons within in the header cells, we need to handle the RowDataBound event from the GridView so we can add the appropriate styling to the cell before it is rendered.  Within the event handler, we first determine which column is being sorted by by seeing which column's SortExpression property matches the SortExpression of the parent GridView.  Once we find the column, we can index into the GridViewRow's Cell's collection to set the CssClass for the header cell.  The code for the event handler is as follows ... .

protected void GvCustomers_RowDataBound(object sender, GridViewRowEventArgs e)
{
    GridView gridView = (GridView)sender;

    if (e.Row.RowType == DataControlRowType.Header)
    {
        int cellIndex = -1;
        foreach (DataControlField field in gridView.Columns)
        {
            e.Row.Cells[gridView.Columns.IndexOf(field)].CssClass = "headerstyle";
            
            if (field.SortExpression == gridView.SortExpression)
            {
                cellIndex = gridView.Columns.IndexOf(field);
            }
        }

        if (cellIndex > -1)
        {
            //  this is a header row,
            //  set the sort style
            e.Row.Cells[cellIndex].CssClass =
                gridView.SortDirection == SortDirection.Ascending
                ? "sortascheaderstyle" : "sortdescheaderstyle";
        }
    }
}

Define the Style Classes

Now that all of the markup and code is in place, we just need to define the appropriate style classes.  To support the feature of allowing the user to click anywhere within the column header, we need to apply the display:block style to the anchor's that are rendered within the column headers (thanks Tim Mackey for this tip).  Additionally, because I am applying the sortascheaderstyle and sortdescheaderstyle classes to the header cells, we will need to define these classes as well.  Here are the style definitions.  The sort icon references are defined as background-images ... 


.tablestyle{
   font-family:arial;
   margin:10px 0pt 15px;
   font-size: 8pt;
   border-color: #CDCDCD;
   width:850px;
   color: #3D3D3D;
}
.tablestyle td, .tablestyle th{
   border-color: #CDCDCD;
}
.alternatingrowstyle{
    background-color:#F0F0F6;
}
.headerstyle {
    background-color:#F0F0F6;
    background-image: url(img/sort_none.gif);
    background-repeat: no-repeat;
    background-position: center left;     
    padding-left: 20px;
}  
.headerstyle a{
   text-decoration:none;
    color:black;
   display:block;
}    
.rowstyle{
   background-color: #FFF;
}
.rowstyle td, .alternatingrowstyle td {
   padding: 4px;
}
.sortascheaderstyle{
    background-image: url(img/sort_asc.gif);     
    background-color: #8dbdd8;    
    background-repeat: no-repeat;
    background-position: center left;       
}
.sortdescheaderstyle{
    background-image: url(img/sort_desc.gif);     
    background-color: #8dbdd8;    
    background-repeat: no-repeat;
    background-position: center left;       
}   

That's it.  Enjoy!


TrackBack

TrackBack URL for this entry:
http://mattberseth.com/blog-mt/mt-tb.fcgi/64

Listed below are links to weblogs that reference How To: Improve Column Sort Presentation for an ASP.NET GridView:

» Thursday Linkorama from Ross Hawkins
Thursday Linkorama [Read More]

» A YUI DataTable Styled GridView from ASP.NET
A YUI DataTable Styled GridView Lately I have been spending more and more time browsing through the YUI [Read More]

Comments


Hey Mark,

Your gridview stuff is great. Have you given any thought to putting it all together with this column sorting, the column resizing and the paging controls at the bottom?

Hi Matt,

Sorry for messing your name up in the previous post. I was on my first cup of coffee or whatever. Anyways, I really enjoy your blog because you focus on the making components more usable and that's what my customers want and expect. Great ideas, great implementation.

--Geri

Posted by: jack on September 16, 2007 10:54 PM

hey matt, so great your gridview stuff. it will be really greater if you make all of these things all together like a custom gridview control... :P

Posted by: Diego on September 17, 2007 08:40 AM

Nice article, but the download link doesn't work. Thank you.


@Diego -

Thanks for the heads-up. It should be fixed now.

Matt.

@Geri/@jack-

I have started this, just haven't had time to complete it. Hopefully soon ...

Matt.

Great article! This is the cleanest approach I've seen yet!

One thing I added so that the individual column header styles don't get overwritten, was to replace the last line of actual code with:

e.Row.Cells[cellIndex].CssClass = gridView.Columns[cellIndex].HeaderStyle.CssClass + (gridView.SortDirection == SortDirection.Ascending ? " sortascheaderstyle" : " sortdescheaderstyle");

Posted by: fardoche on October 15, 2007 10:11 AM

2. Allowing the user to sort by clicking anywhere within the column header instead of just on the header text


strangely that part doesn't work. but works on the link. if i add a filter in the header, its possible to keep the sort?

regards

Posted by: Dan on October 19, 2007 10:46 AM

Great solution to the problem of how to show the user which column they are sorting by.

The only thing I had to change was that I have the AutoGenerateSelectButton set to "True", adding a column to the grid that was throwing the references off. I just added a simple +1 anywhere you reference columns.indexof.

I'm sure there's a more elegant approach then my solution but it works.

Posted by: Hans on December 30, 2007 01:47 PM

Hi,

How about sorting for customized column? Say, I add a column which is bounded value though RowDataBound Event and not retrieved from same data source.

Regards,

Hans

Posted by: Mahesh on March 28, 2008 03:17 PM

Hi,
I am trying to implement this. Just one problem. If my data column's size in gridview is small, then the title and the image in the header overlaps. If the column size is large, everything works fine.

Please help.

Thanks

Posted by: lbassil on May 30, 2008 08:33 AM

Hello All,

I am wondering how to get the cellIndex when the columns of the grid are autogenerated? The "gridView.Columns" returns an empty collection of columns. Any help?

Thanks.

Posted by: Josh on June 27, 2008 12:55 PM

How would you generalize this code so your not copying and pasting this code in every gridview? I guess you could create a base class for all your pages and then put this code in a method and call that method from each gridview's RowDataBound method. Any other ideas? How would you do it?

Post a comment

(If you haven't left a comment here before, you may need to be approved by the site owner before your comment will appear. Until then, it won't appear on the entry. Thanks for waiting.)

Sponsor

Recent Comments

  • Josh wrote: How would you generalize this code so your not copying and pasting this code in every gridview? I g...
  • lbassil wrote: Hello All, I am wondering how to get the cellIndex when the columns of the grid are autogenerated? ...
  • Mahesh wrote: Hi, I am trying to implement this. Just one problem. If my data column's size in gridview is small, ...
  • Hans wrote: Hi, How about sorting for customized column? Say, I add a column which is bounded value though RowD...
  • Dan wrote: Great solution to the problem of how to show the user which column they are sorting by. The only ...
  • fardoche wrote: 2. Allowing the user to sort by clicking anywhere within the column header instead of just on the he...
  • Jon Adams wrote: Great article! This is the cleanest approach I've seen yet! One thing I added so that the indivi...
  • Matt Berseth wrote: @Geri/@jack- I have started this, just haven't had time to complete it. Hopefully soon ... Matt....

Sponsor