Vinz' Blog (ProudMonkey)

"Code, Beer and Music ~ my way of being a programmer"
posts - 146, comments - 611, trackbacks - 0

My Links

News

Tag Cloud

Archives

Post Categories

Image Galleries

I'm a...

I'm at...

FAQ: Cross Browser GridView Fix Header and Footer with ASP.NET Ajax

Technorati Tags: ,,

 

Many developers from the forums (forums.asp.net) are asking if how to implement fix header in GridView while scrolling. We all know that there are lots of examples out there that provide a solution that are available, however some of the solutions provided are not cross browser compatible. While searching for a cross browser solution, I found this article at devarchive.net and it seems very interesting to me. So I play around with the extender provided by devarchive team and thought what if I also wanted to show the footer while scrolling the Grid?So I decided to extend the control a little bit further and added the fix footer while scrolling the rows in the GridView. See the sample screen shots below:

 

IE:

 

 

Firefox:

 

 

Note:I will not elaborate more about the implementation of the extender so before you proceed I would suggest you to read and download the original source codes at devarchive.net first.

 

Here are the updated codes below:

 

GridBoxDesigner.cs

 

using System;

using System.Collections.Generic;

using System.Text;

using System.Web.UI.Design;

using System.Globalization;

using System.Web.UI;

 

namespace GridBox

{

    public class GridBoxDesigner : ControlDesigner

    {

 

        private const string StandardDesignTimeHtml =

        @"<table style=""border:1px solid #CCCCCC;"" cellspacing=""0"" cellpadding=""0"">

        <tr>

        <td nowrap

        style=""font:messagebox;background-color:#ffffff;color:#444444;background-position:bottom;background-repeat:repeat-x;padding:4px;"">

        <strong>{0}</strong> - {1}</td>

        </tr>

        </table>";

 

        public override string GetDesignTimeHtml()

        {

            return

                 String.Format(

                    CultureInfo.InvariantCulture,

                    StandardDesignTimeHtml,

                    Component.GetType().Name,

                    ((Control)Component).ID);

        }

 

    }

}

 

 

GridBoxExtender.cs

 

using System;

using System.Collections.Generic;

using System.Text;

using System.Web.UI;

using System.Web.UI.WebControls;

using System.ComponentModel;

using System.ComponentModel.Design;

using System.Web;

using System.Security.Permissions;

 

namespace GridBox

{

    [

        AspNetHostingPermission(SecurityAction.LinkDemand, Level = AspNetHostingPermissionLevel.Minimal),

        AspNetHostingPermission(SecurityAction.InheritanceDemand, Level = AspNetHostingPermissionLevel.Minimal),

        Designer("GridBox.SimpleDesigner, GridBox"),

        ToolboxData("<{0}:GridBoxExtender runat=server></{0}:GridBoxExtender>"),

        TargetControlType(typeof(GridView))

    ]

    public class GridBoxExtender : ExtenderControl

    {

        #region Overrides

        protected override void OnLoad(EventArgs e) {

            base.OnLoad(e);

        }

 

        protected override IEnumerable<ScriptDescriptor> GetScriptDescriptors(Control targetControl) {

            if (TargetControl == null || !TargetControl.Visible || TargetControl.Rows.Count == 0) {

                TargetControl.Height = Unit.Empty;

                yield break;

            }

            ScriptBehaviorDescriptor descriptor = new ScriptBehaviorDescriptor("GridBox.GridBoxExtender", targetControl.ClientID);

            descriptor.AddProperty("scrollField", HiddenFieldID);

            yield return descriptor;

        }

 

        protected override IEnumerable<ScriptReference> GetScriptReferences() {

            if (TargetControl == null || !TargetControl.Visible || TargetControl.Rows.Count == 0) {

                TargetControl.Height = Unit.Empty;

                yield break;

            }

            yield return new ScriptReference("GridBox.GridBoxExtender.js", this.GetType().Assembly.FullName);

        }

 

        protected override void Render(HtmlTextWriter writer) {

            ScriptManager.RegisterHiddenField(

                this,

                HiddenFieldID,

                LastScroll.ToString()

                );

            base.Render(writer);

        }

 

        private GridView TargetControl {

            get {

                GridView result = this.NamingContainer.FindControl(TargetControlID) as GridView;

                return result;

            }

        }

 

        private int LastScroll {

            get {

                int result = 0;

                if (Page.Request[HiddenFieldID] != null) {

                    int.TryParse(Page.Request[HiddenFieldID], out result);

                }

                return result;

            }

        }

 

        private string HiddenFieldID {

            get {

                return String.Format("{0}_GVFHE_Scroll", ClientID);

            }

        }

 

        #endregion

    }

}

 

 

The JavaScript file:

 

