一个ASP.NET DataGrid自定义控件,只需更改单个属性值即可冻结标题、行、列






4.64/5 (22投票s)
一个ASP.NET DataGrid自定义控件,允许通过更改单个属性值来冻结DataGrid的标题、列和行。它继承自.NET DataGrid,因此DataGrid的所有功能仍然可用。
引言
这是一个自定义控件,通过更改单个属性值,可以轻松地在ASP.NET DataGrid
中实现冻结标题、行和列。它继承自System.Web.UI.WebControls.DataGrid
,因此DataGrid
的所有功能仍然可用。
必备组件
您的系统上需要安装.NET Framework 1.1 Service Pack 1,因为标题是以<TH>
标签渲染的。
属性
FreezeHeader
:是否冻结标题。默认值:false
。FreezeRows
:指定要冻结的行数。这也会自动冻结标题。默认值:0
。FreezeColumns
:指定要在网格中冻结的列数。默认值:0
。AddEmptyHeaders
:这将添加空的<tr><th></th>..</tr>
行;当您希望在DataGrid
的所有页面中保留一个静态的标题行时,这非常有用。EmptyHeaderClass
:当AddEmptyHeaders
非零时,在此指定空行的CSS类。GridHeight
:网格高度;这实际上是<DIV>
标签的高度,而不是<table>
的高度(可以通过Height
设置)。当FreezeHeader
为true
或FreezeRows
非零时,请始终在此提供一个值。GridWidth
:网格宽度;这实际上是<DIV>
标签的宽度,而不是<table>
的宽度(可以通过Width
设置)。当FreezeColumns
非零时,请始终在此提供一个值。
Using the Code
仅冻结DataGrid
中的标题
<Tittle:CustomDataGrid GridHeight="150" FreezeHeader=true
..
/>
以上内容将呈现为
如下所示冻结DataGrid
中的表格行。请注意,在这种情况下,标题会自动冻结。
<Tittle:CustomDataGrid GridHeight="110" FreezeRows=1
..
/>
以上内容将呈现为
冻结DataGrid
中的表格列
<Tittle:CustomDataGrid GridWidth="250" FreezeColumns=2
..
/>
以上内容将呈现为
添加冻结的空标题
<Tittle:CustomDataGrid GridHeight="209"
AddEmptyHeaders=1 EmptyHeaderClass="greyed"
FreezeHeader="true"
..
/>
以上内容将呈现为
技术实现洞察
using System;
using System.Web.UI;
using System.ComponentModel;
using System.Web.UI.WebControls;
using System.Text.RegularExpressions;
using System.Collections.Specialized;
using System.Reflection;
[assembly: TagPrefix ("Tittle.Controls" , "Tittle") ]
namespace Tittle.Controls
{
/// <summary>
/// A Custom DataGrid which Allow freezing of Headers/Rwos/Columns
/// https://codeproject.org.cn/script/articles/list_articles.asp?userid=1654009
/// </summary>
public class CustomDataGrid : DataGrid
{
#region Private Variables
private bool freezeHeader = false;
private int addEmptyHeaders = 0;
private int freezeRows = 0;
private string emptyHeaderClass = "";
private int freezeColumns = 0;
private Unit gridHeight = Unit.Empty;
private Unit gridWidth = Unit.Empty;
#endregion
#region Constructors
/// <summary>
/// default initialization with datagrid properties
/// </summary>
public CustomDataGrid(): base()
{
//set default appearence style
this.HeaderStyle.CssClass = "GridHeader";
this.FooterStyle.CssClass = "GridHeader";
this.ItemStyle.CssClass = "GridNormal";
this.AlternatingItemStyle.CssClass = "GridAlternate";
this.CssClass = "Grid";
this.BorderColor =
System.Drawing.Color.FromArgb(47, 33, 98); //"#2f2162"
this.BorderWidth = Unit.Parse("1");
this.GridLines = GridLines.Both;
this.CellPadding = 2;
this.CellSpacing = 0;
//paging
this.PagerStyle.Mode = PagerMode.NumericPages;
this.PagerStyle.Position = PagerPosition.Bottom;
this.PagerStyle.HorizontalAlign = HorizontalAlign.Center;
this.PagerStyle.ForeColor =
System.Drawing.Color.FromArgb(0, 0, 255); //"#2f2162"
this.PageSize = 10;
}
#endregion
#region Exposed Attributes
/// <summary>
/// Freeze Header : True/False
///
/// Default: False
/// </summary>
<Browsable(true),
Category("Freeze"),
DefaultValue("false"),
Description ("Freeze Header.") >
public bool FreezeHeader
{
get { return freezeHeader; }
set
{
freezeHeader = value;
if ( freezeHeader == true )
{
//this attribute requires .Net Framework 1.1
//service pack 1 installed
//on system, it generates headers
//in <th> tags instead of <td> tags.
this.UseAccessibleHeader = true;
//If user hasnt set height property set a default here.
if ( this.gridHeight == Unit.Empty )
this.gridHeight = 800;
}
}
}
/// <summary>
/// Freeze Data Rows : Specify No. of Rows
/// to Freeze from top excluding the header.
///
/// Default: 0
/// </summary>
<Browsable(true),
Category("Freeze"),
DefaultValue("0"),
Description ("Freeze Data Rows.") >
public int FreezeRows
{
get { return freezeRows; }
set
{
freezeRows = value;
if ( freezeRows >= 1 )
{
this.FreezeHeader = true;
}
}
}
/// <summary>
/// Specify here how many columns you want to freeze in the grid,
/// freeze columns from the left.
///
/// Default: 0
/// </summary>
<Browsable(true),
Category("Freeze"),
DefaultValue("0"),
Description ("Specify no. of columns to freeze
in the grid, it freeze columns from the left.") >
public int FreezeColumns
{
get { return freezeColumns; }
set { freezeColumns = value; }
}
/// <summary>
/// this will add empty <tr><th></th>..</tr> rows
/// the benefit of it is that later we can
/// through client side add/modify any text to individual cell
/// it is done since through client side
/// we can not add more than one row
/// with <th> tags to table.
///
/// It is used when you set FreezeHeader to "true"
/// and wants to have more than one such headers
/// to be freezed but data of all such headers
/// is decided on client side.
///
/// This is useful you want to keep a static
/// row in all of the pages of a datagrid.
/// </summary>
<Browsable(true),
Category("Freeze"),
DefaultValue("0"),
Description ("It will add empty rows.") >
public int AddEmptyHeaders
{
get { return addEmptyHeaders; }
set { addEmptyHeaders = value; }
}
/// <summary>
/// Class of Empty rows <tr><th></th>.. </tr>
/// </summary>
<Browsable(true),
Category("Freeze"),
Description ("Class of empty rows.") >
public string EmptyHeaderClass
{
get { return emptyHeaderClass; }
set { emptyHeaderClass = value; }
}
/// <summary>
/// Grid height, this is actually height of <DIV> tag not of
<table ID="Table1"> which can be set through "Height"
/// </summary>
<Browsable(true),
Category("Freeze"),
Description ("Grid height, this is actually height of
<DIV> tag not of <table ID="Table2">
which can be set through Height.") >
public Unit GridHeight
{
get { return gridHeight; }
set { gridHeight = value; }
}
/// <summary>
/// Grid width, this is actually width of <DIV> tag not of
/// <table ID="Table3"> which can be set through "Width"
/// </summary>
<Browsable(true),
Category("Freeze"),
Description ("Grid width, this is actually width
of <DIV> tag not of <table ID="Table4">
which can be set through Width.") >
public Unit GridWidth
{
get { return gridWidth; }
set { gridWidth = value; }
}
#endregion
#region Overriden events like OnPreRender / Render
/// <summary>
/// Before rendering to screen, check if freezing
/// of column/header/row is required.
/// Add <div> tag before <table ID="Table5"> tag then
/// </summary>
/// <param name="e"></param>
protected override void OnPreRender(EventArgs e)
{
#region DataGrid freezing script
//DataGrid functions/styles etc.
if (!Page.IsClientScriptBlockRegistered("FreezeGridScript") )
{
string freezeGridScript = @"
function FreezeGridColumns(dgTbl, colNo)
{
var tbl = document.getElementById(dgTbl);
for ( var i=0; i<tbl.rows.length; i++)
{
for ( var j=0; j<colNo; j++)
{
tbl.rows[i].cells[j].className = 'locked';
}
}
}
";
Page.RegisterClientScriptBlock("FreezeGridScript",
"<script language="javascript">\n" +
freezeGridScript + "\n</script>");
}
if ( !Page.IsClientScriptBlockRegistered("GridStyle") )
{
string gridStyle = "";
gridStyle = @"
<style TYPE='text/css'>
/* Locks table header */
th
{
/* border-right: 1px solid silver; */
position:relative;
cursor: default;
/*IE5+ only*/
top: expression(this.parentElement.parentElement.
parentElement.parentElement.scrollTop -2);
z-index: 10;
}
TR.GridNormal TH, TR.GridAlternate TH
{
text-align:left;
}
TR.GridHeader TH
{
text-align:center;
}
/* Locks the left column */
td.locked, th.locked
{
/* border-right: 1px solid silver; */
position:relative;
cursor: default;
/*IE5+ only*/
left: expression(this.parentElement.parentElement.
parentElement.parentElement.scrollLeft-2);
}
/* Keeps the header as the top most item.
Important for top left item*/
th.locked {z-index: 99;}
</style>
<style>
/*Overriding Grid Styles*/
.Grid
{
border:0;
background-color: #808080;
}
.GridHeader
{
background-color: #547095;
color:White;
font-weight:bold;
font-family:Tahoma;
text-align:center;
padding : 1px 0px 1px 0px;
font-size:8pt;
}
.GridHeader TD A, TH A
{
text-decoration:none;
color:White;
}
.GridNormal
{
background-color: #FFFFFF;
font-weight: normal;
font: x-small Verdana;
}
.GridAlternate
{
background-color: #EFF8FC;
font-weight: normal;
font: x-small Verdana;
}
.GridSelected
{
background-color: #FFC082;
font-weight: normal;
font: x-small Verdana;
}
.GridPager
{
background-color : White;
font-size : x-small;
}
</style>
";
Page.RegisterClientScriptBlock("GridStyle",gridStyle);
}
//Trim space from the div and datagrid table.
string gridOnLoad = "";
gridOnLoad = @"
// trims the height of the div surrounding the results table
// so that if not a lot of results are returned,
// you do not have a lot of blank space between the results and
// the last buttons on the page
var divTbl = document.getElementById('div_"+this.ID + @"');
var resultsTable = divTbl.children[0];
if ( typeof(resultsTable) != 'undefined' )
{
//alert( 'div ' + divTbl.offsetHeight + ' results: '
// + resultsTable.offsetHeight );
if( divTbl.offsetHeight + 3 > resultsTable.offsetHeight )
divTbl.style.height = resultsTable.offsetHeight + 50;
}
";
//If <div> generated then only
if (freezeColumns >= 1 || freezeHeader == true)
Page.RegisterStartupScript("GridOnLoad",
"<script language="javascript">\n" +
gridOnLoad + "\n</script>");
#endregion
base.OnPreRender(e);
}
/// <summary>
/// Render datagridservercontrol
/// </summary>
/// <param name="output"></param>
protected override void Render(HtmlTextWriter output)
{
if ( freezeRows >= 1 || addEmptyHeaders >= 1 ||
freezeColumns >= 1 || freezeHeader == true )
{
#region Only execute when need to any of this:
Freeze Header, Row, Add Empty Header,
Freeze Columns
//Get the output string rendered.
System.Text.StringBuilder sb = new
System.Text.StringBuilder();
HtmlTextWriter writer = new HtmlTextWriter(new
System.IO.StringWriter(sb));
base.Render(writer);
//the output has been retrieved
//in the stringbuilder AND do the modification here
string datgridString = sb.ToString();
//FREEZE MORE ROWS OTHER THAN JUST HEADER
if ( freezeRows >= 1 )
{
int cnt = 0;
for ( int i=0; i<=freezeRows; i++)
{
cnt = datgridString.IndexOf("</tr>",cnt);
if ( i < freezeRows)
cnt++;
}
if ( cnt != -1 )
{
string firstpart,secondpart;
firstpart = datgridString.Substring(0,cnt);
secondpart = datgridString.Substring(cnt);
firstpart = Regex.Replace(firstpart, @"<td",
@"<th class='innerBorder' " );
firstpart = Regex.Replace(firstpart,
@"</td>", @"</th>");
datgridString = firstpart + secondpart;
}
}
//ADD EMPTY HEADERS. <TH>
//This all done as through client side JAVASCRIPT
//you can not add more than one row TH
//tag to existing TABLE.
if ( addEmptyHeaders >= 1 )
{
string rowData="";
for ( int i=1; i<=addEmptyHeaders; i++)
{
rowData +="<TR class='" + EmptyHeaderClass +
"' >";
for ( int j=0; j<this.Columns.Count; j++)
{
string clsname = "";
if ( j == 0 )
clsname="leftBorder";
if ( j == this.Columns.Count-1 )
clsname="rightBorder";
if ( j > 0 && j < this.Columns.Count )
clsname="innerBorder";
rowData += "<TH class='" + clsname +
"' > </TH>";
}
rowData += "</TR>";
}
if ( addEmptyHeaders >= 1)
{
int headerEnd =
datgridString.IndexOf("</tr>",0);
string prePart =
datgridString.Substring(0,headerEnd+5);
string postPart =
datgridString.Substring(headerEnd+5);
datgridString = prePart + rowData + postPart;
}
}
if ( freezeColumns >= 1 )
{
if ( this.gridWidth == Unit.Empty )
this.gridWidth = 800;
string freezeGridColumn="FreezeGridColumns('" +
this.ID + "',"+freezeColumns.ToString()+");";
Page.RegisterStartupScript("freezeGridColumn",
"<script language="javascript">\n" +
freezeGridColumn + "\n</script>" );
}
if ( freezeColumns >= 1 || freezeHeader == true )
{
string divstring;
divstring = "<div id='div_" + this.ID +
"' style='";
if ( freezeHeader == true )
divstring += " HEIGHT:"+this.gridHeight+"; ";
if ( freezeColumns >= 1 )
divstring += " WIDTH:"+this.gridWidth+"; ";
datgridString = divstring + " OVERFLOW:auto; ' >"
+ datgridString + "</div>";
}
output.Write(datgridString.ToString());
#endregion
}
else
{
base.Render(output);
}
}
#endregion
}
}
<%@ Register TagPrefix="Tittle"
Namespace="Tittle.Controls" Assembly="Controls" %>
<Tittle:CustomDataGrid id="dgTittle"
FreezeHeader=true runat="server"
AutoGenerateColumns=False GridHeight="150"
>
<Columns>
<asp:TemplateColumn HeaderText="User Name" >
<ITEMTEMPLATE>
<asp:Label Runat="server" ID="lblName" Width="100"
Text='<%# DataBinder.Eval(Container, "DataItem.Name") %>' />
</ITEMTEMPLATE>
</asp:TemplateColumn>
<asp:TemplateColumn HeaderText="Grade"
HeaderStyle-HorizontalAlign=Right >
<ITEMTEMPLATE>
<asp:TextBox Runat="server" Width="100" ID="txtAge"
Text='<%# DataBinder.Eval(Container, "DataItem.Age") %>' />
</ITEMTEMPLATE>
</asp:TemplateColumn>
</Columns>
</Tittle:CustomDataGrid>
<%@ Register TagPrefix="Tittle"
Namespace="Tittle.Controls" Assembly="Controls" %>
<Tittle:CustomDataGrid id="dgTittle2" FreezeHeader=true
runat="server" AutoGenerateColumns=True
GridHeight="110"
FreezeRows=1
> />
<%@ Register TagPrefix="Tittle"
Namespace="Tittle.Controls" Assembly="Controls" %>
<body onload="AddEmptyHeaderDataLoad()">
..
<style>
.greyed
{
background-color:#c0c0c0;
}
</style>
<script language="javascript">
function AddEmptyHeaderDataLoad()
{
if ( typeof(dgTittle4) != 'undefined' )
{
dgTittle4.rows[1].cells[0].innerText = 'Tittle Joseph';
dgTittle4.rows[1].cells[1].innerText = '29';
dgTittle4.rows[1].cells[2].innerText =
'5/67 Rachna Vaishali Ghaziabad';
}
}
</script>
<Tittle:CustomDataGrid id="dgTittle4" GridHeight="209" AutoGenerateColumns=false
AddEmptyHeaders=1 EmptyHeaderClass="greyed" PageSize=7
FreezeHeader=true PagerStyle-Mode=NumericPages
AllowPaging=True
OnPageIndexChanged="dgTittle4_PageIndexChanged"
>
<Columns>
<asp:TemplateColumn HeaderText="Name" >
<ITEMTEMPLATE>
<asp:Label Runat="server" ID="Label3" Width="100"
Text='<%# DataBinder.Eval(Container, "DataItem.Name") %>' />
</ITEMTEMPLATE>
</asp:TemplateColumn>
<asp:TemplateColumn HeaderText="Grade"
HeaderStyle-HorizontalAlign=Right >
<ITEMTEMPLATE>
<asp:Label Runat="server" ID="Label4"
Text='<%# DataBinder.Eval(Container,
"DataItem.Age") %>' />
</ITEMTEMPLATE>
</asp:TemplateColumn>
<asp:TemplateColumn HeaderText="Address"
HeaderStyle-HorizontalAlign=Right
ItemStyle-Wrap=False >
<ITEMTEMPLATE>
<asp:Label Runat="server" ID="Label5"
Text='<%# DataBinder.Eval(Container,
"DataItem.Address") %>' />
</ITEMTEMPLATE>
</asp:TemplateColumn>
</Columns>
</Tittle:CustomDataGrid>
..
</body>
常见问题解答
在使用这里提供的自定义控件后,我是否需要更改代码隐藏文件或包含其他一组行或文件来冻结DataGrid的列/行/标题?
否。只需使用提供的自定义控件并设置公开的属性即可。
标题/列是透明的,滚动时可以看到数据。
冻结标题/列时,所有冻结的列/行的backcolor
是必需的,所以请提供背景颜色,这样透明度问题就会消失。
它支持跨浏览器吗?
我不确定,我只在IE中测试过,在那里工作正常。
此代码将在.NET 2.0或更高版本中运行吗?
我不知道。我没有.NET 2.0可以测试。
我可以一起冻结任意数量的列和行吗?
是的。没有问题。
在哪里可以使用属性“FreezeRows”?
假设您想比较一个获得最高分数的学生与其他学生的成绩;那么您可以将排名最高的用户放在网格的顶部,然后设置FreezeRows=1
,这将冻结顶部的学生,然后将其与其他学生进行比较。
什么是“AddEmptyHeaders”,我无法判断将在哪里使用它?
假设您想将客户[A]的销售额与其他客户的销售额(成千上万条记录)进行比较。您可以将客户[A]的信息冻结在顶行,并将其与DataGrid所有页面上的其他客户进行比较。
我认为可以使用“FreezeRows”而不是“AddEmptyHeaders”?
两者都有各自的优点。FreezeRows
总是从顶部冻结行,所以下一页您将看不到相同的记录被冻结,因此FreezeRows
最适合在所有记录都在同一页上时使用,因为您不会在所有页面上看到相同的记录。而当您使用“AddEmptyHeaders
”时,您会在页面加载时在客户端填充顶行数据,这在DataGrid的所有页面上都保持不变。所以结论是,如果DataGrid没有分页,请使用“FreezeRows
”(冻结的行将从服务器端填充)。或者使用“AddEmptyHeaders
”(冻结的行不需要在客户端填充)。
我收到一个错误。“UseAccessibleHeader”属性未找到
您的系统必须安装.NET Framework 1.1 SP1。请从MSDN下载。
资源
有几个网站帮助我实现了这一点。每次我想在一个新的DataGrid
中冻结标题/列时,我都必须重新编写所有复杂的逻辑。为了缓解这个问题,我在自定义DataGrid
中实现了所有逻辑,并公开了属性,可以轻松地冻结标题和列,而无需了解实际的实现和背后的逻辑。
- 我从中获取冻结标题逻辑的网站:https://codeproject.org.cn/aspnet/FreezePaneDatagrid.asp
其他类似的冻结标题相关网站
- http://slingfive.com/pages/code/scrollTable/
- ASP.NET DataGrid中的固定标题
- ScrollingGrid:一个跨浏览器冻结标题双向滚动DataGrid
结论
如果您通过任何方式从这个代码中受益,我将非常感兴趣。请不要犹豫或懒惰地留下您的评论,告诉我您对这个提交的看法以及它对您有多大帮助。如果您在使用该控件的代码中保留此页面的URL,我将不胜感激。
历史
- 2006年2月20日:创建
- 2006年2月24日:修改