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.

<styletype="text/css">
.WrapperDiv{
width
:800px;height:400px;border:1pxsolidblack;
}
.WrapperDivTH{
position
:relative;
}
.WrapperDivTR
{
/*NeededforIE*/
height
:0px;
}
</
style>
<script>
functiononLoad()
{
FreezeGridViewHeader(
'GridView1','WrapperDiv');
}


functionFreezeGridViewHeader(gridID,wrapperDivCssClass)
{
///<summary>
///UsedtocreateafixedGridViewheaderandallowscrolling
///</summary>
///<paramname="gridID"type="String">
///Client-sideIDoftheGridViewcontrol
///</param>
///<paramname="wrapperDivCssClass"type="String">
///CSSclasstobeappliedtotheGridView'swrapperdivelement.
///ClassMUSTspecifytheCSSheightandwidthproperties.
///Example:width:800px;height:400px;border:1pxsolidblack;
///</param>
vargrid=document.getElementById(gridID);
if
(grid!='undefined')
{
grid.style.visibility
='hidden';
var
div=null;
if
(grid.parentNode!='undefined')
{
//FindwrapperdivoutputbyGridView
div=grid.parentNode;
if
(div.tagName=="DIV")
{
div.className
=wrapperDivCssClass;
div.style.overflow="auto";
}
}
//FindDOMTBODYelementandremovefirstTRtagfrom
//itandaddtoaTHEADelementinsteadsoCSSstyles
//canbeappliedproperlyinbothIEandFireFox
vartags=grid.getElementsByTagName('TBODY');
if
(tags!='undefined')
{
vartbody=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);
}
//NeededforFirefox
tbody.style.height=
(div.offsetHeight-headerHeight)+'px';
tbody.style.overflowX="hidden";
tbody.overflow='auto';
tbody.overflowX='hidden';
}
grid.style.visibility
='visible';
}
}
</script>

This solutionworks 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).

Published Tuesday, July 31, 2007 8:30 PM by dwahlin
Filed under: , ,

Comments

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

Tuesday, July 31, 2007 7:18 PM by Dwain Gleason

Looks like a nice solution but the example doesn't seem to work in my IE6 browser - lots of room between rows and the header scrolls right off. I'll download and try it out though. Seems to work fine in Firefox 2.

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

Tuesday, July 31, 2007 7:48 PM by dwahlin

Yep...you're right. I moved the div height and width out of the CSS and into the JS code and it messed it up on IE6. That's what I get for thinking something simple like that wouldn't affect IE6. I'll look into it and post and work-around. :-)

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

Tuesday, July 31, 2007 11:28 PM by dwahlin

I've uploaded some new code that should fix the issue in IE6. It acts really wierd when I try to set the div height and width through script so I've changed things up a little bit to get it working in IE6, IE7 and Firefox 2.

# University Update-AJAX-Freeze ASP.NET GridView Headers by Creating Client-Side Extenders

Pingback from University Update-AJAX-Freeze ASP.NET GridView Headers by Creating Client-Side Extenders

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

Wednesday, August 01, 2007 8:10 AM by coolpran

This is great.

But is there a similar solution to freeze the first column of a grid?

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

Wednesday, August 01, 2007 11:40 AM by dwahlin

You could do that as well. The following code shown in the blog moves the appropriate "frozen" header row into a new tag called THEAD.

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);

}

If you moved trs[0] (the header) and trs[1] (the first data row) into THEAD you could fix that row too although you'd have to set each TR's CSS position to "relative" for IE. It would take some work to play around with the styles, but the core of the code is already done.

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

Wednesday, August 01, 2007 8:36 PM by Dave T

Have you tried it on Opera and the new Safari for windows?

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

Thursday, August 02, 2007 12:32 AM by dwahlin

I tried it on Safari and it scrolls but the header doesn't stay frozen (I mention Safari in the post above). I haven't tried it on Opera though. The initial goal was to target IE and Firefox but if you happen to know of any changes that could be made to get it going on Safari and Opera please post them as I'd be interested in enhancing the code to work on those as well.

# Video: Using the New ASP.NET ListView Control

Thursday, August 02, 2007 3:39 AM by Dan Wahlin's WebLog

ASP.NET 3.5 introduces a new control called the ListView that allows developers to have 100% control

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

Thursday, August 02, 2007 4:12 AM by bloggingdeveloper

I implemented a gridview last year with freezed headers. However, I used jquery.

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

Wednesday, August 08, 2007 8:20 PM by theren maky

how can i maintain the position in postback

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

Thursday, August 09, 2007 1:43 AM by dwahlin

Theren,

To maintain the scroll position you'd have to add some JavaScript to track the existing position and write it to a hidden field (or store it some other way). You might want to check out how ASP.NET does it when you add the MaintainScrollPostionOnPostBack attribute to the page directive. They add X and Y scroll position hiddens and use JavaScript to move the scroll bar back to where it was when the page reloads. You'd have to write similar code.

# JavaScript and CSS Resources

Thursday, August 16, 2007 10:20 PM by Radical Development

JavaScript and CSS Resources

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

Tuesday, August 21, 2007 10:05 AM by Mark

I really like this approach Dan. It works well when sorting in the gridview as long as it's not in an update panel. When in an update panel the grid, if there, becomes hidden.

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

Friday, August 24, 2007 9:41 AM by James Freeman

Good stuuf.

Having an issue with firefox though when scrolling the table cells do not scroll although the test does. This means i can get text half in one cell, half in another!

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

Tuesday, August 28, 2007 10:25 AM by Bhanuprakash

Man, you are saviour!!

This is exactly what I was looking for!!

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

Tuesday, August 28, 2007 4:46 PM by HMote

If you have a border on your GridView of size 2px, just change '.WrapperDiv TH {position:relative;}' in the <style> section to '.WrapperDiv TH {position:relative;top:-2px;}'. This will fix your cell values showing above your header when scrolling down. Adjust the '2px' in the above example to whatever size your border is around your 'GridView1'.

Just for the ones that don't know...

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

Wednesday, August 29, 2007 5:55 PM by HMote

I'm getting a javascript error that says object required on this line - grid.style.visibility = 'hidden';

Any ideas?

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

Thursday, August 30, 2007 10:42 AM by dwahlin

Make sure that your grid is being found and that the ID it's looking for matches with the client-side ID. If you're running the online example I put together then I'm afraid I don't know why you'd get that since it works fine in Firefox, IE6, and IE7 on this end.

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

Thursday, August 30, 2007 7:32 PM by aaron mclean

any way to do this with a datagrid?

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

Friday, August 31, 2007 11:51 AM by Sam

Hey have you tried having a drop down in the grid. It looks very bad in scrolling

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

Wednesday, September 05, 2007 7:38 AM by Manotas

Hi,

I'm implementing this solution and it's doing pretty much what I was looking for, and even after moving the height setting from the CSS to the JScript it's running fine (at least IE6).

However, it turned out that as I'm using the calendarExtender, when the calendar panel is popup, the grid's header is over the calendar.

I tried playing with the z-Index property in the Div, Header and GridView withouw success... For example, in the header row, if I set it to -1 it solves it, but then is placed behind the rest of the grid's rows ( and hides on scrolling)....

Do you have any suggestions ??

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

Wednesday, September 05, 2007 10:33 AM by dwahlin

I haven't played with the exact issue you're talking about, but you're obviously on the right track with z-index. It's one of those tricky issues that I'd have to play with though. If you do happen to find the solution for your particular situation please post it here though.

Posted by 퓨전마법사
,