让GridView具有和Repeater一样灵活的数据展现能力 --提高用户体验
GridView是一个功能强大的控件, 能够无缝的与objectDataSourc等数据源控件结合起来. 方便进行自定义分页.同时管理数据功能也很强大,拥有众多的事件. 在使用的过程中发现GridView最大的不足就是在数据的展现方面. 不能方便的定制展现的效果.
比如要将10条记录分2行5列进行显示就无能为力了(也许是我不知道). 但通过DataList和Repeater就可以实现这种效果.
本文就是介绍如何使GridView具有更灵活的数据展示方式.仿照Repeater的模版列实现.
目标:使GridView就有灵活的数据展现能力.
如以下的效果:
首先了解下GridView中的展现数据的原理
1.在CreateChildControls((IEnumerable dataSource, bool dataBinding))方法中生成所有的行和列 如数据列, EmptyData列, HeaderRow,FooterRow,PagerRow等 .
2.在Render方法中将绑定好数据的列进行输出 .
GridView是由行(Rows)构成, 然后每行有多列(Cells),每列里面有多个控件. 从控件开始依次输出html.
我们可以发现GridView输出的每一列都是都是有<td></td>包围起来.这样就限制了每一行的数据只能是一条记录中的.没法实现在一行中展现多条记录.
为什么每一列都是由<td></td>包围? 如果能去掉这个标签就好了. 通过reflector查看源码发现GridView中的列都是继承自TableCell,而TableCell的默认构造函数就是传入HtmlTextWriterTag.Td, 本来就有带参数的构造函数,可以传入自定义的Tag, 可惜微软把这个设成了internal, 不能进行调用. TableCell在输出html代码时就会在周围加上<td>标签. 看来此路不同,只有另想法子.
Asp.net2.0多了一个特性, 可以通过Controls Adapter进行定制控件的输出样式. 在CodePlex上也有一个开源项目: CSS Friendly Control Adapters 能对控件的输出进行定制. 于是想能不能通过Control Adapters来改变GridView的输出呢,只要把<td>去掉就可以了. 但是还是失望了,他并没有改变TableCell的输出, 还是没一列就围着一个<td></td> . 但是我从CSS Friendly Control Adapters上的到了灵感,将RenderContent方法重写可能会实现想要的效果.
然后我就进行RenderContent的重写, 之中尝试的过程就不多说了. 还是遇到了挺多麻烦 . 只有通过折衷的方式实现了.(如果您知道更好的方式 请指点下)
最终的GridView实现的效果仿照Repeater控件,Columns中具有唯一的模板列
, 在模板列中可以包含<headerTemplate></headerTemplate>,<itemtemplate></itemtemplate>,<footerTemplate></footerTemplate>.还能包括EmptyDataTemplate.
示例代码如下:
其中headerTemplate和footerTemplate不管有多少数据行只执行一次,而itemtemplate模版中的内容会根据行的数量进行多次创建.EmptyDataTemplate会在无数据时显示. 这样就和Repeater的模板列相仿了,可以随意定制输出的HTML.
不多说了, 改造后的GridView代码如下:
使用上面定制过的GridView就可以像Repeater使用灵活的模板列来展示数据(仅仅是展示), 如果要用到GridView管理相关的如Edit,Delete Command的特性,还是使用原来的GridView比较好.毕竟管理页面不需要像展示页面那样灵活多变.
用GridView实现这样的效果就不是难事了
示例代码下载

比如要将10条记录分2行5列进行显示就无能为力了(也许是我不知道). 但通过DataList和Repeater就可以实现这种效果.
本文就是介绍如何使GridView具有更灵活的数据展示方式.仿照Repeater的模版列实现.
目标:使GridView就有灵活的数据展现能力.
如以下的效果:

