Freezing GridView Column Headers using Only CSS
Recently I wrote an article describing how you could use CSS plus a GrdiView Control Adaptor to add the 'frozen column headers' feature to your GridView.
Live Demo (IE only) | Download
I received the following comment from a kind reader named Mitch:
I have also noticed that applying a skin to the gridview also seems to create TH elements for the headers. I have a simple solution that freezes the headers by doing the following:
- apply a skin to a gridview.
- wrap the gridview in a div or fieldset.
- Apply a css class to the wrapper that sets the overflow to scroll and the TH position to relative. You may need to set the TR height to 0px for IE.
I set the height of the wrapper in the local style so I can reuse the css class. I have tested this in IE6 & 7 and Firefox and it works great.
Wow. If this is true, it has a number of advantages over my original solution:
- It can be applied without using Control Adaptors - one less dependency
- It works with both IE and FF
- It is really simple!
So I took this feedback and tried to fix up my old sample site. I created a new site from my trusty GridView web site template and started following Mitch's advise. First off, I created a regular GridView and placed it inside a DIV element like so. Notice I assign the DIV to the container class.
<div class="container" style="height:300px; width:700px;"> <asp:GridView runat="server" AutoGenerateColumns="false" AllowSorting="true" DataSourceID="odsCustomers" BorderWidth="0px"> <Columns> <asp:BoundField HeaderText="ID" DataField="CustomerID" SortExpression="CustomerID" /> <asp:BoundField HeaderText="Name" DataField="ContactName" SortExpression="ContactName" /> <asp:BoundField HeaderText="Title" DataField="ContactTitle" SortExpression="ContactTitle" /> <asp:BoundField HeaderText="Address" DataField="Address" SortExpression="Address" /> <asp:BoundField HeaderText="City" DataField="City" SortExpression="City" /> </Columns> </asp:GridView> </div>
Next, I created the container css style class as Mitch suggests ...
/* So the overflow scrolls */ .container {overflow:auto;} /* Keep the header cells positioned as we scroll */ .container table th {position:relative;} /* For alignment of the scroll bar */ .container table tbody {overflow-x:hidden;}
Finally, I added back in the other styles that are not related to the frozen headers to spruce things up a bit. At this point I opened IE to test it out and sure enough it works great. The only problem is that (again) it doesn't work in FireFox. It behaves better than my original solution in that the grid renders fine, just the header columns do not stay stationary. Even though it isn't perfect, I figured I would pass Mitch's solution on since it seems to be hands down better than my original one. If anyone know's how FF can be supported with pure CSS, I would be interested.
That's it. Enjoy!
Comments
Matt,
In this approach, using Panel control instead of DIV may make more sense since Panel itself is more playable at the VB side.
By this way, we can dynamically set the height of the panel (well, actually DIV) when freezing of the header is really needed depending on the real estate on the UI. Sample code can be like:
Dim dsa As DataSourceSelectArguments = New DataSourceSelectArguments()
dsa.AddSupportedCapabilities(DataSourceCapabilities.RetrieveTotalRowCount)
Dim dv As Data.DataView = CType(SQLDataSource1.Select(dsa), Data.DataView)
Dim m As Integer = CType(dv.Count.ToString, Integer) # of rows in GridView1
If m > 10 Then
Panel1.Height = "500"
End If
Thanks for this great post!
Would position:fixed on the TH work in Firefox?
I think you can overflow just the tbody for FF. Check this out:http://web.tampabay.rr.com/bmerkey/examples/nonscroll-table-header.html
Have you tried this in a file that uses a MasterPage? I added the style to the Master and my gridview headers dont freeze.
@Mike
I did try using this with a Master page and I didnt have any issues. There must be something else going on ...
You can donwload the sample project here
to see if you can spot any differences.
Hope that helps.
Matt.
this works great for me. Thanks Matt.
mesut
Hi Matt,
By looking at this article, Im really hopefull of getting some clue for my strange problem with Gridview.
The requirement is that, I need to provide a gridview in a tag with width in % not in px and same with each bounded columns width. And using css "table-layout:fixed" and "word-wrap:break-word" for grid and coulumn respectively to achieve word wrapping. The issue is word wrapping is happening but the width is not what i have defined.
For eg: width of grid is 50% and for two columns with 20% and 80%. But its rendering the columns as 50% each.
One thing I found, if we remove the tag from rendered page (view source) it renders correctly as 20% and 50% but if dont it renders as 50% each.
i guess some thing related to im missing.
Good stuff, but without firefox support its not even usable.
Interesting that we get all this great ajax toolkit from MS, etc... but we dont have simple header freezing code built into gridviews. Especially since weve already rewritten the datagrid once already lol
@Steve -
Thanks for the feedback. You are right, without FF support this isnt a complete solution.
Matt.
Matt, seems to work but has a few side effects.
My gridview was inside a div with .
That no longer works, it seems the culprit is the "style:height: width:" attribute. I tried adding text-align:center; but that didnt help. Any idea how to keep the table centered?
The second problem was that the table has a border and when scrolled, the border scrolls too and leaves a 2px space on top of the header through which you can see part of the text as you scroll by. Im using master pages by the way.
Regarding the centering issue, I enclosed the div containing the gridview in a panel with a cssstyle which says "text-align:center;" and that worked fine.
Ok, after much fiddling, I found that if I made the enclosing table have no top border and the header have a 2px top border, it seems to solve the problem of the top table border scrolling up. Heres what I used:
.container {overflow:auto;}.container table {border-width: 0px 2px 2px 2px;}.container table th { position:relative; border-width: 2px 1px 0px 1px; border-style: solid; border-color:Black White Black #336699; }
Hi Matt,
This works fine if we had one row of column headers.My probleim is that I can have 2 or three rows of column headers.So when I scroll I am able to keep my original column header row fixed but other rows of column headers above it are gone.Any idea how to solve this.
Thanks in advance.
Does this technique work if the table also needs to scroll horizontally?
I have used other CSS methods (like: http://www.codeproject.com/aspnet/DataGridFixedHeader.asp)
which works well - unless the table needs to scroll horizontally - in which case the header cells stay in place while the detail scrolls left and right.
Your live demo only scrolls vertically - so I am curious whether it suffers from the same limitation.
I am using gridview in update panel. I was loading UserControl dynamically in update panel. My gridview contains a checkbox column. What happens is whenever i scroll the gridview and checks a checkbox the header resets its position to top of the page equivalent to the amount of scroll that i have done in div. Any ideas behind the strange behaviour?
Hi Matt,
Is there a way to freeze the gridview column wise ? so for e-x, if we freeze column 0, column 1 and when scroll horizontally these 2 columns are always visible ?
Thanks,
varadhg
Thanks for the post, but when I change one data in the XML file (the database) and i tried to exceed with more than 100 char without space, the header ruin the layout. Is there other way to limit the width and wrap the row data?
how do you save the scroll position on refresh/postback?
I am also havng a similar problem to Socko. Quote:
"I am using gridview in update panel. I was loading UserControl dynamically in update panel. My gridview contains a checkbox column. What happens is whenever i scroll the gridview and checks a checkbox the header resets its position to top of the page equivalent to the amount of scroll that i have done in div. Any ideas behind the strange behaviour? "
I also have two cases of this in my project. One is a gridview that has a mouse over event which is inside an Accordion Control (essentially updatepanel) and if you have scrolled down and then mouse over, the heder resets its position instead of staying fixed.
My other problem is a gridview control within an AJAX Tab Control. When you have scrolled down and then mouse over the tab header (causing it to flash) the header zooms up and the whole gridview shoots to bottom of page.
Thanks for your help
It is not working in mozilla firefox
Hi,
This works fine util you attach a cssclass to the mouseover event for every datarow. When you do this the header moves up by the no of rows that you have scrolled and moves down when scrolled down using the mouse. Works fine though when u scroll using arrow keys or click on the scroll bar.
Can you please explain this and also how to solve the problem.
Thanks.
This is the only thing that works and is very simple.
.div_grid_container
{
overflow: auto;
height: 150px;
}
.grid_header
{
background-color: #01a0bc;
height: 26px;
position: relative;
top: expression(this.offsetParent.scrollTop-2);
}
I found the solution for the moving header problem: For the parent div that the table headers are located in, set the position to relative as well.
Heres example code:
/* parent div */
div.scrollTable
{
position: relative;
width: 95%;
height: 400px; /* must be greater than tbody*/
overflow: auto;
margin: 0 auto;
padding-right: 10px;
}
/* static header */
.scrollTable th
{
position: relative;
top: expression(offsetParent.scrollTop);
}
--------------
Not 100% sure why it works, but it may be because the jabascript needs to compute the headers location from the parents location (which was set to fixed before, and is now relative).
Incidentally... this also fixes the horizontal scroll issue. Neat!
Hi, i have the paging function on my grid in top and bottom, may i know how to fix the position for the paging? Thank for help ^^
Great code, thanks! I was able to use Matts code plus some of the code in the comments (Paul and RagingKore) in order to solve all of my needs. Thanks! Your site rules!
Thanks. it solved all my problems
Im trying to follow this for Firefox 2. Posts by Elaine on 4-25 imply she got working for Firefox but Im not really clear about that. Heres what I adapted from the above posts (works fine in IE7 but in Firefox the table header row just scrolls out of sight; also the CSS expression on the top property is indicated invalid by Visual Studio 2005 editor)
/* So the overflow scrolls */
div.coveragesContainer
{
overflow: auto;
width: 95%;
height: 380px; /* must be greater than tbody*/
position: relative;
margin: 0 auto;
}
/* Keep the header cells positioned as we scroll */
.coveragesContainer table th
{
position:relative;
top: expression(this.offsetParent.scrollTop);
}
/* For alignment of the scroll bar */
.coveragesContainer table tbody
{
overflow:hidden;
}
Hi am facing performance issue while using CSS to freeze column. The browser gets hang when number of records increases. let me if you have any solution for this issue.Thanks in advance.
No matter what method I employ, I can not get the header to freeze in place. This "simple" method of applying the css to the wrapper does not work. I have even compared the html source from the sample site and my own.
Any ideas what would prevent the solution from working?
Can you please tell me how to fix coloum for the GridView
any idea ?
Hi,
I am still facing moving down whole grid some time while clicking on Girdview Sorting.
Can any one provide Solution for this.
Regards
Vijai
For some reason - when I scroll and move off the tabpanel I have the GV in - the header just disappears.
On my post - when I scroll back to the top of the GV, it "reappears" - why would the css just "disappear"?
Hey chaps just tried this out and it works for me. Strange behaviour tho when there is a dropdown list in the grid. The dropdowns float over the header. Check boxes dont tho. Any ideas please
Re: Travis
Yeah Ive got the same thing. Ive found that if I have a link on one of my table cells that has a style on it A:HOVER {background: yellow} - it causes the header to disappear. Really frustrating. Will have to ditch this solution and have my table header row in a separate table (yuck)... This CSS solution just doesnt seem to be stable enough, not in IE anyway.
Unless anyone has any ideas...?
Mine have more than 30 columns with multiple row header, its become so "messy", the width is set to 100% but the row header widht extended out of the , no longer inside the .
Please, is there any solution for this?
Very good solution to the problem, However does anyone know the css to have alternating Rows ?
Solution Posted by: Paul on April 10, 2008 12:00 AM
Works perfectly for me and it solved the mouseover problem as well.
I've modified the CSS and allow freezing first few columns and header row jst like Excel.
Here is my CSS code:
/* Div container to wrap the datagrid */
div#div-datagrid {
width: 1000px;
height: 460px;
overflow: auto;
scrollbar-base-color:#ffeaff;
}
/* Locks the left column */
th.locked, td.locked
{
background-color: #C9C9C9;
position: relative;
cursor: default;
left: expression(document.getElementById("div-datagrid").scrollLeft-2); /*IE5+ only*/
}
/* Locks table header */
th {
text-align: center;
background-color: navy;
color: white;
border-right: 1px solid silver;
position:relative;
cursor: default;
top: expression(document.getElementById("div-datagrid").scrollTop-2); /*IE5+ only*/
z-index: 10;
}
/* Keeps the header as the top most item. Important for top left item*/
th.locked {z-index: 99;}
/* DataGrid Item and AlternatingItem Style*/
.GridRow {font-size: small; color: black; font-family: Arial; background-color:#ffffff; height:35px;}
.GridAltRow {font-size: small; color: black; font-family: Arial; background-color:#eeeeee; height:35px;}
.Grid
{
font-size: small;
border-color: #CCCCCC;
border-style: none;
border-width: 1px;
font-family: Tahoma;
font-size:x-small;
padding: 0, 0, 0, 100;
background-color:White
Here's problem, my grid contains text box. The aligment or position of the freeze pane went missing or mis-alligned when one of the text box is filled.
Any solution?
Thanks.
Michael,
your CSS doesn't seem to be suitable for the templated stuff on GridView etc., since you use additional CSS for TH or TD. Or how can you get that into the HTML through the ASPX-page?
All the best
Michael
Also: I wanted to add a "Date"-column, so I modified your code by inserting (after the lblSize.Text):
Label lblDat = (Label)args.Row.FindControl("lblDat");
lblDat.Text = fileInfo.LastWriteTime.ToString("d");
...and after the lblSize-template:
But now the next challenge is to sort the directory-listing after Name/Size/Date - and of course it should correctly sort size and date, not based on their string-representation in the table. I guess it should be possible because the 'raw' data is available via the datasource, but the exact implementation is a bit beyond me currently.
Ideas anyone? ;)
This is the easiest way I have found:
Protected Sub uxItemDetails_RowCreated(ByVal sender As Object, ByVal e As System.Web.UI.WebControls.GridViewRowEventArgs)
If e.Row.RowType = DataControlRowType.Header Then
e.Row.Style.Add("position", "relative")
e.Row.Style.Add("top", "0px")
e.Row.Style.Add("z-index", "2")
End If
End Sub
Here is what I came up with
It is combination of all the solutions I got after Google search and combination of the code in the above examples
In css Stylesheet
.scrollTable
{
position: relative; /*width: 200px; set it on individual page
height: 120px; must be greater than tbody*/
overflow: auto;
margin: 0 auto;
/*padding-right: 10px;*/
border-top: gainsboro thin solid;
border-left: gainsboro thin solid;
border-right: gainsboro thin solid;
border-bottom: gainsboro thin solid;
padding-bottom:0px;
}
scrollTable table
{
border-spacing:0px;
padding-top:0px;
}
th.MyStaticCol, td.MyStaticCol
{
position: relative;
left: expression(offsetParent.scrollLeft); /*top: expression(offsetParent.scrollTop);*/
color: Black;
/*background-color: #b0c4de;*/
border-right: orange thin inset;
border-width: 1px; border-style: solid outset;
padding-top:0px;
border-spacing:0px;
}
.scrollTable th
{
/* this is the class which gets attache dto gridview header */
/******imp position :relative and top expression set so that hearder do not scroll with scroll*/
position: relative;
top: expression(offsetParent.scrollTop);
color: White;
height: 20px;
padding-top: 0px;
border-right: gainsboro 1px solid;
border-top: gainsboro 1px solid;
/*border-left: darkviolet 1px;remove left border since it makes the header col border bold getting overlapped with right border of adjacent cell */
border-bottom: gainsboro 1px solid;
font-size:10px;
font-family:Tahoma;
}
th.MyStaticCol
{
z-index:99;
}
.View_RowStyle
{
background-color: #EFF3FB;
Font-Size:10px ;
Color:#333333;
/* color:Navy;*/
height:15px;
/* border-width: 1px; border-style: solid dotted; */
word-wrap:break:word;
}
.View_AlternatingRowStyle
{
background-color:#ffffff;
Font-Size:10px;
height:15px;
Font-Size:10px ;
}
.View_HeaderStyle
{
color: White;
height:15px;
border-width: 1px 1px 1px 1px;
border-style: solid;
border-color:Black;
background-color: #b0c4de;
font-weight: bold;
text-align: center;
border-spacing:0px;
padding-top:0px;
font-size:10px;
font-family:Tahoma;
/* position: Relative; this is set so that hearder do not scroll with scroll
border-bottom-width: thick;
z-index: 10;
*/
}
/* GridView Skin settings */
/* GridView Skin settings */
.View_RowStyleForEdit
{
background-color: #EFF3FB;
Font-Size:8px ;
Color:#333333;
/* color:Navy;*/
height:10px;
}
.View_AlternatingRowStyleForEdit
{
background-color:#ffffff;
Font-Size:8px;
height:10px;
}
.GridStyle
{
font-family:Tahoma ;
Font-Size:Medium;
Color:#333333;
border-style:solid;
border:solid 1px black;
padding:0px;
margin:0px
/* HorizontalAlign:Left;
BorderStyle:Solid;
BorderWidth:1px */
}
.View_FooterStylL
{
background-color: #b0c4de; /*lightSteelBlue;*/
color: #fff;
font-weight: bold;
Font-Size:Small ;
}
/*.View_RowStyle
{
background-color: #EFF3FB;
Font-Size:10px ;
Color:#333333;
height:15px;
}
*/
.View_SelectedRowStyle
{
background-color: #D1DDF1;
color: Maroon;
font-weight: bold;
font-size:x-small;
}
.View_PagerStyle
{
/*background-color: orange;*/ /* #b0c4de;/* LightSteelBlue/* #FFc080;*/
color: #fff;
text-align: right;
font-size: x-small;
font-family: Tahoma;
/*background-image: url(Images/BkgImages/background_main.jpg);*/
background-image: url(../Images/BkgImages/background_main.jpg) ;
background-repeat: repeat-x;
}
In GridView RowDataBound event
e.Row.Cells[0].CssClass = "MyStaticCol";
e.Row.Cells[1].CssClass = "MyStaticCol";
With This 2 Col are locked
Thanks
Forget Firefox. It's cute, but if it can't play by the rules of the market-dominant browser (IE) it will never be useful for development. Developers should not be expected to write code multiple times because Firefox doesn't want to implement what Microsoft uses.
Hai,
@Author:
Your CSS works fine. But when Mouse over the Header, it disappears and won't come again. I need to refresh the page in order to bring it back. Any solution for that?
Thanks for the codes. My challange is to contain the gridview in certain width, even though the actual columns can be many. So, for my view, everything works well, except the headers can not be contained within the div and it overflows. The rest of the table is contained within the div; when I horizontally scroll the table, the headers don't move. Is there a way to solve this problem?
thanks, Qin
With this sample in IE6 and IE7 the header will stay frozen and scroll horizontally with the details.
It sometime bounces, but always comes back.
Only works in IE6 and IE7.
Not a problem for me, as I use internally and only IE6 and IE7 can access the application.
While shown with ASPX, will work in any HTML page.
************************************
STYLESHEET EXAMPLE : See Freezing
************************************
th{
}
.Freezing {
position:relative;
top:expression(this.offsetParent.scrollTop);
z-index:10;
}
Just add class="Freezing" to each TR in your THEAD for your TABLE and the header is frozen.