ASP.NET控件开发之ScrollGridView(兼容FF、IE、苹果、谷歌、搜狗,固定表头滚动的GridView控件)

这篇文章本来在2个月前就应该写了的,只是一直没有找到好的解决方案,所以迟迟没有动笔,直到今天,才找到了比较满意的解决方案。

网上关于固定GridView表头,常见的有两种解决方案,一种是采用css样式,一种则是使用js代码。我再后面贴出了网上常用的解决方案,不过这两种解决方案都无法从根本上解决问题,并且还有一个致命的缺点,那就是在FF中不支持,我认为采用jquery重写表头扩展GridView才是釜底抽薪的解决办法。记得在网上有一个固定表头的GridView扩展控件,不过这个控件在Update下面一回发就会报错。我这个控件是在GridViewFixedHeaderExtender控件之上进行了改良的,因为我发现GridViewFixedHeaderExtender在IE9中页面第一次加载的时候表头布局会乱,经过我一番研究,发现这是因为在IE9中它的相对定位出了问题,苦思了一整天,终于想出了一个好的解决方案,在IE9中,对其进行特别处理,表头的外层设置为相对定位,内层设置为绝对定位,这样就一切oK了。

本控件最终效果图:

以下是ScrollGridView的源代码:

<span style="color:#00ff;">using System;
using System.Collections.Generic;
using System.Text;
using System.Web.UI;
using System.Web.UI.WebControls;


using System.ComponentModel;
using System.Web;
using System.Security.Permissions;


[assembly: WebResource("SureKAM.SPM.Portal.Controls.Resources.Script.GridViewFixedHeaderExtender.js", "application/x-javascript", PerformSubstitution = true)]
namespace SureKAM.SPM.Portal.Controls
{
    [
        AspNetHostingPermission(SecurityAction.LinkDemand, Level = AspNetHostingPermissionLevel.Minimal),
        AspNetHostingPermission(SecurityAction.InheritanceDemand, Level = AspNetHostingPermissionLevel.Minimal),
        Designer("SureKAM.SPM.Portal.Controls.SimpleDesigner, SureKAM.SPM.Portal.Controls"),
        ToolboxData("<{0}:GridViewFixedHeaderExtender runat=server></{0}:GridViewFixedHeaderExtender>"),
        TargetControlType(typeof(GridView))
    ]
    public class GridViewFixedHeaderExtender : 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("SureKAM.SPM.Portal.Controls.GridViewFixedHeaderExtender", 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("SureKAM.SPM.Portal.Controls.Resources.Script.GridViewFixedHeaderExtender.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
    }
}</span>

js代码如下:

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


Type.registerNamespace("SureKAM.SPM.Portal.Controls");


SureKAM.SPM.Portal.Controls.GridViewFixedHeaderExtender = function (element) {
    SureKAM.SPM.Portal.Controls.GridViewFixedHeaderExtender.initializeBase(this, [element]);
    this._documentResizeDelegate = null;
    this._lock = false;
    this._mainTableID = null;
    this._innerTableID = null;
    this._divChild = null;


    this._scrollField = 0;
}


SureKAM.SPM.Portal.Controls.GridViewFixedHeaderExtender.prototype = {
    // Overrides
    //#region
    initialize: function () {
        SureKAM.SPM.Portal.Controls.GridViewFixedHeaderExtender.callBaseMethod(this, 'initialize');
        this.initGrid();
    },
    dispose: function () {
        //Add custom dispose actions here
        $removeHandler(window, "resize", this._documentResizeDelegate);
        if (this._divChild) {
            $clearHandlers(this._divChild);
        }
        SureKAM.SPM.Portal.Controls.GridViewFixedHeaderExtender.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.style.position = "relative";
        mainTable.id = String.format("outer_{0}", target.id);
        target.parentNode.insertBefore(mainTable, target);


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


        if (navigator.appName == "Microsoft Internet Explorer" && navigator.appVersion.split(";")[1].replace(/[ ]/g, "") == "MSIE9.0") {
            var headDiv = document.createElement("div");
            headDiv.style.position = "relative";
            headDiv.style.width = "100%";
            var mainHead = document.createElement("thead");      
            mainHead.style.position = "absolute";
            headDiv.appendChild(mainHead);
            mainTable.appendChild(headDiv);
                        var div = document.createElement("div");
                        div.style.height = target.rows[0].style.height;
                        div.style.width = target.rows[0].style.width;
                        //div.style.minWidth = target.rows[0].style.width;
                        //div.style.position = "static";
                        mainTable.appendChild(div);
        }
        else {
            var mainHead = document.createElement("thead");
            mainTable.appendChild(mainHead);
        }


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


        // Clone original header
        //var header = target.rows[0].cloneNode(true);
        mainHead.appendChild(header);


        // 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.GridViewFixedHeaderExtender = target.GridViewFixedHeaderExtender;


        // 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(clone.rows[0]);
        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;
                    }
                    if (navigator.appName == "Microsoft Internet Explorer" && navigator.appVersion.split(";")[1].replace(/[ ]/g, "") == "MSIE9.0") {
                    }
                    else {
                        header.cells[i].style.width = x + "px";
                    }
                }
            }
            this._lock = false;
        }
    }
    //#endregion
}
SureKAM.SPM.Portal.Controls.GridViewFixedHeaderExtender.registerClass('SureKAM.SPM.Portal.Controls.GridViewFixedHeaderExtender', Sys.UI.Behavior);


