`
java-mans
  • 浏览: 11414392 次
文章分类
社区版块
存档分类
最新评论

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

 
阅读更多

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

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

本控件最终效果图:

以下是ScrollGridView的源代码:

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
  }
}

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">
.Freezing
{
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)
{
vargdvList=document.getElementById("gvCommon");
vargdvHeader=gdvList.cloneNode(true);
gdvHeader.id="gvTitle";
for(i=gdvHeader.rows.length-1; i>0;i--)
{
gdvHeader.deleteRow(i);
}
document.getElementById("divTitle").appendChild(gdvHeader);
vardiv=document.getElementById("divGvData");
vartbl=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中的部分代码:

<divid="divTitle"style="position:relative; top:0; left:0; overflow:hidden; width:978px; border:0px solid red;"></div>
<divid="divGvData"runat="server"style="position:relative; top:0px; left:0px; overflow:scroll; width:994px;height:450px;"onscroll="funGrilViewScroll(); return false;">
<asp:GridViewID="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">
<FooterStyleBackColor="#CCCCCC"ForeColor="Black"/>
<RowStyleBackColor="#E7E7FF"ForeColor="Black"Font-Size="Small"/>
<HeaderStyleHorizontalAlign="Center"BackColor="#000084"BorderColor="White"BorderWidth="1px"BorderStyle="Solid"Font-Bold="True"ForeColor="White"/>
</asp:GridView>
</div>

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics