Introduction
There are several articles on fixed headers. I even wrote one called “Implementing a Fixed GridView Header in ASP.NET.” What I discovered was that the technique described in that article and some others doesn’t work very well when controls are nested or pages become complex. What I was looking for was a more durable solution.
What I started with was a technique that developers have been using for a while: shadowing a grid with a separate table. The challenge with aligning a separate control with a grid is aligning the separate control with the grid and the columns of the grid as it shrinks and grows and the columns change size. In this article, I present a better, improved spin on an old standby. Some interesting JavaScript is used to clone a fix the position of a grid header, handling runtime changes in the associated grid’s appearance.
As you read this article, you will have an opportunity to explore and expand your knowledge of JavaScript and ASP.NET, learn about object-oriented JavaScript, and gain a better understanding of the relationship between ASP.NET Web controls, HTML, and JavaScript.
An Overview of the Solution
Have you ever sat in a presentation listening to a lot of technical details without an idea of where you are going? I have. Many times it would have helped to have a thesis statement, a sort of, overview of what will be learned, why, and how we might get there. Hence this section.
In this article, you will learn a technique that will help you pin a header for a table (specifically the one generated by a ASP.NET GridView) even when the page containing the GridView scrolls. Along the way, you will have an opportunity to learn about object-oriented JavaScript and might glean a better understanding of ASP.NET web controls. If you want your grid headers to stay put, you are in the right place. If you are curious about object-oriented JavaScript, you are in the right place. If you are bored, stuck in an airport, or just curious, welcome.
This better fixed grid header solution has three elements: object-based JavaScript that manages the grid header, a stub separate HTML table for pinning the header, and some sample code that demonstrates a GridView with enough data to need scrolling. Hence, there are three sections to this article: the presentation view and the grid that has enough data to require scrolling, a simple section, the HTML tags that will ultimately represent the pin-able header, another relatively simple section, and the JavaScript that does all of the heavy lifting. The last section has all of the code.
Making a Page with a Scrollable Div and GridView
There are a couple of well-used ways to manage the location of controls. Two popular ways are the HTML Table and HTML Div. In this section, you will use a Div to house the GridView. You could make the Div scrollable or just add enough data to the GridView to make the page scroll. With the totality of the code in this article, either approach will permit the header to scroll correctly. To simplify the creation of the presentation layer, you’ll use a scrolling page and a lot of data in a GridView.
To implement a test presentation—web page—you can create a dummy class, use a generic List, and bind that list to a GridView. Before you write the code in Listing 1, drop a GridView on a web page.
Listing 1: A sample class and code that populates a GridView.
Imports System.Collections.Generic Partial Class _Default Inherits System.Web.UI.Page Protected Sub Page_Load(ByVal sender As Object, _ ByVal e As System.EventArgs) Handles Me.Load If (IsPostBack) Then Return Dim list As List(Of Sample) = New List(Of Sample) Dim I As Integer For I = 1 To 100 list.Add(New Sample(I, "Name" + I.ToString(), I.ToString())) Next GridView1.DataSource = list GridView1.DataBind() End Sub End Class Public Class Sample Private _iD As Integer Public Property ID() As Integer Get Return _iD End Get Set(ByVal Value As Integer) _iD = Value End Set End Property Private _name As String Public Property Name() As String Get Return _name End Get Set(ByVal Value As String) _name = Value End Set End Property Private _filler As String Public Property Filler() As String Get Return _filler End Get Set(ByVal Value As String) _filler = Value End Set End Property ''' <summary> ''' Initializes a new instance of the Sample class. ''' </summary> ''' <param name="iD"></param> ''' <param name="name"></param> ''' <param name="filler"></param> Public Sub New(ByVal iD As Integer, _ ByVal name As String, ByVal filler As String) _iD = iD _name = name _filler = filler End Sub End Class
You can use anything to populate the GridView; it really isn’t relevant to the discussion. All you should care about is that you have something that generates an HTML table and enough data to make that table need to scroll.
It is worth noting that ASP.NET web controls are HTML generators that spit out HTML on the server. Basically, if it looks like a duck, it is a duck. That is, a GridView is actually rendered as an HTML table, so this technique will work on anything that is ultimately an HTML table.
Adding the HTML Div and Table Stubs
The GridView will have a header. For the technique for pinning it, you could use the GridView’s actual header, but is actually a little easier to use a clone. The reason is that if you pin the GridView’s actual header, you have to adjust its position differently when you have scrolled to the top (or you hide row 1), and you might have to account for a positioned header’s index when manipulating the data. For these reasons, I elected to use a stub div and table to mimic the fixed header.
Tip: Did you know that a table header is rendered as a <TR> with <TH> children instead of <TD> (cells) children? Did you also know that you can place the row containing the header (or <TH> cells) anywhere in a Table? You can.
To add the div and table stub, open your web page and add the following HTML just above the GridView’s tag. Here is the additional HTML followed by complete HTML/ASP for the sample form (in Listing 2).
<div id="fixedHeader" style="position: absolute"> <table id="header" style="position: absolute"> </table> </div>