In this article, you will see an example for extending the standard ASP.NET GridView control with the option to display scrollbars (a.k.a. scrollable table with fixed header), instead of using the standard 'paging', allowing to display all list items at once.
Introduction
Although ASP.NET comes with a large set of customizable webcontrols, you may wish in some cases to extend the functionality and behavior of a webcontrol
. Fortunately, ASP.NET controls have a number of extension points:
- Customizable properties
- Style and CSS-classes
- Templating
- Overriding
Although the first three options can be configured at runtime, the fourth option will require some coding. Overriding is required when you wish to change the controls' behavior or layout. In such cases, it is typical to create a subclass for the webcontrol in which the overridden methods are implemented.
This article presents an example for extending the standard ASP.NET GridView
control with the option to display scrollbars (a.k.a. scrollable table with fixed header), instead of using the standard 'paging', allowing to display all list items at once. This solution is tested with Internet Explorer, Firefox and Chrome.
Background
Web standards prescribe 'paging' functionality when browsing through large lists. The idea is that large lists will take a 'long' time to download, so when using paging, users only need to download a single page with a small number of rows.
The standard ASP.NET GridView
supports paging by default. Strangely enough, the GridView
does not optimize loading time by much, since the list that is bound to the gridview
is stored in the ViewState
by default, and therefore still generates a 'large' page resulting in a longer download time.
So one might as well just choose to ignore paging and show the complete list as a whole. This has the advantage that the page does not need to be reloaded each time the user chooses a different page in the grid.
Obviously, the list can become too large to fit on a single webpage, and hence using scrollbars would be a sufficient solution. Something that is quite common in desktop applications. I found however that adding a scrollbar to the GridView
is not entirely trivial: the goal is to only have the list items scrollable, while leaving the gridview
header fixed.
I've looked for solutions for this issue, however some of the proposed solutions are only compatible for some browsers (e.g., CSS based), others were quite complicated or worked only in certain situations. As far as I've seen, no standard solution is available for the GridView
control. The solution presented here is compatible with various common browsers and can be incorporated into any ASP.NET solution with ease. Also, the final rendered HTML is as compact as possible.
Solution
The approach to the solution is not new, e.g., see an example here. Basically, the header row is displayed in a table in a header DIV
, while the body rows are displayed in a separate scrollable body DIV
element. This approach poses the following issues to solve:
Extracting the Header Row from the GridView and Rendering It in a Separate DIV and TABLE
This requires a bit of tinkering to get it working. It requires an override of the default RenderChildren
method of the GridView
control. The RenderChildren
method is responsible for generating the output which is written to the response object. Instead of generating the default GridView
html element, I rearranged and inserted some html elements of my own.
Basically, I'm using the following steps:
- Create a new header
DIV
html element. Assign it a special css-class so it can be accessed from JQuery.
System.Web.UI.HtmlControls.HtmlGenericControl divhead =
new System.Web.UI.HtmlControls.HtmlGenericControl("DIV");
divhead.Attributes["class"] = "GridViewHeaderContainer";
divhead.Style.Add("overflow", "hidden");
if (this.Width != Unit.Empty)
divhead.Style.Add("width", this.Width.ToString());
- Create a new
Table
object and insert it into the header DIV
. Make sure the correct styling is applied.
Table tablehead = new Table();
tablehead.ApplyStyle(this.HeaderStyle);
tablehead.Style.Add("width", "100%");
tablehead.ID = ctrl.UniqueID + "$Header";
divhead.Controls.Add(tablehead);
- Select the header row in the
GridView
and insert it into the newly created table (note that this will remove the row from the original table!).
WebControl ctrl = (WebControl)this.Controls[0];
Control ctrlrow = ctrl.Controls[0];
tablehead.Controls.Add(ctrlrow);
- Render the header
DIV
(this will automatically also render its children elements).
divhead.RenderControl(writer);
- Create the body
DIV
.
System.Web.UI.HtmlControls.HtmlGenericControl divbody =
new System.Web.UI.HtmlControls.HtmlGenericControl("DIV");
- Set the
style
attributes of the DIV
and make sure it is scrollable.
divbody.Attributes["class"] = "GridViewBodyContainer";
if (this.Width != Unit.Empty)
divbody.Style.Add("width", this.Width.ToString());
if (this.Height != Unit.Empty)
divbody.Style.Add("height", this.Height.ToString());
divbody.Style.Add("margin", "0px");
divbody.Style.Add("overflow", "hidden");
divbody.Style.Add("overflow-y", "auto");
- Insert the header row back into the original table at position 0 (at the top).
ctrl.Controls.AddAt(0, ctrlrow);
- Insert the
GridView
table inside the body DIV
and render the body DIV
.
divbody.Controls.AddAt(0, ctrl);
divbody.RenderControl(writer);
- Insert the table back into the
GridView
. This is an essential step, as it will restore the GridView
into its original state.
this.Controls.AddAt(0, ctrl);
- Render any other controls contained in the
gridview
(e.g., the footer):
for (int i = 1; i < this.Controls.Count; i++)
{
ctrl = (WebControl)this.Controls[i];
ctrl.RenderControl(writer);
}
Aligning the Columns of the Header Row With the Columns in the Body Table
To make sure the columns of both tables align, some JavaScript/JQuery is used. The script contains a function called UpdateGrid()
which is called after the gridview
has completely rendered. At this time, the column widths are known and re-aligned at runtime. It will lookup the DIV
s based on their css-class and loop through all rows and columns to determine the width. Then it will explicitly set the width for each table cell.
Once finished, it will make the first row in the body (which is the original header row) invisible. Because this is done after rendering, sometimes this may result in the display of 2 headers for a split second. That's the only drawback from this approach.
Using the Grid in an UpdatePanel
In order to support updates of the Grid
from within an UpdatePanel
, one extra bit of JavaScript is required to make sure the Grid
refreshes and realigns properly after each (partial) postback to the server. Every time the UpdatePanel
updates its content, it must call the UpdateGrid()
JavaScript function. To do this, the following handler must be declared in the webpage right after the ScriptManager
declaration:
<asp:ScriptManager ID="ScriptManager1" runat="server" />
<script type="text/javascript">
Sys.Application.add_load(function () { UpdateGrid(); });
</script>
This nice little trick will keep the grid in sync with the UpdatePanel
.
Using the Code
The provided solution comes in 2 files:
- ScrollableGridView.cs
- ScrollableGridView.js
You can add the files to your own ASP.NET solution or use the ScrollableGridView
directly from the ControlExtensions
component. You will need to add a reference from your web project to the component.
Add the following lines to your webpage:
<%@ Register TagPrefix="ce" Namespace="Grids" Assembly="ControlExtensions" %>
;
<script type="text/javascript" src="Scripts/jquery-1.4.1.min.js"></script>
<script type="text/javascript" src="Scripts/ScrollableGridView.js"></script>
...
<ce:ScrollableGridView id="ScrollableGridView1" runat="server" height="360px" width="100%">
<HeaderStyle BackColor="Orange" />
</ce:ScrollableGridView>
...
This is sufficient to support scrolling in the GridView
. In the sample application included here, some random rows are generated to fill the list. This is done in the page's code-behind simply for testing purposes.
Additionally, an example webpage is added to show how to use the grid in combination with the UpdatePanel
control.
Points of Interest
I was quite happy to find out that the GridView
can be rendered quite differently by overriding the RenderChildren
method. This allows for easy reuse and extension of existing ASP.NET controls.
History
- 5th April, 2013: Version 1.0 published
- 17th April, 2013: Version 1.1 published