让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.
示例代码如下:
<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>

   其中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[0as TableCell;
                }
                
else if ((pos == PagerPosition.Bottom) &&
                         (BottomPagerRow 
!= null))
                {
                    innerTable 
= BottomPagerRow.Cells[0as 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实现这样的效果就不是难事了
    示例代码下载






posted @ 2007-09-28 18:53 jecray 阅读(1134) 评论(8) 编辑 收藏