首先了解下GridView中的展现数据的原理
1.在CreateChildControls((IEnumerable dataSource, bool dataBinding))方法中生成所有的行和列 如数据列, EmptyData列, HeaderRow,FooterRow,PagerRow等 .
2.在Render方法中将绑定好数据的列进行输出 .
GridView是由行(Rows)构成, 然后每行有多列(Cells),每列里面有多个控件. 从控件开始依次输出html.
我们可以发现GridView输出的每一列都是都是有<td></td>包围起来.这样就限制了每一行的数据只能是一条记录中的.没法实现在一行中展现多条记录.
为什么每一列都是由<td></td>包围? 如果能去掉这个标签就好了. 通过reflector查看源码发现GridView中的列都是继承自TableCell,而TableCell的默认构造函数就是传入HtmlTextWriterTag.Td, 本来就有带参数的构造函数,可以传入自定义的Tag, 可惜微软把这个设成了internal, 不能进行调用. TableCell在输出html代码时就会在周围加上<td>标签. 看来此路不同,只有另想法子.
Asp.net2.0多了一个特性, 可以通过Controls Adapter进行定制控件的输出样式. 在CodePlex上也有一个开源项目: CSS Friendly Control Adapters 能对控件的输出进行定制. 于是想能不能通过Control Adapters来改变GridView的输出呢,只要把<td>去掉就可以了. 但是还是失望了,他并没有改变TableCell的输出, 还是没一列就围着一个<td></td> . 但是我从CSS Friendly Control Adapters上的到了灵感,将RenderContent方法重写可能会实现想要的效果.
然后我就进行RenderContent的重写, 之中尝试的过程就不多说了. 还是遇到了挺多麻烦 . 只有通过折衷的方式实现了.(如果您知道更好的方式 请指点下)
最终的GridView实现的效果仿照Repeater控件,Columns中具有唯一的模板列
, 在模板列中可以包含<headerTemplate></headerTemplate>,<itemtemplate></itemtemplate>,<footerTemplate></footerTemplate>.还能包括EmptyDataTemplate.
示例代码如下:
<cc1:TemplateGridView ID="AjaxGridView1" AutoGenerateColumns="False" runat="server" BorderWidth="0"
DataSourceID="ObjectDataSource1">
<Columns>
<asp:TemplateField>
<headerTemplate>
<div>
</headerTemplate>
<itemtemplate>
<div><%#Eval("RowNumber") %></div>
</itemtemplate>
<footerTemplate>
</div>
</footerTemplate>
</asp:TemplateField>
</Columns>
<EmptyDataTemplate>
nodata
</EmptyDataTemplate>
</cc1:TemplateGridView>
DataSourceID="ObjectDataSource1">
<Columns>
<asp:TemplateField>
<headerTemplate>
<div>
</headerTemplate>
<itemtemplate>
<div><%#Eval("RowNumber") %></div>
</itemtemplate>
<footerTemplate>
</div>
</footerTemplate>
</asp:TemplateField>
</Columns>
<EmptyDataTemplate>
nodata
</EmptyDataTemplate>
</cc1:TemplateGridView>
其中headerTemplate和footerTemplate不管有多少数据行只执行一次,而itemtemplate模版中的内容会根据行的数量进行多次创建.EmptyDataTemplate会在无数据时显示. 这样就和Repeater的模板列相仿了,可以随意定制输出的HTML.
不多说了, 改造后的GridView代码如下:
using System.Collections;
using System.ComponentModel;
using System.Web.UI;
using System.Web.UI.WebControls;
namespace Jec.CustomControls
{
[DefaultProperty("Text")]
[ToolboxData("<{0}:TemplateGridView runat=server></{0}:TemplateGridView>")]
public class TemplateGridView : GridView, INamingContainer
{
protected override void RenderContents(HtmlTextWriter writer)
{
GridView gridView = this;
writer.Indent++;
//分页在顶部
WritePagerSection(writer, PagerPosition.Top);
ArrayList rows = new ArrayList();
GridViewRowCollection gvrc = null;
//输出columns
WriteRows(writer, gridView, gridView.Rows);
//分页在底部
WritePagerSection(writer, PagerPosition.Bottom);
writer.Indent--;
writer.WriteLine();
}
private void WriteRows(HtmlTextWriter writer, GridView gridView, GridViewRowCollection rows)
{
if (DesignMode == true)
{
return;
}
TemplateField templateField = null;
ITemplate itemTemplate = null;
if (rows.Count > 0)
{
foreach (GridViewRow row in rows)
{
DataControlRowType rowType = row.RowType;
//当前行的类型 为枚举值 Header = 0,Footer = 1,DataRow = 2,Separator = 3,Pager = 4, EmptyDataRow = 5,
DataControlRowState rowState = row.RowState;
int rowIndex = row.RowIndex;
if (rowType == DataControlRowType.DataRow)
{
templateField = ((DataControlFieldCell) (row.Cells[0])).ContainingField as TemplateField;
//得到模板列
if (templateField != null && templateField.HeaderTemplate != null && rowIndex == 0)
//如果有headerTemplate
{
RepeaterItem header = new RepeaterItem(row.RowIndex, ListItemType.Header);
templateField.HeaderTemplate.InstantiateIn(header);
header.RenderControl(writer); //输出headerTemplate
}
if (templateField != null && templateField.ItemTemplate != null) //如果有itemTemplate
{
(((DataBoundLiteralControl) (row.Cells[0].Controls[0]))).RenderControl(writer);
//输出itemTemplate 注意itemTemplate中是带数据绑定的
}
//如果有footerTemplate
if (templateField != null && templateField.FooterTemplate != null && rowIndex == rows.Count - 1)
{
RepeaterItem footer = new RepeaterItem(row.RowIndex, ListItemType.Footer);
templateField.FooterTemplate.InstantiateIn(footer);
footer.RenderControl(writer);
//输出footerTemplate
}
}
writer.WriteLine();
}
}
else //显示EmptyTemplate
{
RepeaterItem emptyTemplate = new RepeaterItem(0, ListItemType.Item);
gridView.EmptyDataTemplate.InstantiateIn(emptyTemplate);
emptyTemplate.RenderControl(writer);
}
}
private void WritePagerSection(HtmlTextWriter writer, PagerPosition pos)
{
if (AllowPaging &&
((PagerSettings.Position == pos) || (PagerSettings.Position == PagerPosition.TopAndBottom)))
{
TableCell innerTable = null;
if ((pos == PagerPosition.Top) &&
(TopPagerRow != null))
{
//innerTable = this.TopPagerRow.Cells[0].Controls[0] as Table;
innerTable = BottomPagerRow.Cells[0] as TableCell;
}
else if ((pos == PagerPosition.Bottom) &&
(BottomPagerRow != null))
{
innerTable = BottomPagerRow.Cells[0] as TableCell;
}
if ((innerTable != null))
{
string className = "AspNet-GridView-";
className += (pos == PagerPosition.Top) ? "Top " : "Bottom ";
if (PagerStyle != null)
{
className += PagerStyle.CssClass;
}
className = className.Trim();
writer.WriteLine();
writer.WriteBeginTag("div");
writer.WriteAttribute("class", className);
writer.Write(HtmlTextWriter.TagRightChar);
writer.Indent++;
foreach (Control ctrl in innerTable.Controls)
{
writer.WriteLine();
ctrl.RenderControl(writer);
}
writer.Indent--;
writer.WriteLine();
writer.WriteEndTag("div");
}
}
}
}
}
using System.ComponentModel;
using System.Web.UI;
using System.Web.UI.WebControls;
namespace Jec.CustomControls
{
[DefaultProperty("Text")]
[ToolboxData("<{0}:TemplateGridView runat=server></{0}:TemplateGridView>")]
public class TemplateGridView : GridView, INamingContainer
{
protected override void RenderContents(HtmlTextWriter writer)
{
GridView gridView = this;
writer.Indent++;
//分页在顶部
WritePagerSection(writer, PagerPosition.Top);
ArrayList rows = new ArrayList();
GridViewRowCollection gvrc = null;
//输出columns
WriteRows(writer, gridView, gridView.Rows);
//分页在底部
WritePagerSection(writer, PagerPosition.Bottom);
writer.Indent--;
writer.WriteLine();
}
private void WriteRows(HtmlTextWriter writer, GridView gridView, GridViewRowCollection rows)
{
if (DesignMode == true)
{
return;
}
TemplateField templateField = null;
ITemplate itemTemplate = null;
if (rows.Count > 0)
{
foreach (GridViewRow row in rows)
{
DataControlRowType rowType = row.RowType;
//当前行的类型 为枚举值 Header = 0,Footer = 1,DataRow = 2,Separator = 3,Pager = 4, EmptyDataRow = 5,
DataControlRowState rowState = row.RowState;
int rowIndex = row.RowIndex;
if (rowType == DataControlRowType.DataRow)
{
templateField = ((DataControlFieldCell) (row.Cells[0])).ContainingField as TemplateField;
//得到模板列
if (templateField != null && templateField.HeaderTemplate != null && rowIndex == 0)
//如果有headerTemplate
{
RepeaterItem header = new RepeaterItem(row.RowIndex, ListItemType.Header);
templateField.HeaderTemplate.InstantiateIn(header);
header.RenderControl(writer); //输出headerTemplate
}
if (templateField != null && templateField.ItemTemplate != null) //如果有itemTemplate
{
(((DataBoundLiteralControl) (row.Cells[0].Controls[0]))).RenderControl(writer);
//输出itemTemplate 注意itemTemplate中是带数据绑定的
}
//如果有footerTemplate
if (templateField != null && templateField.FooterTemplate != null && rowIndex == rows.Count - 1)
{
RepeaterItem footer = new RepeaterItem(row.RowIndex, ListItemType.Footer);
templateField.FooterTemplate.InstantiateIn(footer);
footer.RenderControl(writer);
//输出footerTemplate
}
}
writer.WriteLine();
}
}
else //显示EmptyTemplate
{
RepeaterItem emptyTemplate = new RepeaterItem(0, ListItemType.Item);
gridView.EmptyDataTemplate.InstantiateIn(emptyTemplate);
emptyTemplate.RenderControl(writer);
}
}
private void WritePagerSection(HtmlTextWriter writer, PagerPosition pos)
{
if (AllowPaging &&
((PagerSettings.Position == pos) || (PagerSettings.Position == PagerPosition.TopAndBottom)))
{
TableCell innerTable = null;
if ((pos == PagerPosition.Top) &&
(TopPagerRow != null))
{
//innerTable = this.TopPagerRow.Cells[0].Controls[0] as Table;
innerTable = BottomPagerRow.Cells[0] as TableCell;
}
else if ((pos == PagerPosition.Bottom) &&
(BottomPagerRow != null))
{
innerTable = BottomPagerRow.Cells[0] as TableCell;
}
if ((innerTable != null))
{
string className = "AspNet-GridView-";
className += (pos == PagerPosition.Top) ? "Top " : "Bottom ";
if (PagerStyle != null)
{
className += PagerStyle.CssClass;
}
className = className.Trim();
writer.WriteLine();
writer.WriteBeginTag("div");
writer.WriteAttribute("class", className);
writer.Write(HtmlTextWriter.TagRightChar);
writer.Indent++;
foreach (Control ctrl in innerTable.Controls)
{
writer.WriteLine();
ctrl.RenderControl(writer);
}
writer.Indent--;
writer.WriteLine();
writer.WriteEndTag("div");
}
}
}
}
}
使用上面定制过的GridView就可以像Repeater使用灵活的模板列来展示数据(仅仅是展示), 如果要用到GridView管理相关的如Edit,Delete Command的特性,还是使用原来的GridView比较好.毕竟管理页面不需要像展示页面那样灵活多变.
用GridView实现这样的效果就不是难事了
示例代码下载

