• How to create fixed header for GridView using Javascript

    Posted on June 28, 2012 by in ASP.NET, C#, Dotnet

    It is often frustrating to not see your header when you scroll through records. I sometimes wish we have something similar to WPF Scrollviewer.  Since we don’t have one we have to do more plumbing work to get this done.  In this article, I am going to explain to achieve this using simple JAVSCRIPT.

    As usual, I am going to use Adventure Works as datasource. Added following entry under connectionStrings element in web.config.

    <add name="Sql" connectionString="Data Source=(local);Initial Catalog=AdventureWorks; User=testuser; Password=testuser;" providerName="System.Data.SqlClient"/>
    

    I don’t want to go through the pain of using ADO.NET APIL to fetch data from database, so I simply use SQLDataSource control. The SqlDataSource data source control is simply a control that represents data in an SQL relational database to data-bound controls. You can use the SqlDataSource control in conjunction with a data-bound control such as Dropdownlist, gridview etc to retrieve data from a relational database and to display, edit, and sort data on a Web page with little or no code. The ConnectionString property of the SqlDataSource control specifies how to connect to the database. This property can be a hard-coded connection string or can point to a connection string in a Web.config file as shown in the code given above. The SelectCommand property specifies the SQL statement to execute to retrieve the data.

    <asp:SqlDataSource ID="sqlDSProducts" runat="server" ConnectionString="<%$ ConnectionStrings:Sql %>"
    SelectCommand="SELECT top 10 P.ProductID, P.Name, P.ProductNumber, P.ListPrice  FROM Production.Product P ORDER BY P.ProductID desc">       
    </asp:SqlDataSource>
    

    I have added a GridView to the page and applied some formatting to make it look nice.  We are not allowing user to perform any updates. So we render ProductID, Product Number, Product Name & Price in BoundColumns.

    <asp:GridView ID="gvProducts"           
        AutoGenerateColumns="False"  CssClass="mGrid2"
        runat="server" DataKeyNames="ProductID"  
        DataSourceID="sqlDSProducts">           
        <Columns>
            <asp:BoundField DataField="ProductID" HeaderText="Product ID"/>  
            <asp:BoundField DataField="ProductNumber" HeaderText="Product Number"/>  
            <asp:BoundField DataField="Name" HeaderText="Name"/>  
            <asp:BoundField DataField="ListPrice" HeaderText="Price" DataFormatString="{0:c}"/>
        </Columns>  
    </asp:GridView>
    

    In the Page_Load function, we register a startup script that would call CreateGridHeader javascript function that would remove the header from the grid control and creates a new fixed header.

    protected void Page_Load(object sender, EventArgs e)
    {
        if (!Page.IsPostBack)
        {                
            ClientScript.RegisterStartupScript(this.GetType(),
                        "CreateGridHeader", "<script>CreateGridHeader('gvProducts');</script>");
        }
    }
    

    The bulk of the job is done in the javascript function. The idea is to remove the header from the grid control and add it to a new parent (div element). While doing so we also need to make sure we copy all required attributes (Width, height, border, spacing etc). As far the scrolling is concerned, we could achieve simply by using the div and setting the height to overflow.

    function CreateGridHeader(gridName) {
    
        //get reference to the gridcontrol
        var gridCtl = document.getElementById(gridName);    
                        
        //create a new table and copy all the attributes of grid to this table
        //store the header cells width in a array
        var headerCellWidths = new Array();        
        for (var i = 0; i < gridCtl.getElementsByTagName("th").length; i++) {
            headerCellWidths[i] = gridCtl.getElementsByTagName("th")[i].offsetWidth;            
        }        
        
        //create a new table element
        var table = document.createElement("table");
        
        //copy all attributes from grid to this new table
        for (index = 0; index < gridCtl.attributes.length; index++) {
            if (gridCtl.attributes[index].specified && gridCtl.attributes[index].name != "id") {
                table.setAttribute(gridCtl.attributes[index].name, gridCtl.attributes[index].value);
            }
        }
    
        //get the width of the gridview, in pixels. 
        //The value contains the width with the padding, scrollBar, and the border, 
        //but does not include the margin.
        var gridWidth = gridCtl.offsetWidth;
        
        //set the width of the grid to the table        
        table.style.width = gridWidth + "px";
        
        //create the new table body element
        table.appendChild(document.createElement("tbody"));
        
        //move the header row from the gridview to the body of this new table
        table.getElementsByTagName("tbody")[0].appendChild(gridCtl.getElementsByTagName("tr")[0]);
    
        //get all the cells from the header row
        var tableCells = table.getElementsByTagName("th");
    
        //we dont need to set the width for all rows
        //setting it for one row should do
        var gridRow = gridCtl.getElementsByTagName("tr")[0];
    
        //copy the width of the previously saved header rows to new header row 
        // and first row of the grid
        for (var i = 0; i < tableCells.length; i++) {
            tableCells[i].style.width = headerCellWidths[i] + "px";            
            gridRow.getElementsByTagName("td")[i].style.width = headerCellWidths[i] + "px";
        }
    
        //get the parent div of the grid control
        var rootDiv = gridCtl.parentNode;
        
        //remove the grid control from this div
        rootDiv.removeChild(gridCtl);
        
        //add the grid to a new div
        var header = document.createElement("div");
        header.appendChild(table);
        rootDiv.appendChild(header);        
    
        //create a new child div and add the grid control to it
        var childDiv = document.createElement("div");
        gridWidth = parseInt(gridWidth) + 20;
        //this would create the scroll
        childDiv.style.cssText = "overflow:auto;height:300px;width:" + gridWidth + "px";
        childDiv.appendChild(gridCtl);
    
        //add the div to the root div
        rootDiv.appendChild(childDiv);
    }
    

    Run the application and you would see a vertical scrollbar.

    Be Sociable, Share!
      Post Tagged with , ,

    Written by

    Software architect with over 10 years of proven experience in designing & developing n-tier and web based software applications, for Finance, Telecommunication, Manufacturing, Internet and other Commercial industries. He believes that success depends on one's ability to integrate multiple technologies to solve a simple as well as complicated problem.

    View all articles by

    Email : [email protected]

    5 Responsesso far.

    1. Salvador says:

      Great example.
      Could you post your CSS style sheet, please?

    2. Iren says:

      Thank you very much!

    3. Benjamín Traverso says:

      Hi! where should i place function CreateGridHeader(gridName)? right in the code section below the Page_Load?
      Thanks!

      • Vanamali says:

        As specified in the post, you have to register the script in the page load function.

        protected void Page_Load(object sender, EventArgs e)
                {
                    if (!Page.IsPostBack)
                    {
                        ClientScript.RegisterStartupScript(this.GetType(),
                                    "CreateGridHeader",
                                    "<script>CreateGridHeader('gvProducts');</script>");
                    }
                }  
    4. Peter Luns says:

      When using the script in a master/content page construction you will need to prefix the name of the gridview with “MainContent_”

    Leave a Reply