1,样式固定。

这个方法是从网上参考的,但是忘记了来源,使用之后发现效果不是很好,有闪动,并且在FF浏览器中不支持。以下是源码(来源于网络):


<style type="text/css"> 
.Freezin
{ 
position:relative; 
table-layout:fixed;
top:expression(this.offsetParent.scrollTop); 
z-index: 10;
} 
.Freezing th{text-overflow:ellipsis;overflow:hidden;white-space: nowrap;padding:2px;} 
</style>

2,Javascript方法。

也是网上参考,搜索应该比较多,据网友回帖说是效果很好,自己使用效果不好。以下是源码分析

// 创建表头
if (document.getElementById( " gvTitle " ==  null )
{
var  gdvList  =  document.getElementById( " gvCommon " );
var  gdvHeader  =  gdvList.cloneNode( true );
gdvHeader.id  =  " gvTitle " ;
for (i  =  gdvHeader.rows.length  -  1 ; i  >  0 ;i -- )
{
gdvHeader.deleteRow(i);
}
document.getElementById( " divTitle " ).appendChild(gdvHeader);
var  div  =  document.getElementById( " divGvData " );
var  tbl  =  document.getElementById( " divTitle " );
tbl.style.position  =  " absolute " ;
tbl.style.zIndex  =  100 ;
tbl.style.top  =  div.offsetTop;
tbl.style.left  =  div.offsetLeft;
}

大致做法是利用JS方法Copy出一个表头 gdvHeader 放在一个“divTitle”的DIV中。

GridView是包含在“divGvData”DIV中的,然后设置divTitle的页面位置和divGvData的一致,也就是覆盖在上面。 目前发现效果还行。有一点要注意,gdvHeader.id = "gvTitle";要重新设置一个ID,不然删除的还是GridView的数据行。缺点:FF中不支持。

HTML中的部分代码:

< div  id ="divTitle"  style ="position:relative; top:0; left:0; overflow:hidden; width:978px; border:0px solid red;" ></ div >
< div  id ="divGvData"  runat ="server"  style ="position:relative; top:0px; left:0px; overflow:scroll; width:994px;height:450px;"  onscroll ="funGrilViewScroll(); return false;" >
< asp:GridView  ID ="gvCommon"  style ="position:relative; top:0px; left:0px;"  runat ="server"  CssClass ="gvFixd"  BackColor ="White"  BorderColor ="#999999" BorderStyle ="None"  BorderWidth ="1px"  CellPadding ="3"  AutoGenerateColumns ="False"  GridLines ="Vertical"  PageSize ="5"  AllowSorting ="True" OnSorting ="gvCommon_Sorting"   >
< FooterStyle  BackColor ="#CCCCCC"  ForeColor ="Black"  />
< RowStyle  BackColor ="#E7E7FF"  ForeColor ="Black"  Font-Size ="Small"  />
< HeaderStyle  HorizontalAlign ="Center"  BackColor ="#000084"  BorderColor ="White"  BorderWidth ="1px"  BorderStyle ="Solid"  Font-Bold ="True"  ForeColor ="White" />
</ asp:GridView >
</ div >

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

邹琼俊

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值