« Video: Using the New ASP.NET ListView Control | Main | Video: First Look at Visual Studio .NET 2008 and the LinqDataSource »

Freeze ASP.NET GridView Headers by Creating Client-Side Extenders

Lately I've been working on a pet project where I needed to freeze a GridView header so that it didn't move while the grid records were scrolled by an end user.  After searching the Web I came across a lot of "pure" CSS ways to freeze a header.  After researching them more they tend to rely on a lot of CSS hacks to do the job and require HTML to be structured in a custom way.  I also found a lot of custom GridView classes (another one here), and more.  Some of the solutions were really good while others seemed a bit over the top and required a lot of custom C# or VB.NET code just to do something as simple as freezing a header.  Freezing a header requires CSS and potentially JavaScript depending upon what needs done.  Regardless if CSS or JavaScript are used, these are client-side technologies of course.  I started to write my own derivative of the GridView class but quickly realized that it made more sense to use the standard GridView control and simply extend it from the client-side.  An example of what I was after is shown next:



In the past I've always made a scrollable GridView by wrapping a <div> tag around the control, adding a table with header rows inside the <div> (but above the GridView control code), and setting the CSS overflow property on the wrapper div to "auto".  Scott Mitchell has a nice article on doing this with a DataGrid and you can see a sample of doing something like that at my XML for ASP.NET Developers Website if you're interested.  This technique works well but makes it a bit hard to line up the header and child rows perfectly (although it can be done).

I decided to build my own client-side GridView "extender" code to accomplish the task since I didn't want to worry about yet another GridView assembly being in my toolbox just to freeze a header.  It appears to work great in IE6+ and Firefox 2 (haven't tried FireFox 1 – 1.5) although I'm sure there are some ways it could be improved and I can't say I've done extensive testing.  The code basically grabs the first header row from the GridView using JavaScript and moves it inside of a THEAD tag so that CSS can easily be applied to all of the TH tags and things work well in FireFox.  It then applies a few styles to the appropriate items within the GridView HTML that is generated. 

The JavaScript and CSS code is shown below.  On online version can be viewed here.

<style type="text/css">
    
.WrapperDiv {
        width
:800px;height:400px;border: 1px solid black;
    
}        
    
.WrapperDiv TH {
        position
:relative;
    
}
    
.WrapperDiv TR 
    
