Rick Strahl's Weblog  

Wind, waves, code and everything in between...
.NET • C# • Markdown • WPF • All Things Web
Contact   •   Articles   •   Products   •   Support   •   Advertise
Sponsored by:
West Wind WebSurge - Rest Client and Http Load Testing for Windows

Adding proper THEAD sections to a GridView


:P
On this page:

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:

<table>
   <tbody>
      <tr>  <-- header
      <tr>  <—detail row
      <tr>  <—detail row
   </tbody>
</table>

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]] });
    });
</script>

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("tbody").before("<thead><tr></tr></thead>");
        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;
this.gvCustomers.DataBind();

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.

Posted in ASP.NET  jQuery  

The Voices of Reason


 

AdamS
November 18, 2010

# re: Adding proper THEAD sections to a GridView

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.

rkirk92
November 18, 2010

# re: Adding proper THEAD sections to a GridView

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:
http://www.devcurry.com/2009/02/make-aspnet-gridview-generate-thead-and.html

Siderite
November 19, 2010

# re: Adding proper THEAD sections to a GridView

What is wrong with gridView.HeaderRow.TableSection=TableRowSection.TableHeader; ?

Rick Strahl
November 19, 2010

# re: Adding proper THEAD sections to a GridView

@Siderite - Uhm actually had no idea that exists. Why in the world isn't that the default if the grid actually supports it???

Don L
November 19, 2010

# re: Adding proper THEAD sections to a GridView

Rick,

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

Rick Strahl
November 19, 2010

# re: Adding proper THEAD sections to a GridView

@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:http://www.west-wind.com/westwindwebtoolkit/samples/Ajax/jqGrid.aspx 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.

Betty
November 19, 2010

# re: Adding proper THEAD sections to a GridView

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.

Andrew Brown
January 31, 2011

# re: Adding proper THEAD sections to a GridView

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.

Lester
March 05, 2011

# re: Adding proper THEAD sections to a GridView

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("tbody").before("<thead><tr></tr></thead>");
              jTbl.find("thead:first tr").append(jTbl.find("th"));
              jTbl.find("tbody tr:first").remove();
        }
});

Tom Knowlton
May 03, 2016

# re: Adding proper THEAD sections to a GridView

I am wondering if the same solution is available for the DataGrid?

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