Extending ASP.NET Controls: A Scrollable GridView





5.00/5 (18 votes)
Extending the standard ASP.NET GridView control to add a vertical scrollbar in the grid
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"; // Assign a css-style // accessible by jquery divhead.Style.Add("overflow", "hidden"); // Make sure the inner table // will not overflow if (this.Width != Unit.Empty) divhead.Style.Add("width", this.Width.ToString()); // Assign new width, // overrule stylesheet. // Use width defined // in the control
- Create a new
Table
object and insert it into the headerDIV
. 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]; // Reference the table Control ctrlrow = ctrl.Controls[0]; // Reference the first row: // table->row[0] tablehead.Controls.Add(ctrlrow); // This will automatically // bind the row to the new // parent control (and remove // itself from the previous container)
- Render the header
DIV
(this will automatically also render its children elements).divhead.RenderControl(writer); // will render the DIV // including the table // containing only the header.
- Create the body
DIV
.System.Web.UI.HtmlControls.HtmlGenericControl divbody = new System.Web.UI.HtmlControls.HtmlGenericControl("DIV");
- Set the
style
attributes of theDIV
and make sure it is scrollable.divbody.Attributes["class"] = "GridViewBodyContainer"; // style is hardcoded // for jquery if (this.Width != Unit.Empty) divbody.Style.Add("width", this.Width.ToString()); // Assign new width. if (this.Height != Unit.Empty) // Note: if no height // is set, // scrollbars will not be // visible since all rows // will be displayed then divbody.Style.Add("height", this.Height.ToString()); // Assign new height. 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); // This will automatically bind the row // to the parent table (and remove itself // from previous container)
- Insert the
GridView
table inside the bodyDIV
and render the bodyDIV
.divbody.Controls.AddAt(0, ctrl); // Bind the table to the body DIV // (and remove itself from previous container) divbody.RenderControl(writer); // will render the DIV and the included table
- Insert the table back into the
GridView
. This is an essential step, as it will restore theGridView
into its original state.this.Controls.AddAt(0, ctrl); // Restore to previous container, // this way the control will not break
- 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" %>
<!-- add the following js scripts -->;
<script type="text/javascript" src="Scripts/jquery-1.4.1.min.js"></script>
<script type="text/javascript" src="Scripts/ScrollableGridView.js"></script>
...
<!-- insert the scrollable gridview control on the page at the desired position
make sure to set the Height property of the control to make sure scrollbars are displayed-->
<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