介绍平时使用GridView的时候会有固定表头、指定行或指定列的需求,就像Excel冻结行、列那样。其实我们可以用CSS来搞定。扩展一下GridView,通过设置几个属性来达到这样的功能。控件开发1、新建一个继承自GridView的类,另外为了保持滚动条状态,还要继承IPostBackDataHandler接口
/**/ /// <summary> /// 继承自GridView /// </summary> [ToolboxData( @" <{0}:SmartGridView runat='server'></{0}:SmartGridView> " )] public class SmartGridView : GridView, IPostBackDataHandler { }2、新建一个FixRowCol类,有七个属性
using System; using System.Collections.Generic; using System.Text; using System.ComponentModel; namespace YYControls.SmartGridView { /**//// <summary> /// 固定表头、指定行或指定列的实体类 /// </summary> [TypeConverter(typeof(ExpandableObjectConverter))] public class FixRowCol { private bool _isFixHeader; /**//// <summary> /// 固定表头否? /// </summary> [Description("固定表头否?"), Category("扩展"), DefaultValue(false), NotifyParentProperty(true)] public virtual bool IsFixHeader { get { return _isFixHeader; } set { _isFixHeader = value; } } private bool _isFixPager; /**//// <summary> /// 固定分页行否? /// </summary> [Description("固定分页行否?"), Category("扩展"), DefaultValue(false), NotifyParentProperty(true)] public virtual bool IsFixPager { get { return _isFixPager; } set { _isFixPager = value; } } private string _fixRowIndices; /**//// <summary> /// 需要固定的行的索引(用逗号“,”分隔) /// </summary> [Description("需要固定的行的索引(用逗号“,”分隔)"), Category("扩展"), NotifyParentProperty(true)] public virtual string FixRowIndices { get { return _fixRowIndices; } set { _fixRowIndices = value; } } private string _fixColumnIndices; /**//// <summary> /// 需要固定的列的索引(用逗号“,”分隔) /// </summary> [Description("需要固定的列的索引(用逗号“,”分隔)"), Category("扩展"), NotifyParentProperty(true)] public virtual string FixColumnIndices { get { return _fixColumnIndices; } set { _fixColumnIndices = value; } } private System.Web.UI.WebControls.Unit _tableWidth; /**//// <summary> /// 表格的宽度 /// </summary> [Description("表格的宽度"), Category("扩展"), NotifyParentProperty(true)] public System.Web.UI.WebControls.Unit TableWidth { get { return _tableWidth; } set { _tableWidth = value; } } private System.Web.UI.WebControls.Unit _tableHeight; /**//// <summary> /// 表格的高度 /// </summary> [Description("表格的高度"), Category("扩展"), NotifyParentProperty(true)] public System.Web.UI.WebControls.Unit TableHeight { get { return _tableHeight; } set { _tableHeight = value; } } private bool _enableScrollState; /**//// <summary> /// 是否保持滚动条的状态 /// </summary> [Description("是否保持滚动条的状态"), Category("扩展"), DefaultValue(false), NotifyParentProperty(true)] public bool EnableScrollState { get { return _enableScrollState; } set { _enableScrollState = value; } } /**//// <summary> /// ToString(); /// </summary> /// <returns></returns> public override string ToString() { return "FixRowCol"; } }}3、在继承自GridView的类中加一个复杂对象属性,该复杂对象就是第2步创建的那个FixRowCol
private FixRowCol _fixRowCol; /**/ /// <summary> /// 固定表头、指定行或指定列 /// </summary> [ Description( " 固定表头、指定行或指定列 " ), Category( " 扩展 " ), DefaultValue( "" ), DesignerSerializationVisibility(DesignerSerializationVisibility.Content), PersistenceMode(PersistenceMode.InnerProperty) ] public virtual FixRowCol FixRowCol { get { if (_fixRowCol == null) { _fixRowCol = new FixRowCol(); } return _fixRowCol; } }4、重写OnRowDataBound以设置每个单元格的样式,从而实现固定表头、指定行或指定列的功能。
/**/ /// <summary> /// OnRowDataBound /// </summary> /// <param name="e"></param> protected override void OnRowDataBound(GridViewRowEventArgs e) { if (e.Row.RowType == DataControlRowType.Pager) { if (FixRowCol.IsFixPager) { if (this.PagerSettings.Position == PagerPosition.Top || (this.PagerSettings.Position == PagerPosition.TopAndBottom && _isTopPager)) { // TopPager固定行和列 e.Row.Cells[0].Attributes.Add("style", "z-index:999; position: relative; top: expression(this.offsetParent.scrollTop); left: expression(this.offsetParent.scrollLeft);"); // 现在是TopPager,之后就是BottomPager了,所以设置_isTopPager为false _isTopPager = false; } else if (this.PagerSettings.Position == PagerPosition.TopAndBottom && !_isTopPager) { // BottomPager只固定列 e.Row.Cells[0].Attributes.Add("style", "z-index:999; position: relative; left: expression(this.offsetParent.scrollLeft);"); // 现在是BottomPager,之后就是TopPager了,所以设置_isTopPager为true _isTopPager = true; } } } if (e.Row.RowType == DataControlRowType.DataRow || e.Row.RowType == DataControlRowType.Header) { // 给每一个指定固定的列的单元格加上css属性 if (!String.IsNullOrEmpty(FixRowCol.FixColumnIndices)) { // 列索引 foreach (string s in FixRowCol.FixColumnIndices.Split(',')) { int i; if (!Int32.TryParse(s, out i)) throw new ArgumentException("FixColumnIndices", "含有非整形的字符"); if (i > e.Row.Cells.Count) throw new ArgumentOutOfRangeException("FixColumnIndices", "溢出"); e.Row.Cells[i].Attributes.Add("style", "position: relative; left: expression(this.offsetParent.scrollLeft);"); } } bool isFixRow = false; // 当前行是否固定 if (FixRowCol.IsFixHeader && e.Row.RowType == DataControlRowType.Header) { isFixRow = true; } if (!String.IsNullOrEmpty(FixRowCol.FixRowIndices) && e.Row.RowType == DataControlRowType.DataRow) { // 行索引 foreach (string s in FixRowCol.FixRowIndices.Split(',')) { int i; if (!Int32.TryParse(s, out i)) throw new ArgumentException("FixRowIndices", "含有非整形的字符"); if (i > e.Row.Cells.Count) throw new ArgumentOutOfRangeException("FixRowIndices", "溢出"); if (i == e.Row.RowIndex) { isFixRow = true; break; } } } // 固定该行 if (isFixRow) { // 该行的每一个单元格 for (int j = 0; j < e.Row.Cells.Count; j++) { // 该单元格不属于固定列 if (String.IsNullOrEmpty(e.Row.Cells[j].Attributes["style"]) || e.Row.Cells[j].Attributes["style"].IndexOf("position: relative;") == -1) { e.Row.Cells[j].Attributes.Add("style", " position: relative; top: expression(this.offsetParent.scrollTop);"); } // 该单元格属于固定列 else { e.Row.Cells[j].Attributes.Add("style", e.Row.Cells[j].Attributes["style"] + "top: expression(this.offsetParent.scrollTop); z-index: 666;"); } } } } base.OnRowDataBound(e); }5、增加两个私有变量
/**/ /// <summary> /// 如果固定行、列的话 滚动条的x位置 /// </summary> private int _yy_SmartGridView_x; /**/ /// <summary> /// 如果固定行、列的话 滚动条的y位置 /// </summary> private int _yy_SmartGridView_y;6、重写GridView的OnPreRender方法,用于注册两个HiddenField,以及注册设置GridView的滚动条的位置的javascript代码
/**/ /// <summary> /// OnPreRender /// </summary> /// <param name="e"></param> protected override void OnPreRender(EventArgs e) { if (FixRowCol.EnableScrollState) { // 滚动条x位置 Page.ClientScript.RegisterHiddenField("yy_SmartGridView_x", _yy_SmartGridView_x.ToString()); // 滚动条y位置 Page.ClientScript.RegisterHiddenField("yy_SmartGridView_y", _yy_SmartGridView_y.ToString()); // 设置GridView的滚动条的位置 Page.ClientScript.RegisterStartupScript( this.GetType(), "jsSetScroll", "<script type=/"text/javascript/">document.getElementById('yy_ScrollDiv').scrollLeft=" + _yy_SmartGridView_x + ";document.getElementById('yy_ScrollDiv').scrollTop=" + _yy_SmartGridView_y + ";</script>" ); // 将控件注册为要求在页回发至服务器时进行回发处理的控件 if (Page != null) Page.RegisterRequiresPostBack(this); } base.OnPreRender(e); }7、重写GridView的Render方法,将GridView用一个div包裹起来。
/**/ /// <summary> /// Render /// </summary> /// <param name="writer"></param> protected override void Render(HtmlTextWriter writer) { // 给GridView一个容器 <div> if (!FixRowCol.TableWidth.IsEmpty || !FixRowCol.TableHeight.IsEmpty) { if (FixRowCol.TableWidth.IsEmpty) FixRowCol.TableWidth = new Unit(100, UnitType.Percentage); if (FixRowCol.TableHeight.IsEmpty) FixRowCol.TableHeight = new Unit(100, UnitType.Percentage); writer.Write("<div id='yy_ScrollDiv' style=/"overflow: auto; width: " + FixRowCol.TableWidth.ToString() + "; height: " + FixRowCol.TableHeight.ToString() + "; position: relative;/" "); // 如果保持滚动条的状态的话,用隐藏字段记录滚动条的位置 if (FixRowCol.EnableScrollState) { writer.Write("οnscrοll=/"document.getElementById('yy_SmartGridView_x').value = this.scrollLeft; document.getElementById('yy_SmartGridView_y').value = this.scrollTop;/">"); } else { writer.Write(">"); } } base.Render(writer); // </div> 结束 if (!FixRowCol.TableWidth.IsEmpty || !FixRowCol.TableHeight.IsEmpty) { writer.Write("</div>"); } }8、获取存储了滚条位置信息的HiddenField的值
void IPostBackDataHandler.RaisePostDataChangedEvent() { } bool IPostBackDataHandler.LoadPostData( string postDataKey, NameValueCollection postCollection) { // 获取两个保存了 固定行、列后 的GridView滚动条的位置信息 _yy_SmartGridView_x = String.IsNullOrEmpty(postCollection["yy_SmartGridView_x"]) ? 0 : Convert.ToInt32(postCollection["yy_SmartGridView_x"]); _yy_SmartGridView_y = String.IsNullOrEmpty(postCollection["yy_SmartGridView_y"]) ? 0 : Convert.ToInt32(postCollection["yy_SmartGridView_y"]); return false; }控件使用添加这个控件到工具箱里,然后拖拽到webform上,设置其FixRowCol下的7个属性即可。IsFixHeader是固定表头否?;IsFixPager是固定分页行否?;FixRowIndices是需要固定的行的索引(用逗号“,”分隔);FixColumnIndices是需要固定的列的索引(用逗号“,”分隔);TableWidth是表格的宽度;TableHeight是表格的高度;EnableScrollState为是否保持滚动条的状态ObjData.cs
using System; using System.Data; using System.Configuration; using System.Web; using System.Web.Security; using System.Web.UI; using System.Web.UI.WebControls; using System.Web.UI.WebControls.WebParts; using System.Web.UI.HtmlControls; using System.ComponentModel; /**/ /// <summary>/// OjbData 的摘要说明/// </summary> public class OjbData { public OjbData() { // // TODO: 在此处添加构造函数逻辑 // } [DataObjectMethod(DataObjectMethodType.Select, true)] public DataTable Select() { DataTable dt = new DataTable(); dt.Columns.Add("no", typeof(string)); dt.Columns.Add("name", typeof(string)); for (int i = 0; i < 30; i++) { DataRow dr = dt.NewRow(); dr[0] = "no" + i.ToString().PadLeft(2, '0'); dr[1] = "name" + i.ToString().PadLeft(2, '0'); dt.Rows.Add(dr); } return dt; }}Default.aspx
<% @ Page Language="C#" AutoEventWireup="true" CodeFile="Default.aspx.cs" Inherits="_Default" %> <! 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 id ="Head1" runat ="server" > < title > SmartGridView测试 </ title > </ head > < body > < form id ="form1" runat ="server" > < yyc:SmartGridView ID ="SmartGridView1" runat ="server" AutoGenerateColumns ="False" DataSourceID ="ObjectDataSource1" Width ="1000px" > < Columns > < asp:BoundField DataField ="no" HeaderText ="序号" SortExpression ="no" /> < asp:BoundField DataField ="name" HeaderText ="名称" SortExpression ="name" /> < asp:BoundField DataField ="no" HeaderText ="序号" SortExpression ="no" /> < asp:BoundField DataField ="name" HeaderText ="名称" SortExpression ="name" /> < asp:BoundField DataField ="no" HeaderText ="序号" SortExpression ="no" /> < asp:BoundField DataField ="name" HeaderText ="名称" SortExpression ="name" /> < asp:BoundField DataField ="no" HeaderText ="序号" SortExpression ="no" /> < asp:BoundField DataField ="name" HeaderText ="名称" SortExpression ="name" /> < asp:BoundField DataField ="no" HeaderText ="序号" SortExpression ="no" /> < asp:BoundField DataField ="name" HeaderText ="名称" SortExpression ="name" /> </ Columns > < FixRowCol FixColumnIndices ="0,1" FixRowIndices ="0" IsFixHeader ="True" TableHeight ="300px" TableWidth ="300px" EnableScrollState ="true" /> </ yyc:SmartGridView > < asp:ObjectDataSource ID ="ObjectDataSource1" runat ="server" SelectMethod ="Select" TypeName ="OjbData" ></ asp:ObjectDataSource > </ form > </ body > </ html >