Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / web / ASP.NET

Extending ASP.NET Controls: A Scrollable GridView

5.00/5 (20 votes)
7 Sep 2020CPOL5 min read 85.5K   3.1K  
Extending the standard ASP.NET GridView control to add a vertical scrollbar in the grid
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.

Scrollbar in the GridView

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:

  1. Customizable properties
  2. Style and CSS-classes
  3. Templating
  4. 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:

  1. Create a new header DIV html element. Assign it a special css-class so it can be accessed from JQuery.
    C#
        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
  2. Create a new Table object and insert it into the header DIV. Make sure the correct styling is applied.
    C#
    Table tablehead = new Table();
    tablehead.ApplyStyle(this.HeaderStyle);
    tablehead.Style.Add("width", "100%");
    tablehead.ID = ctrl.UniqueID + "$Header";
    divhead.Controls.Add(tablehead);
    
  3. 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!).
    C#
        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)
  4. Render the header DIV (this will automatically also render its children elements).
    C#
         divhead.RenderControl(writer);                 // will render the DIV 
                                                        // including the table 
                                                        // containing only the header.
  5. Create the body DIV.
    C#
    System.Web.UI.HtmlControls.HtmlGenericControl divbody =
                 new System.Web.UI.HtmlControls.HtmlGenericControl("DIV");
    
  6. Set the style attributes of the DIV and make sure it is scrollable.
    C#
            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");
  7. Insert the header row back into the original table at position 0 (at the top).
    C#
    ctrl.Controls.AddAt(0, ctrlrow); // This will automatically bind the row
                                     // to the parent table (and remove itself
                                     // from previous container)
    
  8. Insert the GridView table inside the body DIV and render the body DIV.
    C#
    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
    
  9. Insert the table back into the GridView. This is an essential step, as it will restore the GridView into its original state.
    C#
    this.Controls.AddAt(0, ctrl);     // Restore to previous container,
                                      // this way the control will not break
    
  10. Render any other controls contained in the gridview (e.g., the footer):
    C#
    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 DIVs 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.NET
<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:

ASP.NET
<%@ 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

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)