{
        
/* Needed for IE */
        height
:0px;
    

</
style>
<script>
    
function onLoad()
    {
        FreezeGridViewHeader(
'GridView1','WrapperDiv');
    
}    
    
    
    
function FreezeGridViewHeader(gridID,wrapperDivCssClass) 
    {
        
/// <summary>
        ///   Used to create a fixed GridView header and allow scrolling
        /// </summary>
        /// <param name="gridID" type="String">
        ///   Client-side ID of the GridView control
        /// </param>
        /// <param name="wrapperDivCssClass" type="String">
        ///   CSS class to be applied to the GridView's wrapper div element.  
        ///   Class MUST specify the CSS height and width properties.  
        ///   Example: width:800px;height:400px;border:1px solid black;
        /// </param>
        
var grid = document.getElementById(gridID);
        if 
(grid !'undefined')
        {
            grid.style.visibility 
'hidden';
            var 
div = null;
            if 
(grid.parentNode !'undefined'
            {
                
//Find wrapper div output by GridView
                
div grid.parentNode;
                if 
(div.tagName == "DIV")
                {
                    div.className 
wrapperDivCssClass;  
                    
div.style.overflow "auto";                   
                
}
            }                
            
//Find DOM TBODY element and remove first TR tag from 
            //it and add to a THEAD element instead so CSS styles
            //can be applied properly in both IE and FireFox
            
var tags grid.getElementsByTagName('TBODY');
            if 
(tags !'undefined')
            {
                
var tbody tags[0];
                var 
trs tbody.getElementsByTagName('TR');
                var 
headerHeight 8;
                if 
(trs !'undefined'
                {
                    headerHeight +
trs[0].offsetHeight;
                    var 
headTR tbody.removeChild(trs[0]);
                    var 
head = document.createElement('THEAD');
                    
head.appendChild(headTR);
                    
grid.insertBefore(head, grid.firstChild);
                
}
                
//Needed for Firefox
                
tbody.style.height 
                  
(div.offsetHeight -  headerHeight) + 'px';
                
tbody.style.overflowX "hidden";
                
tbody.overflow 'auto';
                
tbody.overflowX 'hidden';
            
}
            grid.style.visibility 
'visible';
        
}
    }
</script>

This solution works well but it means I'd have to reference a CSS file and JavaScript file for each GridView control that needs a frozen header.  I decided to build an ASP.NET AJAX Extender control based upon the ASP.NET AJAX Toolkit which turned out to be fairly straightforward.  This means that I have to reference an assembly in my project (which I complained about above), but I can still use the stock GridView control.  I'll talk more about the extender control  (called GridViewHeaderExtender) in a future post.  Those interested in seeing the code can download it here.  The extender seems to work great with IE6+ and FireFox 2 but doesn't work with Safari on Windows (very few of the frozen header solutions I found online worked with Safari). 

Posted on Tuesday, July 31, 2007 at 03:50PM by Registered CommenterDan Wahlin in | Comments8 Comments | References1 Reference

PrintView Printer Friendly Version

EmailEmail Article to Friend

References (1)

References allow you to track sources for this article, as well as articles that were written in response to this article.
  • Response
    Providing a fixed header is much simpler than you make think. All you have to do is use CSS. Tested with IE6 and IE7 only!

Reader Comments (8)

Nice Dan! One little nit - it'd be nice if the scrollbar didn't include the header ie. terminated below the header. But not sure if that would work with your approach.

Cool idea - I have to play around with this
August 1, 2007 | Unregistered CommenterRick Strahl
Oh funny - in Firefox it DOES actually terminate below the header. Hmmm...
August 1, 2007 | Unregistered CommenterRick Strahl
Hey Rick. I couldn't figure out how to move the scroll bar below the header in IE but I agree that would be nice. Firefox does it because it relies on the TBODY tag's overflow:auto CSS to fix the header and do the scrolling of the TBODY content (which is why I had to move the TH tags the GridView generates up into a THEAD tag). IE doesn't work that way though...relies on position:relative for the TH tags. I haven't tried playing around with margins for the scroll bar in IE but if you come across anything like that let me know.
August 1, 2007 | Unregistered CommenterDan Wahlin
I played around with this a bit last night actually mainly because I had no idea that you this would work at all, but I don't think this can be done in IE. The scrollbar in IE is from the DIV whereas in Mozilla it's actually for the TBody.

Yet another place where it would be nice if IE followed the rules.

Great idea though - I have a couple of places where this will be very useful.
August 1, 2007 | Unregistered CommenterRick Strahl
I was just asked to implement this onto an existing page. After looking all over the place, this is a fairly clean approach. Thanks!
August 7, 2007 | Unregistered CommenterT Wood
Hi Dan,

I recent ran into this. This is exactly what i was looking for.

For the web app i am building i also implemented some custom Javascript for saving & loading of the scroll position. After implementing your code my scrollbar positioning saver/loader failed to function. In an effort to re-establish the scroll saver/loader i removed your JavaScript. This still left the style sheet elements implemented. I added an overflow: auto; to the main style.
'.WrapperDiv'

The result was rather surprising, the scroll saver/loader worked as intended and the Header remained frozen. The style ...

.WrapperDiv TH {
position:relative;
}

Did all the work for me.

So i am a bit puzzled as to why all the JavaScript was needed.

Cheers,
Teh Confused Mike. :)
August 27, 2007 | Unregistered CommenterMichael
Cool. And to add a horizontal scroll bar to this, we could just....um...uhh.... Hmmm...
August 30, 2007 | Unregistered Commenterpaul
Hi,
I need both horizantal, virtical scroll bars along with fixed headers.


so,
I made overflow: scroll

then the header came out of the div tag px
region.

Help???

September 13, 2007 | Unregistered Commenterpadma
Editor Permission Required
You must have editing permission for this entry in order to post comments.