Sample Image - FreezeHeader.jpg

Introduction

It is not any other article on Freezing Header and Columns but a Custom Control which ease the implementation of such requirement. Freeze Header, Rows, Columns in a Asp.net datagrid just changing a single attribute value. It is inherited from System.Web.UI.WebControls.DataGrid therefore all features of existing datagrid is still available.

Prerequisites

You need to have .Net Framework 1.1 Service Pack 1 installed on your system, as headers are rendered in <TH> tag then only.

Attributes

  • FreezeHeader: Freeze the Header or not. Default: false.
  • FreezeRows: Specify no. of Rows to be freezed. This will automatically freeze the header as well. Default: 0.
  • FreezeColumns: Specify no. of columns to freeze in the grid. Default: 0.
  • AddEmptyHeaders: This will add empty <tr><th></th>..</tr> rows, the benefit of it is when you want to keep a static header row in all of the pages of a datagrid.
  • EmptyHeaderClass: When AddEmptyHeaders is non zero, specify here the Empty Row CSS class here.
  • GridHeight: Grid height, this is actually height of <DIV> tag not of <table> which can be set through Height. When FreezeHeader is true or FreezeRows is non zero,always provide some value here.
  • GridWidth: Grid width, this is actually width of <DIV> tag not of <table> which can be set through Width. When FreezeColumns is non zero, always provide some value here.

Using the Code

Just Freeze Header in Datagrid

<Tittle:CustomDataGrid GridHeight="150" FreezeHeader=true     ../>

The above would be rendered as:

Freeze Table Rows in Datagrid. Pleae note header automatically freezes in this case.

<Tittle:CustomDataGrid GridHeight="110" FreezeRows=1     ../>

The above would be rendered as:

Freeze Table Columns in Datagrid

<Tittle:CustomDataGrid GridWidth="250" FreezeColumns=2     ../>

The above would be rendered as:

Add Freezed Empty Headers

<Tittle:CustomDataGrid GridHeight="209"         AddEmptyHeaders=1 EmptyHeaderClass="greyed"         FreezeHeader="true" ../>

The above would be rendered as:

Tehnical Implementation Insight

CustomDataGrid.cs

Collapse
using System;using System.Web.UI;using System.ComponentModel;using System.Web.UI.WebControls;using System.Text.RegularExpressions;using System.Collections.Specialized;using System.Reflection;[assembly: TagPrefix ("Tittle.Controls" , "Tittle") ]namespace Tittle.Controls{        /// <summary>    /// A Custom DataGrid which Allow freezing of Headers/Rwos/Columns    /// http://www.codeproject.com/script/articles/list_articles.asp?userid=1654009    /// </summary>    public class CustomDataGrid : DataGrid    {            #region Private Variables        private bool freezeHeader = false;        private int addEmptyHeaders = 0;        private int freezeRows = 0;        private string emptyHeaderClass = "";        private int freezeColumns = 0;        private Unit gridHeight = Unit.Empty;        private Unit gridWidth = Unit.Empty;        #endregion                #region Constructors        /// <summary>        /// default initialization with datagrid properties        /// </summary>        public CustomDataGrid(): base()         {            //set default appearence style            this.HeaderStyle.CssClass = "GridHeader";            this.FooterStyle.CssClass = "GridHeader";            this.ItemStyle.CssClass = "GridNormal";            this.AlternatingItemStyle.CssClass = "GridAlternate";            this.CssClass = "Grid";            this.BorderColor =                System.Drawing.Color.FromArgb(47, 33, 98); //"#2f2162"            this.BorderWidth = Unit.Parse("1");            this.GridLines = GridLines.Both;             this.CellPadding = 2;             this.CellSpacing = 0;                    //paging            this.PagerStyle.Mode = PagerMode.NumericPages;            this.PagerStyle.Position = PagerPosition.Bottom;             this.PagerStyle.HorizontalAlign = HorizontalAlign.Center;                             this.PagerStyle.ForeColor =                 System.Drawing.Color.FromArgb(0, 0, 255); //"#2f2162"                            this.PageSize = 10;        }        #endregion        #region Exposed Attributes        /// <summary>        /// Freeze Header : True/False        ///         /// Default: False        /// </summary>        [Browsable(true),        Category("Freeze"),        DefaultValue("false"),        Description ("Freeze Header.") ]         public bool FreezeHeader        {            get { return freezeHeader; }            set             {                                 freezeHeader = value;                if ( freezeHeader == true )                {                    //this attribute requires .Net Framework 1.1                     //service pack 1 installed                    //on system, it generates headers                     //in <th> tags instead of <td> tags.                    this.UseAccessibleHeader = true;                    //If user hasnt set height property set a default here.                    if ( this.gridHeight == Unit.Empty )                        this.gridHeight = 800;                }            }        }        /// <summary>        /// Freeze Data Rows : Specify No. of Rows        /// to Freeze from top excluding the header.        ///         /// Default: 0        /// </summary>        [Browsable(true),        Category("Freeze"),        DefaultValue("0"),        Description ("Freeze Data Rows.") ]         public int FreezeRows        {            get { return freezeRows; }            set             {                                 freezeRows = value;                    if ( freezeRows >= 1 )                {                    this.FreezeHeader = true;                }            }        }        /// <summary>        /// Specify here how many columns you want to freeze in the grid,         /// freeze columns from the left.        ///         /// Default: 0        /// </summary>        [Browsable(true),        Category("Freeze"),        DefaultValue("0"),        Description ("Specify no. of columns to freeze            in the grid, it freeze columns from the left.") ]        public int FreezeColumns        {            get { return freezeColumns; }            set { freezeColumns = value; }        }                /// <summary>        /// this will add empty <tr><th></th>..</tr> rows        /// the benefit of it is that later we can        /// through client side add/modify any text to individual cell         /// it is done since through client side        /// we can not add more than one row        /// with <th> tags to table.        ///         /// It is used when you set FreezeHeader to "true"        /// and wants to have more than one such headers         /// to be freezed but data of all such headers        /// is decided on client side.        ///         /// This is useful you want to keep a static        /// row in all of the pages of a datagrid.         /// </summary>        [Browsable(true),        Category("Freeze"),        DefaultValue("0"),        Description ("It will add empty rows.") ]         public int AddEmptyHeaders        {            get { return addEmptyHeaders; }            set { addEmptyHeaders = value;    }        }                        /// <summary>        /// Class of Empty rows <tr><th></th>.. </tr>        /// </summary>        [Browsable(true),        Category("Freeze"),        Description ("Class of empty rows.") ]        public string EmptyHeaderClass        {            get { return emptyHeaderClass; }            set { emptyHeaderClass = value; }        }                /// <summary>        /// Grid height, this is actually height of <DIV> tag not of        <table ID="Table1"> which can be set through "Height"        /// </summary>        [Browsable(true),        Category("Freeze"),        Description ("Grid height, this is actually height of           <DIV> tag not of <table ID="Table2">          which can be set through Height.") ]        public Unit GridHeight        {            get { return gridHeight; }            set { gridHeight = value; }        }        /// <summary>        /// Grid width, this is actually width of <DIV> tag not of         /// <table ID="Table3"> which can be set through "Width"        /// </summary>        [Browsable(true),        Category("Freeze"),        Description ("Grid width, this is actually width           of <DIV> tag not of <table ID="Table4">           which can be set through Width.") ]        public Unit GridWidth        {            get { return gridWidth; }            set { gridWidth = value; }        }        #endregion        #region Overriden events like OnPreRender / Render        /// <summary>        /// Before rendering to screen, check if freezing        /// of column/header/row is required.        /// Add <div> tag before <table ID="Table5"> tag then        /// </summary>        /// <param name="e"></param>        protected override void OnPreRender(EventArgs e)        {                        #region DataGrid freezing script            //DataGrid functions/styles etc.            if (!Page.IsClientScriptBlockRegistered("FreezeGridScript") )            {                string freezeGridScript = @"                                     function FreezeGridColumns(dgTbl, colNo)                      {                                                         var tbl = document.getElementById(dgTbl);                         for ( var i=0; i<tbl.rows.length; i++)                         {                             for ( var j=0; j<colNo; j++)                              {                                  tbl.rows[i].cells[j].className = 'locked';                              }                          }                                      }                 ";                Page.RegisterClientScriptBlock("FreezeGridScript",                      "<script language=javascript>\n" +                      freezeGridScript + "\n</script>");            }            if ( !Page.IsClientScriptBlockRegistered("GridStyle") )            {                string gridStyle = "";                gridStyle = @"                      <style TYPE='text/css'>                     /* Locks table header */                    th                     {                                             /* border-right: 1px solid silver; */                         position:relative;                        cursor: default;                        /*IE5+ only*/                         top: expression(this.parentElement.parentElement.                             parentElement.parentElement.scrollTop -2);                        z-index: 10;                        }                    TR.GridNormal TH, TR.GridAlternate TH                    {                        text-align:left;                    }                    TR.GridHeader TH                    {                        text-align:center;                    }                                                                /* Locks the left column */                     td.locked, th.locked                     {                                             /* border-right: 1px solid silver; */                         position:relative;                         cursor: default;                         /*IE5+ only*/                            left: expression(this.parentElement.parentElement.                              parentElement.parentElement.scrollLeft-2);                        }                                         /* Keeps the header as the top most item.                       Important for top left item*/                     th.locked {z-index: 99;}                     </style>                     <style>                    /*Overriding Grid Styles*/                    .Grid                    {                        border:0;                        background-color: #808080;                        }                    .GridHeader                    {                        background-color: #547095;                        color:White;                        font-weight:bold;                        font-family:Tahoma;                        text-align:center;                            padding : 1px 0px 1px 0px;                        font-size:8pt;                            }                    .GridHeader TD A, TH A                    {                        text-decoration:none;                        color:White;                    }                    .GridNormal                    {                        background-color: #FFFFFF;                        font-weight: normal;                        font: x-small Verdana;                    }                    .GridAlternate                    {                        background-color: #EFF8FC;                        font-weight: normal;                        font: x-small Verdana;                    }                    .GridSelected                    {                        background-color: #FFC082;                        font-weight: normal;                        font: x-small Verdana;                    }                    .GridPager                    {                        background-color : White;                        font-size : x-small;                    }                    </style>                    ";                 Page.RegisterClientScriptBlock("GridStyle",gridStyle);            }            //Trim space from the div and datagrid table.            string gridOnLoad = "";            gridOnLoad = @"                // trims the height of the div surrounding the results table                // so that if a not a lot of results are returned,                // you dont have a lot of blank space between the results and                // the last buttons on the page                var divTbl = document.getElementById('div_"+this.ID + @"');                var resultsTable = divTbl.children[0];                if ( typeof(resultsTable) != 'undefined' )		{                //alert( 'div ' + divTbl.offsetHeight + ' results: '                 //         +  resultsTable.offsetHeight );                if( divTbl.offsetHeight + 3 > resultsTable.offsetHeight )                     divTbl.style.height = resultsTable.offsetHeight + 50;                }                ";            //If <div> generated then only            if (freezeColumns >= 1 || freezeHeader == true)                 Page.RegisterStartupScript("GridOnLoad",                      "<script language=javascript>\n" +                      gridOnLoad + "\n</script>");            #endregion            base.OnPreRender(e);        }        /// <summary>        /// Render datagridservercontrol        /// </summary>        /// <param name="output"></param>        protected override void Render(HtmlTextWriter output)        {                                    if ( freezeRows >= 1 || addEmptyHeaders >= 1 ||                  freezeColumns >= 1 || freezeHeader == true )             {                #region Only execute when need to any of this:                         Freeze Header, Row, Add Empty Header,                         Freeze Columns                  //Get the output string rendered.                System.Text.StringBuilder sb = new                        System.Text.StringBuilder();                HtmlTextWriter writer = new HtmlTextWriter(new                                         System.IO.StringWriter(sb));                base.Render(writer);                //the output has been retrieved                 //in the stringbuilder AND do the modification here                      string datgridString = sb.ToString();                //FREEZE MORE ROWS OTHER THAN JUST HEADER                if ( freezeRows >= 1 )                {                                int cnt = 0;                    for ( int i=0; i<=freezeRows; i++)                    {                        cnt = datgridString.IndexOf("</tr>",cnt);                        if ( i < freezeRows)                            cnt++;                    }                    if ( cnt != -1 )		    {                       string firstpart,secondpart;                       firstpart = datgridString.Substring(0,cnt);                       secondpart = datgridString.Substring(cnt);                                       firstpart = Regex.Replace(firstpart, @"<td",                                 @"<th class='innerBorder' " );                       firstpart = Regex.Replace(firstpart,                                 @"</td>", @"</th>");                       datgridString = firstpart + secondpart;                    }                }                            //ADD EMPTY HEADERS. <TH>                //This all done as through client side JAVASCRIPT                 //you can not add more than one row TH                 //tag to existing TABLE.                if ( addEmptyHeaders >= 1 )                {                    string rowData="";                    for ( int i=1; i<=addEmptyHeaders; i++)                    {                        rowData +="<TR class='" + EmptyHeaderClass +                                                               "' >";                                                    for ( int j=0; j<this.Columns.Count; j++)                        {                            string clsname = "";                            if ( j == 0 )                                clsname="leftBorder";                            if ( j == this.Columns.Count-1 )                                clsname="rightBorder";                            if ( j > 0  && j < this.Columns.Count )                                clsname="innerBorder";                            rowData += "<TH class='" + clsname +                                        "' ></TH>";                        }                        rowData += "</TR>";                    }                    if ( addEmptyHeaders >= 1)                    {                        int headerEnd =                             datgridString.IndexOf("</tr>",0);                        string prePart =                             datgridString.Substring(0,headerEnd+5);                        string postPart =                             datgridString.Substring(headerEnd+5);                        datgridString = prePart + rowData + postPart;                    }                }                if ( freezeColumns >= 1 )                {                    if ( this.gridWidth == Unit.Empty )                        this.gridWidth = 800;                                    string freezeGridColumn="FreezeGridColumns('" +                            this.ID + "',"+freezeColumns.ToString()+");";                    Page.RegisterStartupScript("freezeGridColumn",                          "<script language=javascript>\n" +                          freezeGridColumn + "\n</script>" );                }                if ( freezeColumns >= 1 || freezeHeader == true )                {                    string divstring;                    divstring = "<div id='div_" + this.ID +                                 "' style='";                                if ( freezeHeader == true )                        divstring += " HEIGHT:"+this.gridHeight+"; ";                    if ( freezeColumns >= 1 )                        divstring += " WIDTH:"+this.gridWidth+"; ";                    datgridString = divstring + " OVERFLOW:auto; ' >"                                     + datgridString + "</div>";                }                output.Write(datgridString.ToString());                    #endregion            }            else            {                base.Render(output);            }        }        #endregion    }}

Display.ASPX - (View Output)

Collapse
<%@ Register TagPrefix="Tittle" Namespace="Tittle.Controls" Assembly="Controls" %><Tittle:CustomDataGrid id="dgTittle"          FreezeHeader=true runat="server"          AutoGenerateColumns=False GridHeight="150"    ><Columns>    <asp:TemplateColumn HeaderText="User Name" >        <ITEMTEMPLATE>            <asp:Label Runat="server" ID="lblName" Width="100"   Text='<%# DataBinder.Eval(Container, "DataItem.Name") %>'  />        </ITEMTEMPLATE>    </asp:TemplateColumn>    <asp:TemplateColumn HeaderText="Grade"               HeaderStyle-HorizontalAlign=Right  >        <ITEMTEMPLATE>            <asp:TextBox Runat="server"  Width="100" ID="txtAge" Text='<%# DataBinder.Eval(Container, "DataItem.Age") %>'  />        </ITEMTEMPLATE>    </asp:TemplateColumn></Columns>                  </Tittle:CustomDataGrid>

FreezeRow.ASPX - (View Output)

<%@ Register TagPrefix="Tittle" Namespace="Tittle.Controls" Assembly="Controls" %><Tittle:CustomDataGrid id="dgTittle2" FreezeHeader=true  runat="server" AutoGenerateColumns=True  GridHeight="110"  FreezeRows=1> />

AddEmptyHeaders ASPX - (View Output)

Collapse
<%@ Register TagPrefix="Tittle" Namespace="Tittle.Controls" Assembly="Controls" %><body onload="AddEmptyHeaderDataLoad()">..<style>.greyed{    background-color:#c0c0c0;}</style><script language=javascript>function AddEmptyHeaderDataLoad(){    if ( typeof(dgTittle4) != 'undefined' )    {        dgTittle4.rows[1].cells[0].innerText = 'Tittle Joseph';        dgTittle4.rows[1].cells[1].innerText = '29';        dgTittle4.rows[1].cells[2].innerText =                               '5/67 Rachna Vaishali Ghaziabad';    }}        </script><Tittle:CustomDataGrid id="dgTittle4" GridHeight="209" AutoGenerateColumns=false    AddEmptyHeaders=1 EmptyHeaderClass="greyed" PageSize=7     FreezeHeader=true PagerStyle-Mode=NumericPages     AllowPaging=True     OnPageIndexChanged="dgTittle4_PageIndexChanged"><Columns>    <asp:TemplateColumn HeaderText="Name" >        <ITEMTEMPLATE>            <asp:Label Runat="server" ID="Label3" Width="100" Text='<%# DataBinder.Eval(Container, "DataItem.Name") %>'  />        </ITEMTEMPLATE>    </asp:TemplateColumn>    <asp:TemplateColumn HeaderText="Grade"              HeaderStyle-HorizontalAlign=Right  >        <ITEMTEMPLATE>            <asp:Label Runat="server" ID="Label4"                  Text='<%# DataBinder.Eval(Container,                            "DataItem.Age") %>'  />        </ITEMTEMPLATE>    </asp:TemplateColumn>    <asp:TemplateColumn HeaderText="Address"               HeaderStyle-HorizontalAlign=Right               ItemStyle-Wrap=False >        <ITEMTEMPLATE>            <asp:Label Runat="server" ID="Label5"               Text='<%# DataBinder.Eval(Container,                         "DataItem.Address") %>'  />        </ITEMTEMPLATE>    </asp:TemplateColumn></Columns>         </Tittle:CustomDataGrid>..</body>

FAQs

Do I need to make any change in CodeBehind or need to include any other set of lines or files for freezing column/row/header of a datagrid after I use Custom Control you supplied?

Nope. Just use Custom Control provided and set exposed attribute.

Header/Column is transparent and I could see data while scrolling.?

While freezing header/column it is mandatory to have a backcolor of all freezed columns/rows, so provide the background color and transparency problem will go away.

Does it support cross-browsers?

I dont think so, I checked this in IE only and works fine there.

Will this code work in .NET 2.0 or Above?

I dont know I dont have .NET 2.0 to test it.

Can I Freeze any no. of Columns and Rows altogether?

Yes. Without a problem.

Where I can use attribute "FreezeRows"?

Let's say you want to compare marks of a student who got highest compare to others, then you can bring highest marked user on top of grid and then set FreezeRows=1, which freezes top highest student, and then compare with other students.

What is "AddEmptyHeaders", I'm not able to judge where I'll be using it?

Lets say you want to compare a customer [A] sales with other customers sales (thousands records), so you can keep customer [A] information freezed in top row and compare with other customers in all of the pages.

I think I can use "FreezeRows" instead of "AddEmptyHeaders"?

Both have their own benefits. "FreezeRows" always freezes rows from top, so in next page you wont be seeing the same record freezed, so FreezeRows is best used when all records are on the same page, because you dont see same records on all the pages. Whereas when you use "AddEmptyHeaders" you fill the top row data on client side on page load, which remains same on all pages of a datagrid. So conclusion is if there is no paging in datagrid use "FreezeRows" (freezed rows will be filled from server side) Or use "AddEmptyHeaders" (Freezed rows need to be filled at client side.).

I'm getting error. Attribute "UseAccessibleHeader" not found?

You must have .net Framework 1.1 SP1 installed on your system. Download it from MSDN.

Resources

There are few sites which helped me achieving this, but what I felt is that everytime I want to freeze header/column in a new data grid I've to re-write all complex logic once again, to mitigate this problem I implemented all such complex logic within the custom datagrid and exposed attributes which easily let freezing of headers and columns, without knowing the actual implementation and logic behind it.

Site from where I picked up the logic of freezing the header

http://www.codeproject.com/aspnet/FreezePaneDatagrid.asp

Other such freeze header related sites

http://slingfive.com/pages/code/scrollTable/ http://www.codeproject.com/aspnet/DataGridFixedHeader.asp http://www.codeproject.com/aspnet/ScrollingGrid.asp

Conclusion

I would really be interested in knowing if this code has helped you by any means, please do not hesitate or be lazy in dropping your comments telling what you have felt about this submission and how much it has helped you. I would appreciate it if you keep the URL of this page inside the control's code while using it.

History

  • Created on: 20 Feb 2006.
  • Modified on: 24 Feb 2006.

About Tittle Joseph


Total 7.5+ years of experience in Software Development. Expertise in .net (c#, asp.net, controls making, webservice, ado.net, xml/xsl, assemblies, rational XDE etc.) also ASP, Javascript, VbScript, Dhtml, CSS etc.

Regular member of Planet-Source-Code.com, www.brainbench.com and many other and have submitted several snippets/sample applications on the web.

Working with HCL Technologies Noida as Project Leader.

Email:tittlejoseph@yahoo.com

Click here to view Tittle Joseph's online profile.

'ASP.NET' 카테고리의 다른 글

Ajax C#.NET simple chat:  (0) 2007.09.04
Datareader to an Excel file using C#  (0) 2007.09.04
DataTableReader  (0) 2007.09.03
Using JavaScript Along with ASP.NET  (0) 2007.08.28
VS 2008/.Net Framework 3.5 Beta2 출시  (0) 2007.08.25
Posted by 퓨전마법사
,