扩展GridView控件(五)——固定表头、指定行或指定列
介绍 平时使用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
>