这篇文章本来在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">
.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 )
{
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 ="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 >