GridBoxExtender.js

 

/// <reference name="MicrosoftAjax.debug.js" />

/// <reference name="MicrosoftAjaxTimer.debug.js" />

/// <reference name="MicrosoftAjaxWebForms.debug.js" />

 

Type.registerNamespace("GridBox");

 

    GridBox.GridBoxExtender = function(element) {

    GridBox.GridBoxExtender.initializeBase(this, [element]);

    this._documentResizeDelegate = null;

    this._lock = false;

    this._mainTableID = null;

    this._innerTableID = null;

    this._divChild = null;

   

    this._scrollField = 0;

}

 

GridBox.GridBoxExtender.prototype = {

    // Overrides

    //#region

    initialize: function() {

        GridBox.GridBoxExtender.callBaseMethod(this, 'initialize');

        this.initGrid();

    },

    dispose: function() {

        //Add custom dispose actions here

        $removeHandler(window, "resize", this._documentResizeDelegate);

        if (this._divChild) {

            $clearHandlers(this._divChild);

        }

        GridBox.GridBoxExtender.callBaseMethod(this, 'dispose');

    },

    //#endregion

 

    // Properties

    //#region

    get_scrollField: function() {

        return this._scrollField;

    },

 

    set_scrollField: function(value) {

        if (this._scrollField !== value) {

            this._scrollField = value;

            this.raisePropertyChanged('scrollField');

        }

    },

    //#endregion

 

    // Methods

    //#region

    getLastScroll: function() {

        var result = 0;

        var hf = $get(this._scrollField);

        if (hf) {

            result = parseInt(hf.value);

            if (!result) result = 0;

        }

        return result;

    },

    setLastScroll: function(value) {

        var hf = $get(this._scrollField);

        if (hf) {

            hf.value = value;

        }

    },

    initGrid: function() {

        // create deep clone of target grid

        var target = this.get_element();

        var clone = target.cloneNode(true);

 

        // get desired height of inner scrollable area

        var height = target.style.height;

        var width = target.style.width;

 

        var mainTable = target.cloneNode(false);

        mainTable.id = String.format("outer_{0}", target.id);

        target.parentNode.insertBefore(mainTable, target);

 

 

        var mainHead = document.createElement("thead");

        mainTable.appendChild(mainHead);

        var mainBody = document.createElement("tbody");

        mainTable.appendChild(mainBody);

 

        //**** ADDED FOOTER

        var mainFoot = document.createElement("tfoot");

        mainTable.appendChild(mainFoot);

 

        // Clone original header

        var header = target.rows[0].cloneNode(true);

        mainHead.appendChild(header);

 

        //**** ADDED FOOTER clone footer

        var footer = target.rows[target.rows.length - 1].cloneNode(true);

        mainFoot.appendChild(footer);

 

        // add scrollable area mainTable

        var secondRow = document.createElement("tr");

        mainBody.appendChild(secondRow);

        var mainTd = document.createElement("td");

        secondRow.appendChild(mainTd)

        this.setAttribute(mainTd, "colspan", target.rows[0].cells.length);

        this.setAttribute(mainTd, "align", "left");

        this.setAttribute(mainTd, "valign", "top");

        var divChild = document.createElement("div");

        mainTd.appendChild(divChild);

        divChild.style.width = width;

        divChild.style.height = height;

        $addHandler(divChild, "scroll", Function.createDelegate(this, this.syncScroll));

        divChild.style.overflow = "auto";

        divChild.style.overflowX = "hidden";

        divChild.style.overflowY = "scroll";

        this._divChild = divChild;

 

        //        Sys.UI.DomElement.addCssClass(divChild, "divScrollVertical");

 

        // now remove old grid from document and insert new clone into the place

        target.parentNode.removeChild(target);

        divChild.appendChild(clone);

 

        // assign extender related data to clone

        clone._behaviors = target._behaviors;

        clone.GridBoxExtender = target.GridBoxExtender;

 

        // correct styles

        var attributes = [];

        for (var i = 0; i < clone.attributes.length; i++) {

            var attr = clone.attributes.item(i);

            var value = attr.value.trim().toLowerCase();

            if (value != "cellpadding" && value != "cellspacing") {

                Array.add(attributes, attr);

            }

        }

        Array.forEach(attributes, this.deleteAttribute, clone);

        clone.deleteRow(0); //remove header row

        clone.deleteRow(clone.rows.length - 1); //remove 5footer

        clone.border = "0";

        clone.style.borderWidth = "0px";

        clone.style.width = "100%";

        clone.style.height = "";

        mainTable.style.height = "";

        target.style.height = "";

 

       

        // correct widths of header columns and subscribe to document resize event:

        this._mainTableID = mainTable.id;

        this._innerTableID = clone.id;

        this._documentResizeDelegate = Function.createDelegate(

            this,

            this.syncWidths

        );

        this._documentResizeDelegate.call();

        // Attach to window's resize event to resize header cells when inner cells change their size

        $addHandler(window, "resize", this._documentResizeDelegate);

        // Restore scroll position from last time 

        divChild.scrollTop = this.getLastScroll();

    },

 

    setAttribute: function(element, attribute, value) {

        var namedItem = document.createAttribute(attribute);

        namedItem.value = value;

        element.attributes.setNamedItem(namedItem);

    },

    deleteAttribute: function(attribute, index, attributes) {

        this.removeAttribute(attribute);

    },

    syncScroll: function(args) {

        if (this._divChild) {

            this.setLastScroll(this._divChild.scrollTop);

        }

    },

    syncWidths: function(args) {

        if (!this._lock) {

            this._lock = true;

            var mainTable = $get(this._mainTableID);

            var innerCellPadding = mainTable.cellPadding;

            var header = mainTable.rows[0];

            var innerTable = $get(this._innerTableID);

            var originalRow = innerTable.rows[0];

            var headerWidth = Sys.UI.DomElement.getBounds(header).width;

            var originalRowWidth = Sys.UI.DomElement.getBounds(originalRow).width;

            var diff = headerWidth - originalRowWidth - innerCellPadding * 2;

            if (originalRow && header) {

                for (var i = 0; i < originalRow.cells.length; i++) {

                    var bounds = Sys.UI.DomElement.getBounds(originalRow.cells[i]);

                    var x = bounds.width;

                    if (i == originalRow.cells.length - 1) {

                        x = x + diff - innerCellPadding * 2;

                    } else {

                        x = x - innerCellPadding;

                    }

                    header.cells[i].style.width = x + "px";

                }

            }

            this._lock = false;

        }

    }

    //#endregion

}

