在ASP.NET 2.0中操作数据之二十九:用DataList和Repeater来显示数据
导言
在之前的28篇教程的例子里,如果我们需要显示某个数据源的多条记录,我们使用gridview .gridview 的一行表示数据源的一条记录,列表示一个字段.虽然gridview 用来显示数据,分页,排序,编辑,删除非常的方便,但是有点臃肿.而且gridview 结构的标记是固定的—它包含一个带有<tr>和<td>的html <table>标记.
为了在显示多条记录时,有更好的自定义功能,asp.net 2.0提供了datalist 和repeater (asp.net 1.x版本里也有 ).datalist 和repeater 使用模板来显示内容,而不是象在gridview里那样使用boundfields, checkboxfields, buttonfields等.datalist 的标记语言为html <table>, 不过它允许每一行显示多条记录.另一方面,repeater不会生成多余的标记语言,因此如果你想精确控制标记语言的生成,它是最理想的选择.
在后面的若干章教程里,我们将从使用datalist 和repeater 的模板显示数据开始,来学习它们的最基本的用法.我们将学习如何控制这些控件的格式,如何在datalist里改变数据的布局,最常见的主/从场景,编辑和删除数据的方法,以及如何分页等.
第一步 1: 添加datalist 和repeater 教程页
在开始本篇教程前,我们首先花点时间来创建一些页,这些页会在本篇和后面的几篇教程里用到.先添加一个名为datalistrepeaterbasics的文件夹,然后,添加下面的页,添加页的时候确保每页都选择了 site.master作为母板页:
default.aspx
basics.aspx
formatting.aspx
repeatcolumnanddirection.aspx
nestedcontrols.aspx
图 1: 创建 datalistrepeaterbasics 文件夹 和添加页
打开default.aspx页的设计视图,从usercontrols文件夹将sectionleveltutoriallisting.ascx用户控件拖进来.这个用户控件提供的功能就是列出教程章节.我们在母板页和站点导航里创建的它.
图 2: 添加sectionleveltutoriallisting.ascx 用户控件到default.aspx
最后,将这些页的地址加到 web.sitemap 的条目里.在paging and sorting <sitemapnode>之后添加下面的标记.
<sitemapnode title="displaying data with the datalist and repeater" description="samples of reports that use the datalist and repeater controls" url="~/datalistrepeaterbasics/default.aspx" > <sitemapnode title="basic examples" description="examines the basics for displaying data using the datalist and repeater controls." url="~/datalistrepeaterbasics/basics.aspx" /> <sitemapnode title="formatting" description="learn how to format the datalist and the web controls within the datalist and repeater's templates." url="~/datalistrepeaterbasics/formatting.aspx" /> <sitemapnode title="adjusting the datalist s layout" description="illustrates how to alter the datalist's layout, showing multiple data source records per table row." url="~/datalistrepeaterbasics/repeatcolumnanddirection.aspx" /> <sitemapnode title="nesting a repeater within a datalist" description="learn how to nest a repeater within the template of a datalist." url="~/datalistrepeaterbasics/nestedcontrols.aspx" /> </sitemapnode>
图 3: 向 site map 里添加新的页
第二步: 在 datalist里显示product信息
和formview一样,datalist 使用模板来显示信息,而非boundfields, checkboxfields等.而与formview不同的是,datalist 是被用来显示一组记录,而不是单独的一条.现在我们开始本章的教程.首先看看如何将product 绑定到datalist.打开datalistrepeaterbasics 文件夹里的basics.aspx 页,然后从工具箱里拖一个datalist 进来.如图4所示,在指定模板前,设计器会是灰色的.
图 4: 从工具箱拖一个datalist到设计器里
打开datalist的智能标签,添加一个objectdatasource ,使用productsbll 类的getproducts 方法来配置它.因为在本教程里创建的datalist 为只读的,因此在insert, update, 和delete 标签的下拉列表里都选择none.
图 5: 创建一个新的objectdatasource
图 6: 用productsbll 类来配置objectdatasource
图 7: 使用getproducts 方法来获取所有product的信息
通过智能标签里配置完objectdatasource ,并把它和datalist 关联起来后,visual studio会在datalist 里自动为数据源返回的每个字段创建一个itemtemplate 用来显示name 和value (见下面的代码).这个默认的itemtemplate看起来和绑定formview 时自动产生的模板是一样的.
<asp:datalist id="datalist1" runat="server" datakeyfield="productid" datasourceid="objectdatasource1" enableviewstate="false"> <itemtemplate> productid: <asp:label id="productidlabel" runat="server" text='<%# eval("productid") %>' /><br /> productname: <asp:label id="productnamelabel" runat="server" text='<%# eval("productname") %>' /><br /> supplierid: <asp:label id="supplieridlabel" runat="server" text='<%# eval("supplierid") %>' /><br /> categoryid: <asp:label id="categoryidlabel" runat="server" text='<%# eval("categoryid") %>'/><br /> quantityperunit: <asp:label id="quantityperunitlabel" runat="server" text='<%# eval("quantityperunit") %>' /><br /> unitprice: <asp:label id="unitpricelabel" runat="server" text='<%# eval("unitprice") %>' /><br /> unitsinstock: <asp:label id="unitsinstocklabel" runat="server" text='<%# eval("unitsinstock") %>' /><br /> unitsonorder: <asp:label id="unitsonorderlabel" runat="server" text='<%# eval("unitsonorder") %>' /><br /> reorderlevel: <asp:label id="reorderlevellabel" runat="server" text='<%# eval("reorderlevel") %>' /><br /> discontinued: <asp:label id="discontinuedlabel" runat="server" text='<%# eval("discontinued") %>' /><br /> categoryname: <asp:label id="categorynamelabel" runat="server" text='<%# eval("categoryname") %>' /><br /> suppliername: <asp:label id="suppliernamelabel" runat="server" text='<%# eval("suppliername") %>' /><br /> <br /> </itemtemplate> </asp:datalist> <asp:objectdatasource id="objectdatasource1" runat="server" oldvaluesparameterformatstring="original_{0}" selectmethod="getproducts" typename="productsbll"> </asp:objectdatasource>
注意:当通过智能标签将数据源绑定到formview 时,vistual studio会创建一个itemtemplate,一个insertitemtemplate和一个edititemtemplate.然而对datalist来说,只会创建一个itemtemplate .这是因为datalist 不象formview那样,有内置的编辑和插入功能.datalist 没有编辑和删除相关的事件,虽然要完成这些功能,对datalist 来说没有formview那么简单,我们仍然可以加少量代码来实现它.我们在以后的教程里会讲到如何在datalist 里完成编辑和删除的功能.
让我们花点时间来改善一下模板的外观.我们只显示product的name,supplier,category,数量和单价.而且我们用<h4> 来显示名字,其它字段都放在 <h4>heading下的<table>里.你可以通过datalist的只能标签里的 edit templates ,或者直接修改页面声明语法来达到以上目的.如果你是通过edit templates 来实现,那你的页面代码可能和下面的不完全一样.但是通过浏览器浏览你的页面应该和图8看起来差不多.
<asp:datalist id="datalist1" runat="server" datakeyfield="productid" datasourceid="objectdatasource1" enableviewstate="false"> <itemtemplate> <h4><asp:label id="productnamelabel" runat="server" text='<%# eval("productname") %>' /></h4> <table border="0"> <tr> <td class="productpropertylabel">category:</td> <td><asp:label id="categorynamelabel" runat="server" text='<%# eval("categoryname") %>' /></td> <td class="productpropertylabel">supplier:</td> <td><asp:label id="suppliernamelabel" runat="server" text='<%# eval("suppliername") %>' /></td> </tr> <tr> <td class="productpropertylabel">qty/unit:</td> <td><asp:label id="quantityperunitlabel" runat="server" text='<%# eval("quantityperunit") %>' /></td> <td class="productpropertylabel">price:</td> <td><asp:label id="unitpricelabel" runat="server" text='<%# eval("unitprice", "{0:c}") %>' /></td> </tr> </table> </itemtemplate> </asp:datalist>
注意:上面的例子使用的是text指定为数据绑定的值的label控件.我们也可以不使用label,而只是保留数据绑定的代码.也就是说,我们可以用<%# eval("categoryname") %>来代替<asp:label id="categorynamelabel" runat="server" text='<%# eval("categoryname") %>' />.
使用label控件有两个好处,第一点在下一章我们会看到,就是提供了一个格式化数据的简单途径.第二点是不使用web控件的时候,edit templates 不显示声明的数据绑定代码.通过edit templates 的界面很容易操作静态标记语言和web控件,其前提是所有的数据绑定都是通过web控件的智能标签里的edit databindings对话框来实现.因此,使用datalist的时候,我建议使用label控件,这样通过edit templates 就可以操作其内容.我们会看到,使用repeater 时如果需要编辑其内容,需要切换到源视图.而设计repeater模板的时候,我通常不使用label控件,除非我需要格式化绑定数据的外观.
图 8: 用datalist的 itemtemplate显示product
第三步: 改善datalist的外观
和gridview一样,datalist 提供了一些和风格有关的属性,比如font, forecolor, backcolor, cssclass, itemstyle, alternatingitemstyle, selecteditemstyle等.当使用 gridview 和detailsview 时,我们首先在datawebcontrols theme里创建了一些皮肤文件,这些文件预定义了这两个控件的cssclass 属性和rowstyle, headerstyle等.我们使用datalist的时候也采取这种方法.
象在使用objectdatasource展现数据 里谈到的那样,一个skin 文件定义了一个web控件的默认显示属性.一个theme 是一组skin, css, image, 和javascript files 的集合,它定义了一个web站点的外观.在使用objectdatasource展现数据 那一章里,我们创建了一个datawebcontrols theme(app_themes 文件夹下) ,它包含两个skin文件- gridview.skin 和detailsview.skin.我们现在来为datalist添加第三个.右键单击app_themes/datawebcontrols 文件夹,选择add a new item,选择skin file,在名字里填datalist.skin.
图 9: 创建一个名为datalist.skin的skin文件
将下面的标记语言添加到datalist.skin里.
<asp:datalist runat="server" cssclass="datawebcontrolstyle"> <alternatingitemstyle cssclass="alternatingrowstyle" /> <itemstyle cssclass="rowstyle" /> <headerstyle cssclass="headerstyle" /> <footerstyle cssclass="footerstyle" /> <selecteditemstyle cssclass="selectedrowstyle" /> </asp:datalist>
上面用gridview 和detailsview 使用的css文件设置datalist .在datawebcontrolstyle, alternatingrowstyle, rowstyle里用到的css文件是在styles.css 里定义的.
添加完skin后,datalist的外观看起来会变了(你可以在视图菜单里选择刷新来看改变后的效果).见图10,alternating product 的背景色为粉红色.
图 10: 添加skin文件后的效果
第四步: 浏览datalist的其它templates
datalist 还支持除了itemtemplate外的其它6种template:
headertemplate — 用来呈现 header row
alternatingitemtemplate — 用来呈现alternating items
selecteditemtemplate — 用来呈现selected item; selected item 的index 可以通过datalist 的 selectedindex property 得到
edititemtemplate — 用来呈现被编辑的item
separatortemplate — 用来分隔各个item
footertemplate - 用来呈现footer row
当指定headertemplate 或footertemplate时,datalist 会加一个header 或footer .和gridview一样,datalist 的header 和footer 没有和数据绑定在一起.
注意:如我们在在gridview的页脚中显示统计信息 一章里看到的那样,header 和footer 不支持数据绑定语法,而数据绑定的信息可以通过gridview的rowdatabound event handler来写.这个技术可以用来技术绑定的数据的和或其它信息,并在footer里显示.同样的,可以在datalist 和repeater 里面这样做.它们唯一的区别在于对datalist 和repeater 来说是为itemdatabound 创建event handler (而不是rowdatabound ).
在我们的例子里,我们将标题“product information”用<h3> 显示在datalist的results 的顶部.为了达到这个目的,在headertemplate 中添加合适的标记语言.或者通过datalist的智能标签中的edit templates 来实现.从下拉列表中选择header template ,从style 下拉列表中选择heading 3 并输入text(见图11).
图 11: 添加text 为“product information”的headertemplate
同样,直接在<asp:datalist>标记里加入以下代码也可以达到上面的目的.
<headertemplate> <h3>product information</h3> </headertemplate>
为了在每个列出的product 之间保留一些空间,我们现在来添加一个separatortemplate .<hr>标签可以完成这种分割的功能.见下面的标记语言
<separatortemplate> <hr /> </separatortemplate>
注意:与headertemplate 和footertemplates一样,separatortemplate 并不和数据源里的任何数据绑定.因此,并不能直接的和datalist绑定的数据发生关系.
现在在浏览器里浏览这个页面,看起来应该和图12差不多.注意header 和各个product 之间的线.
图 12: datalist 现在包含了 header row 和每个product 之间有一条线
第五步: 使用repeater
在浏览图12的例子时,你可以看看页面的源文件.你会看到datalist 包含有<tr>和<td>标记的html<table>.这个实际上和gridview一样.我们会在将来的教程里看到,datalist允许每一行显示多条记录.
但如果你不想使用html<table>呢?我们将使用repeater .repeater 也是基于templates构建的.它提供以下5种template:
headertemplate — 在items前加指定的标记
itemtemplate — 用来呈现items
alternatingitemtemplate — 用来呈现alternating items
separatortemplate —在各个item 之间加指定的标记
footertemplate - 在items后加指定的标记
在asp.net 1.x版本里.repeater 通常用来显示一些数据列.在这种情况下,headertemplate 和footertemplates 包含一对<ul>标记,而itemtemplate 包含 <li> 和数据绑定语法.这种方法在asp.net 2.0也适用,比如我们在母板页和站点导航一章里看到的例子:
在site.master母板页里, repeater 用来显示*站点内容(basic reporting, filtering reports, customized formatting, and so on); 嵌套的repeater 用来显示 子区域的内容.
在sectionleveltutoriallisting.ascx用户控件里, repeater 用来显示当前站点区域的子区域内容.
注意:asp.net 2.0可以使用bulletedlist control.使用它的时候不需要指定任何和list有关的html.而仅仅是指定每个list item的字段.
repeater 是一个"全能"的控件,如果你找不到控件可以产生需要的标记语言,那么可以使用repeater .我们来举例说明,在第二步里创建的显示product信息的datalist上显示出categoried.我们将每个categorie作为一列显示在单行的html<table>里.从工具箱里拖一个repeater 到显示product 的datalist上.和datalist一样,在定义templates前,repeater 是灰色的.
图 13: 添加一个 repeater 控件
在repeater 的智能标签里只有一个可选项:选择数据源.创建一个objectdatasource ,用categoriesbll 类的getcategories 方法配置它.
图 14: 创建objectdatasource
图 15: 用 categoriesbll 类配置objectdatasource
图16: 用 getcategories method获取所有categories的信息
和datalist不一样,在绑定到数据源后,visual studio不会为repeater 自动创建itemtemplate .而且repeater 的templates 不能通过设计器来配置,只能写页面代码.
我们用如下标记来将每个category作为一列显示在单行的<table>里:
<table> <tr> <td>category 1</td> <td>category 2</td> ... <td>category n</td> </tr> </table>
由于<td>category x</td>是重复的一部分,因此会显示在repeater的itemtemplate里.在它之前的标记<table><tr>会放在headertemplate里,而结束标记</tr></table>会放在footertemplate里.在设计器的左下角点源视图按钮进入asp.net页的声明代码部分,输入以下代码:
<asp:repeater id="repeater1" runat="server" datasourceid="objectdatasource2" enableviewstate="false"> <headertemplate> <table> <tr> </headertemplate> <itemtemplate> <td><%# eval("categoryname") %></td> </itemtemplate> <footertemplate> </tr> </table> </footertemplate> </asp:repeater>
repeater 精确的包含在它模板里指定的标记,不会有任何多余的部分.图17显示通过浏览器浏览repeater的样子.
图 17: 在单行的html <table> 用单独的列列出每个category
第六步: 改善repeater的外观
既然repeater 是精确呈现在templates里指定的标记,那么你应该可以想到它不包含任何和风格有关的属性.为了改变repeater产生的内容的外观,我们需要手动的将html或css加到它的templates里.
在这个例子里,我们将做一个类似datalist的alternating rows那样的东西,改变category 的背景色.我们通过itemtemplate 和alternatingitemtemplate 来为每个repeater item 指定rowstyle css class ,为每个alternating repeater item 指定alternatingrowstyle css class ,象下面的代码一样:
<itemtemplate> <td class="rowstyle"><%# eval("categoryname") %></td> </itemtemplate> <alternatingitemtemplate> <td class="alternatingrowstyle"><%# eval("categoryname") %></td> </alternatingitemtemplate>
我们还要添加一个text为“product categories”的header .由于我们不知道 <table>会由多少列组成,最简单的方法保证产生的header 可以跨越所有的列是使用两个<table>.第一个<table>包含两行 — header 和一行包含第二个 <table>的行.第二个 <table>里每个category 为一列.
<table> <tr> <th>product categories</th> </tr> <tr> <td> <table> <tr> <td>category 1</td> <td>category 2</td> ... <td>category n</td> </tr> </table> </td> </tr> </table>
下面的 headertemplate 和footertemplate 产生需要的标记:
<asp:repeater id="repeater1" runat="server" datasourceid="objectdatasource2" enableviewstate="false"> <headertemplate> <table cellpadding="0" cellspacing="0"> <tr> <th class="headerstyle">product categories</th> </tr> <tr> <td> <table cellpadding="4" cellspacing="0"> <tr> </headertemplate> <itemtemplate> <td class="rowstyle"><%# eval("categoryname") %></td> </itemtemplate> <alternatingitemtemplate> <td class="alternatingrowstyle"> <%# eval("categoryname") %></td> </alternatingitemtemplate> <footertemplate> </tr> </table> </td> </tr> </table> </footertemplate> </asp:repeater>
图18 里可以看到现在repeater的样子.
图 18: category 列的背景色交替变换and includes a header row
总结
虽然使用gridview 来显示,编辑,删除,排序和分页数据都非常容易,但是很臃肿.为了更好的控制外观,我们需要使用datalist 或repeater .这些控件使用templates 来显示记录,而不是boundfields.
datalist 包含一个html <table>,默认情况下table的一行显示数据源的一条记录,和gridview一样.我们在以后的教程里会看到,datalist 可以在一个table 行里表示多条记录.而另一方面,repeater严格的显示在templates指定的标记.它不会添加任何额外的信息,因此通常被用来在除了 <table> 以外的html元素里显示数据.
datalist 和repeater 在输出上提供了更大的灵活性,而和gridview相比,它们又缺少很多内置的特性.在以后的教程里我们会看到,有一些特性我们可以很简单的加上.但是要记住,使用datalist 和repeater而不是gridview ,如果想使用这些特性的话,你必须去实现它们.
祝编程快乐!
作者简介
scott mitchell,著有六本asp/asp.net方面的书,是4guysfromrolla.com的创始人,自1998年以来一直应用 微软web技术。scott是个独立的技术咨询顾问,培训师,作家,最近完成了将由sams出版社出版的新作,24小时内精通asp.net 2.0。他的联系电邮为,也可以通过他的博客http://scottonwriting.net与他联系。