Contact   •   Articles   •   Products   •   Search

Rick Strahl's Web Log

Wind, waves, code and everything in between...
ASP.NET • C# • HTML5 • JavaScript • AngularJs

Adding proper THEAD sections to a GridView

I’m working on some legacy code for a customer today and dealing with a page that has my favorite ‘friend’ on it: A GridView control. The ASP.NET GridView control (and also the older DataGrid control) creates some pretty messed up HTML. One of the more annoying things it does is to generate all rows including the header into the page in the <tbody> section of the document rather than in a properly separated <thead> section.

Here’s is typical GridView generated HTML output:

<table class="tablesorter blackborder" cellspacing="0" rules="all" border="1" 
id="Table1" style="border-collapse:collapse;"> <tr> <th scope="col">Name</th>
th scope="col">Company</th
th scope="col">Entered</th><th scope="col">Balance</th
> </tr>
tr> <td>Frank Hobson</td><td>Hobson Inc.</td>
td>10/20/2010 12:00:00 AM</td><td>240.00</td> </tr> ... </table>

Notice that all content – both the headers and the body of the table – are generated directly under the <table> tag and there’s no explicit use of <tbody> or <thead> (or <tfooter> for that matter).

When the browser renders this the document some default settings kick in and the DOM tree turns into something like this:

      <tr>  <-- header
      <tr>  <—detail row
      <tr>  <—detail row

Now if you’re just rendering the Grid server side and you’re applying all your styles through CssClass assignments this isn’t much of a problem. However, if you want to style your grid more generically using hierarchical CSS selectors it gets a lot more tricky to format tables that don’t properly delineate headers and body content. Also many plug-ins and other JavaScript utilities that work on tables require a properly formed table layout, and many of these simple won’t work out of the box with a GridView. For example, one of the things I wanted to do for this app is use the jQuery TableSorter plug-in which – not surprisingly – requires to work of table headers in the DOM document. Out of the box, the TableSorter plug-in doesn’t work with GridView controls, because the lack of a <thead> section to work on.

Luckily with a little help of some jQuery scripting there’s a real easy fix to this problem. Basically, if we know the GridView generated table has a header in it, code like the following will move the headers from <tbody> to <thead>:

<script type="text/javascript">
    $(document).ready(function () {
        // Fix up GridView to support THEAD tags            
        $("#gvCustomers tbody").before("<thead><tr></tr></thead>");            
        $("#gvCustomers thead tr").append($("#gvCustomers th"));
        $("#gvCustomers tbody tr:first").remove();

        $("#gvCustomers").tablesorter({ sortList: [[1, 0]] });

And voila you have a table that now works with the TableSorter plug-in.

If you use GridView’s a lot you might want something a little more generic so the following does the same thing but should work more generically on any GridView/DataGrid missing its <thead> tag:

function fixGridView(tableEl) {       
    var jTbl = $(tableEl);
    if(jTbl.find("tbody>tr>th").length > 0) {
        jTbl.find("thead tr").append(jTbl.find("th"));
        jTbl.find("tbody tr:first").remove();

which you can call like this:

   $(document).ready(function () {
        fixGridView( $("#gvCustomers") );
        $("#gvCustomers").tablesorter({ sortList: [[1, 0]] });

Server Side THEAD Rendering
[updated from comments 11/21/2010]

Several commenters pointed out that you can also do this on the server side by using the GridView.HeaderRow.TableSection property to force rendering with a proper table header. I was unaware of this option actually – not exactly an easy one to discover. One issue here is that timing of this needs to happen during the databinding process so you need to use an event handler:

this.gvCustomers.DataBound += (object o, EventArgs ev) =>                        
    gvCustomers.HeaderRow.TableSection = TableRowSection.TableHeader;

this.gvCustomers.DataSource = custList;

You can apply the same logic for the FooterRow. It’s beyond me why this rendering mode isn’t the default for a GridView – why would you ever want to have a table that doesn’t use a THEAD section??? But I disgress :-)

I don’t use GridViews much anymore – opting for more flexible approaches using ListViews or even plain code based views or other custom displays that allow more control over layout, but I still see a lot of old code that does use them old clunkers including my own :) (gulp) and this does make life a little bit easier especially if you’re working with any of the jQuery table related plug-ins that expect a proper table structure.

Make Donation
Posted in ASP.NET  jQuery  

Feedback for this Post

# re: Adding proper THEAD sections to a GridView
by AdamS November 18, 2010 @ 5:59pm
You can also create a base class that hooks into the DataBoud event and call this...

 if (HeaderRow != null)
      HeaderRow.TableSection = TableRowSection.TableHeader;
 if (FooterRow != null && ShowFooter)
      FooterRow.TableSection = TableRowSection.TableFooter;

I've used this many times due to the jQuery tableSorter plugin.
# re: Adding proper THEAD sections to a GridView
by rkirk92 November 18, 2010 @ 6:12pm
I like the jQuery implementation, I did also find an article at DevCurry that does this via the UseAccessibleHeader attribute on the PreRender event in the Gridview. It can be found here:
# re: Adding proper THEAD sections to a GridView
by Siderite November 19, 2010 @ 12:43am
What is wrong with gridView.HeaderRow.TableSection=TableRowSection.TableHeader; ?
# re: Adding proper THEAD sections to a GridView
by Rick Strahl November 19, 2010 @ 2:26am
@Siderite - Uhm actually had no idea that exists. Why in the world isn't that the default if the grid actually supports it???
# re: Adding proper THEAD sections to a GridView
by Don L November 19, 2010 @ 8:23am

Have you tried the JQGrid plugin for .NET? If you (or other readers) have any thoughts to share on it, I would appreciate it. I have done some work converting a GridView to this JQueryUI plugin and, while it seems capable, I am not sure it has saved me any work and the documentation is spotty. I usually check code from their demo pages because they are more complete than the Wiki. Thanks, Don
# re: Adding proper THEAD sections to a GridView
by Rick Strahl November 19, 2010 @ 4:17pm
@Don - yes I've used it and it works well, but as you point out it isn't easy to use and hard to figure out due to the docs. It does work nice once set up and it serves me really well in a few places. I think Ruben has some MVC and WebForms (?) controls that wrap jqGrid for sale. That probably makes life a bit easier than the raw JavaScript configuration.

FWIW, I have a Web Toolkit sample for jqGrid here: which uses the Toolkits AJAX backend functions to retrieve the data for the grid. I've been thinking about building a control that wraps the grid setup and AJAX callback automatically to make this easier, but haven't found the time to do this yet.
# re: Adding proper THEAD sections to a GridView
by Betty November 19, 2010 @ 4:39pm
I believe gridview.UseAccessibleHeader = true; changes td's to th's too. But you still need the TableSection change to put them in a thead.
# re: Adding proper THEAD sections to a GridView
by Andrew Brown January 31, 2011 @ 1:14pm
Awesome...this post just helped me out with the jQuery tablesorter plugin.

Wouldn't it be better to hook up to the PreRender event so that the fix will occur on post backs where the data is not re-bound?

gvCustomers.PreRender += new EventHandler(GridViewHelper.GridViewTagFix);

Rick, thanks for all the posts! There is some amazing content here.
# re: Adding proper THEAD sections to a GridView
by Lester March 05, 2011 @ 7:41am
Nice solution using jQuery...

I should modify a little the code that you propose to work with a GridView with paging enabled. The problem is that the paging is rendered as a table like the main GridView, and your current code put the THEAD with the header columns to both tables, the main GridView and the paging. The fix consists in take only the first THEAD found to append the headers. Here the code:

$("table.grid").each(function () {
        var jTbl = $(this);
        if (jTbl.find("tbody>tr>th").length > 0) {
              jTbl.find("thead:first tr").append(jTbl.find("th"));
              jTbl.find("tbody tr:first").remove();

West Wind  © Rick Strahl, West Wind Technologies, 2005 - 2015