GridBox.GridBoxExtender.registerClass('GridBox.GridBoxExtender', Sys.UI.Behavior);

 

 

Usage of the Extender:

 

<%@ Page Language="C#" AutoEventWireup="true" CodeFile="TestGridBox.aspx.cs" Inherits="TestGridBox" %>

 

<%@ Register Assembly="GridBox" Namespace="GridBox" TagPrefix="cc1" %>

 

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

 

<html xmlns="http://www.w3.org/1999/xhtml">

<head runat="server">

    <title>GridBox Demo</title>

    <link href="CSS/GridBoxSample.css" rel="stylesheet" type="text/css" />

</head>

<body>

    <form id="form1" runat="server">

    <asp:ScriptManager ID="ScriptManager1" runat="server"/>

    <div style="width:500px">

        <asp:GridView ID="GridView1" runat="server" Width="100%" GridLines="None" ShowFooter="true"  AutoGenerateColumns="false" CssClass="gridbox" Height="150px">

        <RowStyle CssClass="data-row" />

        <FooterStyle CssClass="footer-style"/>

        <Columns>

            <asp:TemplateField ItemStyle-Width="5%">

            <HeaderTemplate>

                <asp:CheckBox ID="CheckBoxAll" runat="server" Text="All" />

            </HeaderTemplate>

            <ItemTemplate>

                <asp:CheckBox ID="CheckBoxSelect" runat="server" />

            </ItemTemplate>

            <FooterTemplate>

            </FooterTemplate>

            </asp:TemplateField>

            <asp:BoundField DataField="TerminalNumber" HeaderText="Terminal #" ItemStyle-Width="10%"  />

            <asp:BoundField DataField="TransactionNumber" HeaderText="Transaction #" ItemStyle-Width="10%"/>

            <asp:BoundField DataField="OperatorNumber" HeaderText="Operator #" ItemStyle-Width="10%"/>

            <asp:BoundField DataField="OperatorName" HeaderText="Operator Name" ItemStyle-Width="10%"/>

        </Columns>

        </asp:GridView>

       <cc1:GridBoxExtender ID="GridBoxExtender1" runat="server" TargetControlID="GridView1" />

      </div>

    <asp:Button ID="Button1" runat="server" Text="Button" />

    </form>

</body>

</html>

 

That was it! Hope you will find this post useful!

 

Again credits to http://devarchive.net/ (Kirill Chilingarashvili)

Print | posted on Wednesday, January 06, 2010 5:17 PM | Filed Under [ ASP.NET C# GridView ]

Feedback

Gravatar

# re: FAQ: Cross Browser GridView Fix Header and Footer with ASP.NET Ajax

Excellent article !
6/8/2010 8:11 PM | Ramesh Patel
Post A Comment
Title:
Name:
Email:
Website:
Comment:
Verification:
 
 

Powered by: