Scrollable Area : the HTC option
Just for info I give back the HTC code (only for IE) originally written by the Microsoft Web Team, and I am sure it's possible to adapt this code to work with other browsers.
Update: I used this with Datagrid and yes it's working well (tested with IE 6)
If the Web Team had a dollar for every time we've seen an e-mail from a developer asking for this feature we wouldn't be pining for the good old days when the NASDAQ was sky high. Well, it's high time we addressed this request and satisfy our loyal readers. Naturally, we thought the best way to do this was to come up with an HTML Behavior that we could attach to any table we want, even ones emitted by the ASP.NET DataGrid. Our control, ScrollTable.htc, is yours to customize or improve. We'll explain how it works.
First, the scrolltable.htc is attached to our table element by specifying the behavior attribute in the CSS class used on the element.
behavior:url(scrolltable.htc);
When the ondocumentready event fires, our behavior creates two table elements by calling cloneNode(false) on our table. We clone the table without child elements rather than creating new tables so that we preserve attributes, such as cellSpacing and style, instead of manually having to add each attribute to our two new tables. The two table elements we create are the header containing the first row of the table in the test page and the body containing all of the other rows. The body table is inserted into a container DIV with the CSS overflow attribute set to auto, which allows scrolling if necessary. The table in the test page is then modified to contain only two rows. The first row contains a TD and the header table, and the second row contains a TD with the container DIV. Next, we store the offsetWidths of all the cells in the first row of the table in an array, rgWidths, so that we can set the cells in the header and body tables and they remain in sync. Then we add the header row to the tblHeader element and the body rows to the tblBody element. We then insert the tblBody into the container DIV. After that, we remove all the rows from our table and insert two new rows—one for the tblHeader and one for tblBody. Once all the rows are added to the body and header tables, we set the width of the columns in the two tables to the width values stored in our array. Finally, we set some properties on the header and body to make them look right.
In TestPage.htm, one can see that we have added three expando properties to our TABLE. The first attribute, bodyHeight, is used to set the height of the body. If this property is not set, the body will not scroll. We also have headerCSS and bodyCSS, which simply allow you to specify separate CSS classes for the header and body tables.
Here are the sources:
ScrollTable.htc
<PUBLIC:ATTACH event="ondocumentready" handler="onDocumentReady" /> <SCRIPT language="JScript"> function onDocumentReady() { // Create elements var tblHeader = this.cloneNode(false); var tblBody = this.cloneNode(false); var divCntr = document.createElement("DIV"); // Get column widths var rgWidths = new Array(); for (var i = 0; i < this.rows[0].cells.length; i++) { rgWidths[i] = this.rows[0].cells[i].offsetWidth; } // Add header row var tbdyHeader = document.createElement("TBODY"); tblHeader.appendChild(tbdyHeader); tbdyHeader.appendChild(this.rows[0].cloneNode(true)); // Add body rows var tbdyBody = document.createElement("TBODY"); tblBody.appendChild(tbdyBody); for (var i = 1; i < this.rows.length; i++) { var oRow = this.rows[i].cloneNode(true); tbdyBody.appendChild(oRow); } // Set up body container divCntr.style.overflow = "auto"; if (this.bodyHeight) divCntr.style.height = this.bodyHeight; divCntr.appendChild(tblBody); // Change existing table for (var i = this.rows.length; i > 0; i--) { this.rows[i-1].removeNode(true); } var tr1 = this.insertRow(); var td1 = tr1.insertCell(); var tr2 = this.insertRow(); var td2 = tr2.insertCell(); td1.appendChild(tblHeader); td2.appendChild(divCntr); // Set column widths of all but the last column for (var i = 0; i < rgWidths.length - 1; i++) { tblHeader.rows[0].cells[i].width = rgWidths[i]; tblBody.rows[0].cells[i].width = rgWidths[i]; } tblHeader.style.fontSize = "100%"; tblHeader.width = "100%"; tblHeader.style.tableLayout = "fixed"; tblHeader.className = this.headerCSS ? this.headerCSS : ""; tblHeader.border = 0; tblBody.style.fontSize = "100%"; tblBody.width = "100%"; tblBody.style.tableLayout = "fixed"; tblBody.className = this.bodyCSS ? this.bodyCSS : ""; tblBody.border = 0; this.cellSpacing = 0; this.cellPadding = 0; } </SCRIPT>
TestPage.htm
<HTML> <HEAD> <STYLE> .tblMain { behavior:url(scrolltable.htc); background-color: highlight; border: 1px solid darkblue; font-family: Verdana; font-size: .8em; } .tblHeader { color: highlighttext; } .tblBody { background-color: #EEEEEE; color: darkblue; } </STYLE> </HEAD> <BODY> <TABLE bodyHeight="100" bodyCSS="tblBody" headerCSS="tblHeader" class="tblMain" width="75%" cellspacing=5 cellpadding=0> <TR> <TD nowrap>Header A</TD> <TD nowrap>Header B</TD> <TD nowrap>Header C</TD> </TR> <TR> <TD>Aaaaaaaaaaaaaaaaaaa</TD><TD>B</TD><TD>Ccccccccccccccccccc</TD> </TR> <TR> <TD>A</TD><TD>Bbbbbbb</TD><TD>C</TD> </TR> <TR> <TD>A</TD><TD>B</TD><TD>C</TD> </TR> <TR> <TD>A</TD><TD>B</TD><TD>C</TD> </TR> <TR> <TD>A</TD><TD>B</TD><TD>C</TD> </TR> <TR> <TD>A</TD><TD>B</TD><TD>C</TD> </TR> <TR> <TD>A</TD><TD>B</TD><TD>C</TD> </TR> <TR> <TD>A</TD><TD>B</TD><TD>C</TD> </TR> <TR> <TD>A</TD><TD>B</TD><TD>C</TD> </TR> </TABLE> </BODY> </HTML>