GridView既强大又好用。为了让它更强大、更好用,我们来写一个继承自GridView的控件。
[索引页]
[源码下载]
扩展GridView控件(5) - 固定指定行、指定列
作者:
webabcd
/*
正式版的实现 开始
*/
介绍
扩展GridView控件:
固定指定行、指定列,根据RowType固定行,根据RowState固定行
使用方法(设置FixRowColumn复合属性):
FixRowType - 需要固定的行的RowType(用逗号“,”分隔)
FixRowState - 需要固定的行的RowState(用逗号“,”分隔)
FixRows - 需要固定的行的索引(用逗号“,”分隔)
FixColumns - 需要固定的列的索引(用逗号“,”分隔)
TableWidth - 表格的宽度
TableHeight - 表格的高度
关键代码
css
/**/
/*固定行*/
.yy_sgv_fixRow

{
}
{
position: relative; top: expression(this.offsetParent.scrollTop - 1);
}

/**/
/*固定列*/
.yy_sgv_fixCol

{
}
{
position: relative; left: expression(this.offsetParent.scrollLeft - 1);
}

/**/
/*高优先级的固定*/
.yy_sgv_fixHigh

{
}
{
z-index: 9999;
}

/**/
/*低优先级的固定*/
.yy_sgv_fixLow

{
}
{
z-index: 1000;
}
c#
using
System;
using
System.Collections.Generic;
using
System.Text;

using
System.Web.UI.WebControls;
using
System.Web.UI;

namespace
YYControls.SmartGridViewFunction

{

/**//// <summary>
/// 扩展功能:固定指定行、指定列
/// </summary>
public class FixRowColumnFunction : ExtendFunction

{

/**//// <summary>
/// 构造函数
/// </summary>
public FixRowColumnFunction()
: base()

{

}


/**//// <summary>
/// 构造函数
/// </summary>
/// <param name="sgv">SmartGridView对象</param>
public FixRowColumnFunction(SmartGridView sgv)
: base(sgv)

{

}


/**//// <summary>
/// 扩展功能的实现
/// </summary>
protected override void Execute()

{
this._sgv.RowDataBoundCell += new SmartGridView.RowDataBoundCellHandler(_sgv_RowDataBoundCell);
this._sgv.RenderBegin += new SmartGridView.RenderBeginHandler(_sgv_RenderBegin);
this._sgv.RenderEnd += new SmartGridView.RenderEndHandler(_sgv_RenderEnd);
}


/**//// <summary>
/// SmartGridView的RowDataBoundCell事件
/// </summary>
/// <param name="sender"></param>
/// <param name="gvtc"></param>
void _sgv_RowDataBoundCell(object sender, GridViewTableCell gvtc)

{
TableCell tc = gvtc.TableCell;
GridViewRow gvr = (GridViewRow)tc.Parent;

int i = 0; // 0-既不固定行也不固定列;1-固定行或固定列;2-既固定行也固定列
// 固定行
if
(
(
!String.IsNullOrEmpty(this._sgv.FixRowColumn.FixRows)
&&

Array.Exists(this._sgv.FixRowColumn.FixRows.Split(','), delegate(string s)
{ return s == gvr.RowIndex.ToString(); })
)
||
(
!String.IsNullOrEmpty(this._sgv.FixRowColumn.FixRowType)
&&

Array.Exists(this._sgv.FixRowColumn.FixRowType.Split(','), delegate(string s)
{ return s == gvr.RowType.ToString(); })
)
||
(
!String.IsNullOrEmpty(this._sgv.FixRowColumn.FixRowState)
&&

Array.Exists(this._sgv.FixRowColumn.FixRowState.Split(','), delegate(string s)
{ return s == gvr.RowState.ToString(); })
)
)

{
i++;
Helper.Common.SetAttribute(tc, "class", "yy_sgv_fixRow", AttributeValuePosition.Last, ' ');
}
// 固定列
if
(
!String.IsNullOrEmpty(this._sgv.FixRowColumn.FixColumns)
&&

Array.Exists(this._sgv.FixRowColumn.FixColumns.Split(','), delegate(string s)
{ return s == gvtc.ColumnIndex.ToString(); })
)

{
i++;
Helper.Common.SetAttribute(tc, "class", "yy_sgv_fixCol", AttributeValuePosition.Last, ' ');
}

// 低等级的z-index
if (i == 1)

{
Helper.Common.SetAttribute(tc, "class", "yy_sgv_fixLow", AttributeValuePosition.Last, ' ');
}
// 高等级的z-index
else if (i == 2)

{
Helper.Common.SetAttribute(tc, "class", "yy_sgv_fixHigh", AttributeValuePosition.Last, ' ');
}
}


/**//// <summary>
/// RenderBegin
/// </summary>
/// <param name="sender"></param>
/// <param name="writer"></param>
void _sgv_RenderBegin(object sender, HtmlTextWriter writer)

{
writer.AddStyleAttribute(HtmlTextWriterStyle.Overflow, "auto");
writer.AddStyleAttribute(HtmlTextWriterStyle.Position, "relative");
writer.AddStyleAttribute(HtmlTextWriterStyle.Width, String.IsNullOrEmpty(this._sgv.FixRowColumn.TableWidth) ? "100%" : this._sgv.FixRowColumn.TableWidth);
writer.AddStyleAttribute(HtmlTextWriterStyle.Height, String.IsNullOrEmpty(this._sgv.FixRowColumn.TableHeight) ? "100%" : this._sgv.FixRowColumn.TableHeight);
writer.RenderBeginTag(HtmlTextWriterTag.Div);
}


/**//// <summary>
/// RenderEnd
/// </summary>
/// <param name="sender"></param>
/// <param name="writer"></param>
void _sgv_RenderEnd(object sender, HtmlTextWriter writer)

{
writer.RenderEndTag();
}
}
}
/*
正式版的实现 结束
*/
/*
测试版的实现 开始
*/
介绍
平时使用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("onscroll=\"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
>
/*
测试版的实现 结束
*/
OK
[源码下载]