Introduction
Frozen header for an ASP.NET GridView
is a common requirement for web developers nowadays. For usability reasons, frozen or fixed headers are a must for data viewing controls like DataGrid
, GridView
, or a raw HTML table etc. You must have observed that, in Excel, you can freeze the header cells of your spread sheet. When you scroll down, the headers are always visible, making your sheet more readable. The same effect is often needed in a GridView
control, especially if you are displaying many records at a time. In this article, I will explain a technique that will allow you to achieve this with ease.
Conceptual Discussions
We all know that after executing the server-side code, raw HTML is returned to the browser, and the browser just shows the HTML. Obviously, a GridView
needs to be converted to an HTML table. So, we need to do some customizations on the returned table to reach our goal. There are some steps to do that customization that are mentioned below:
- Take away the table row which usually represents the header and assign it to a newly created “
<THEAD>
” tag. - Make the rest of table/grid row scrollable by limiting the height of the table and adding the “
overflow: auto
” attribute inside both “<TBODY>
” and “<DIV>
”. “<DIV>
” is simply a wrapper over the grid which helps to represent the scrollable option. - Now, fix/freeze the “
<THEAD>
” using a technique applicable to the browser (because different browsers act differently in this case).
Now, whenever you scroll the grid rows, the header always stays on the top row.
Using the Code
The complete sample code is given below. You can also download the sample project from the above link.
<%@ Page Language="C#" AutoEventWireup="true"
CodeFile="Default.aspx.cs" Inherits="_Default" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<style type="text/css">
.WrapperDiv
{
width:560px;height:290px;border: 1px solid black;
}
.WrapperDiv TH
{
position: relative;
}
.WrapperDiv TR
{
height:0px;
}
.ScrollContent
{
display: block;
overflow: auto;
width: 100%;
height: 250px;
}
.FixedHeader
{
display: block;
}
</style>
<script runat="server">
ICollection CreateDataSource( )
{
System.Data.DataTable dt = new System.Data.DataTable();
System.Data.DataRow dr;
dt.Columns.Add(new System.Data.DataColumn("Product Id", typeof(System.String)));
dt.Columns.Add(new System.Data.DataColumn("Product Name", typeof(System.String)));
dt.Columns.Add(new System.Data.DataColumn("Product Price", typeof(System.Decimal)));
dt.Columns.Add(new System.Data.DataColumn("Manufacture Time", typeof(System.String)));
dt.Columns.Add(new System.Data.DataColumn("Expired Time", typeof(System.String)));
for (int i = 1; i <= 50; i++)
{
System.Random rd = new System.Random(Environment.TickCount * i); ;
dr = dt.NewRow();
dr[0] = i.ToString();
dr[1] = "Sample Product" + i.ToString();
dr[2] = System.Math.Round(rd.NextDouble() * 100, 2);
dr[3] = "September/2008";
dr[4] = "September/2012";
dt.Rows.Add(dr);
}
System.Data.DataView dv = new System.Data.DataView(dt);
return dv;
}
protected void Page_Load( object sender, EventArgs e )
{
if (!IsPostBack)
{
GridView1.DataSource = CreateDataSource();
GridView1.DataBind();
}
}
</script>
<script type="text/ecmascript">
var headerHeight = 8;
function onLoad()
{
if(navigator.appName == "Opera")
{
freezeGridViewHeaderForOpera('GridView1');
}
else
{
freezeGridViewHeaderForIEAndFF('GridView1','WrapperDiv');
}
}
function freezeGridViewHeaderForIEAndFF(gridViewId,wrapperDivCssClass)
{
var grid = document.getElementById(gridViewId);
if (grid != 'undefined')
{
grid.style.visibility = 'hidden';
var div = null;
if (grid.parentNode != 'undefined')
{
div = grid.parentNode;
if (div.tagName == "DIV")
{
div.className = wrapperDivCssClass;
div.style.overflow = "auto";
}
}
var grid = prepareFixedHeader(grid);
var tbody = grid.getElementsByTagName('TBODY')[0];
tbody.style.height = (div.offsetHeight - headerHeight) + 'px';
tbody.style.overflowX = "hidden";
tbody.overflow = 'auto';
tbody.overflowX = 'hidden';
grid.style.visibility = 'visible';
}
}
function freezeGridViewHeaderForOpera(gridViewId)
{
var grid = document.getElementById(gridViewId);
if (grid != 'undefined')
{
grid = prepareFixedHeader(grid);
var headers = grid.getElementsByTagName('THEAD')[0];
headers.className = "FixedHeader";
var tbody = grid.getElementsByTagName('TBODY')[0];
tbody.className = "ScrollContent";
var cells = tbody.childNodes[0];
for(var i = 0; i < cells.childNodes.length;i++)
{
var tableCell = cells.childNodes[i];
var tableCellWidth = getStyle(tableCell,'width')
var headerCell = headers.childNodes[0].childNodes[i];
var headerCellWidth = getStyle(headerCell,'width');
if(widthPxToInt(tableCellWidth) > widthPxToInt(headerCellWidth))
{
headerCell.style.width=(widthPxToInt(tableCellWidth) - 10) + "px";
}
else
{
tableCell.style.width=(widthPxToInt(headerCellWidth) - 10) + "px";
}
}
}
}
function prepareFixedHeader(grid)
{
var tags = grid.getElementsByTagName('TBODY');
if (tags != 'undefined')
{
var tbody = tags[0];
var trs = tbody.getElementsByTagName('TR');
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);
}
}
return grid;
}
function getStyle(oElm, strCssRule)
{
var strValue = "";
if(document.defaultView && document.defaultView.getComputedStyle){
strValue = document.defaultView.getComputedStyle(oElm,
"").getPropertyValue(strCssRule);
}
else if(oElm.currentStyle){
strCssRule = strCssRule.replace(/\-(\w)/g, function (strMatch, p1){
return p1.toUpperCase();
});
strValue = oElm.currentStyle[strCssRule];
}
return strValue;
}
function widthPxToInt(width)
{
width = width.substr(0,width.length-2);
return new Number(width);
}
</script>
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<title>Untitled Page</title>
</head>
<body>
<form id="form1" runat="server">
<div>
<asp:GridView ID="GridView1" runat="server"
Font-Size="12px" BackColor="#FFFFFF"
GridLines="Both" CellPadding="4" Width="560px">
<HeaderStyle BackColor="#EDEDED" Height="26px" />
</asp:GridView>
</div>
</form>
</body>
</html>
<script type="text/ecmascript">
window.onload = onLoad();
</script>
Browser Compatibility
Since I have used JavaScript and CSS to solve this problem, there is no doubt that it will raise the question, “Is the solution compatible for all browsers?”. If you Google, you will find lots of solutions for this problem. But, I have not yet found any client-side solution that supports all the browsers. Most of the solutions support Firefox and IE. But, very few support Opera.
I did test my provided solution in FireFox-3.0.3 , IE-7, and Opera-9.2 and got successful results.