在ASP.NET 2.0中操作数据之三十:格式化DataList和Repeater的数据
导言
在前面的教程里我们学习了datalist提供了一些风格样式的属性.而且我们还学习了如何定义headstyle, itemstyle, alternatingitemstyle, 和selecteditemstyle等属性的默认css.除了这四个属性外,datalist还提供了其它属性,比如font, forecolor, backcolor, 和borderwidth.而repeater没有提供任何这样的属性.如果你需要用reperter来实现这些效果,你就需要在templates里直接写标记语言.
通常,数据需要怎样的格式取决于数据本身.比如,我们可能使用灰色的字体列出那些被停止使用的product,或者在unitsinstock等于0的时候显示高亮.前面的教程里我们已经学习了gridview, detailsview, 和formview 都提供了两种截然不同的格式化数据的方法.
databound 事件— 为databound 事件创建一个合适的event handler, 它在数据绑定到item的时候激发(对gridview来说是rowdatabound 事件; 对 datalist 和repeater来说是 itemdatabound 事件). 在这些事件里, 刚刚绑定的数据可以被格式化. 参见《基于数据的自定义格式化》 这章.
templates 的格式化功能— 在detailsview 或gridview 里使用templatefields , 或 在formview 里使用template , 我们可以在asp.net page的code-behind class里或者bll里,或者任何其它web程序里可以调用的类库里加格式化信息. 这种格式化功能可以接收任意的输入参数, 但是在template里比如返回html . 格式化功能最早在在gridview控件中使用templatefield 这章里谈到过. 这两种方法都可以在datalist和repeater里使用.在本章里我们将一步步用这两种方法在这两个控件里做示例.
使用 itemdatabound event handler
当数据绑定到 datalist时, 无论是使用数据源控件或者 直接在代码里使用datasource 和 databind() , datalist的databinding 事件都会被激发. datalist 为数据源的每条记录创建一个 datalistitem 对象,然后绑定到当前记录. 在这个过程中datalist 激发两个事件:
itemcreated — 在创建datalistitem 后激发
itemdatabound — 当前记录绑定到datalistitem 后激发
下面列出了datalist数据绑定过程的大概步骤
datalist的databinding event 被激发
datalist
对数据源的每条记录...
for each record in the data source…
创建一个datalistitem 对象
激发itemcreated event
绑定记录到datalistitem
激发itemdatabound event
将datalistitem 添加到items collection
当数据绑定到repeater时,和上面所说的情况一样.唯一的区别在于,datalistitem换成了repeateritem.
注意:细心的读者可能注意到了datalist和repeater绑定到数据时的步骤顺序和gridview有些许差别.在数据绑定过程的后期,gridview会激发databound事件,而datalist和repeater则都没有这个事件.
和gridview一样,可以为itemdatabound事件创建一个event handler 来格式化数据.这个event handler 可以处理刚刚绑定到datalistitem或repeateritem的数据,来按照需要进行格式化.
对datalist来说,可以使用风格样式相关的属性,如font, forecolor, backcolor, cssclass等,来格式化item.而如果你想格式化datalist里的template里的web控件,你需要编程去获取这些控件,然后来控制.我们在《custom formatting based upon data》里已经看过怎样做.和repeater控件一样,repeateritem类也没有风格样式相关的属性,因此,你需要在itemdatabound event handler里编程去实现.
由于在datalist和repeater里使用itemdatabound格式化技术从本质上来说是由于的,因此我们的示例主要讲datalist.
第一步: 在datalist显示product 信息
在学习格式化之前,我们首先创建一个使用datalist显示product信息的页面.在前面一章里,我们创建了一个itemtemplate显示product 的name,category, supplier, quantity和price的datalist.我们在本章来重复做一次.你可以重新创建datalist和它的objectdatasource ,或者直接把前面一章里的basics.aspx里的控件复制到本章的页面(formatting.aspx)里.当你完成了formatting.aspx后,将datalist的id从datalist1改为itemdataboundformattingexample.下面,在浏览器里看看datalist.如图1所示,唯一的格式在于每个product的交替的背景色.
图 1: 在datalist 里列出product信息
在本章教程里,我们来将价格小于 $20.00 的product的名字和单价用黄色 高亮来显示.
第二步: 在 itemdatabound event handler里编程判断数据的值
由于只有价格低于$20.00 的product会被格式化,因此我们首先要判断每个product的价格.在绑定数据到datalist时,datalist 为每条数据源的记录创建一个datalistitem实例,并绑定数据.当记录绑定到datalistitem对象后,itemdatabound事件被激发.我们可以为这个事件创建一个event handler来判断当前datalistitem的值,再根据这个值来格式化数据.
添加以下代码为datalist创建itemdatabound事件
protected void itemdataboundformattingexample_itemdatabound (object sender, datalistitemeventargs e) { if (e.item.itemtype == listitemtype.item || e.item.itemtype == listitemtype.alternatingitem) { // programmatically reference the productsrow instance bound // to this datalistitem northwind.productsrow product = (northwind.productsrow)((system.data.datarowview)e.item.dataitem).row; // see if the unitprice is not null and less than $20.00 if (!product.isunitpricenull() && product.unitprice < 20) { // todo: highlight the product's name and price } } }
datalist的itemdatabound event handler在概念和语义上来说,和gridview的rowdatabound event handler一样(见基于数据的自定义格式化),语法上有一点差别.当itemdatabound事件激发时,刚刚绑定数据的datalistitem通过e.item(在gridview里是e.row和rowdatabound)传递给相关的event handler.datalist的itemdatabound event handler影响到每一行,包括 header , footer 和separator.但是product信息只绑定到data行.因此,在处理itemdatabound事件前,我们首先要判断处理的是否是data行.这个可以通过检查datalistitem的itemtype 属性来完成,它可以有以下八个值:
alternatingitem
edititem
footer
header
item
pager
selecteditem
separator
item和alternatingitem都表示datalist的data item.假设我们在处理item或alternatingitem,我们可以获取绑定到当前datalistitem的productsrow的实例.datalistitem的dataitem属性包含了datarowview对象的引用,通过它的row属性可以获取productsrow对象.
下面我们来检查productsrow实例的单价属性.由于product表的unitprice字段允许空值,所以在获取unitprice属性前我们应该先用isunitpricenull()方法检查这个值是否为空.如果不是,我们再检查看它是否低于$20.00.如果是,我们就进行格式化处理.
第三步: 是product的 name 和price高亮显示
一旦我们发现product的price低于$20.00,我们将使它的name和price显示高亮.首先我们要编程获得itemtemplate里显示product的name和price的label控件.然后我们将它的背景色显示为黄色.这个可以直接通过修改label空间的backcolor属性(labelid.backcolor = color.yellow).当然最理想的做法是所有的显示相关的行为都通过css来实现.实际上我们在基于数据的自定义格式化一章里创建的styles.css - affordablepriceemphasis已经提供了这个功能.
使用以下代码设置两个label控件的cssclass 属性为affordablepriceemphasis来完成格式化:
// highlight the product name and unit price labels // first, get a reference to the two label web controls label productnamelabel = (label)e.item.findcontrol("productnamelabel"); label unitpricelabel = (label)e.item.findcontrol("unitpricelabel"); // next, set their cssclass properties if (productnamelabel != null) productnamelabel.cssclass = "affordablepriceemphasis"; if (unitpricelabel != null) unitpricelabel.cssclass = "affordablepriceemphasis";
itemdatabound 事件完成后,在浏览器里浏览formatting.aspx页.如图2所示,价格低于 $20.00 的product的name和prict都高亮显示了.
图2: 价格低于$20.00 的product都被高亮显示
注意:由于datalist使用 html <table>, datalistitem实例有可以设置整个item风格的属性.比如,如果我们想在price低于$20.00时将所有的item都用黄色来高亮显示,我们可以用e.item.cssclass = "affordablepriceemphasis"来代替上面的代码(见图3).
而组成repeater的repeateritem并没有提供这样的属性.因此,在repeater里自定义格式需要设置templates里的控件的格式,象在图2里所做的那样.
图 3: the entire product item is highlighted for products under $20.00
使用 template的格式化功能
在在gridview控件中使用templatefield 一章里,我们学习了如何使用gridview templatefield的格式化功能来格式化gridview的数据.格式化功能是一种可以从template里调用并返回html显示的方法.格式化功能可以写在asp.net page的 code-behind class 或app_code 文件夹里的类文件里或单独的类库项目里.如果你想在其它asp.net web程序或多个asp.net 页用到同样的功能,那么不要把它下在asp.net page的 code-behind class 里.
为了演示这个功能,我们将修改product信息.如果product被停用,我们在product的name后面增加一个“[discontinued]”的text.同样的,如果price低于 $20.00 我们将会用黄色来高亮显示(如我们在itemdatabound event handler例子里做的那样).如果price等于或高于 $20.00,我们将不显示实际的价格,而是在text里显示“please call for a price quote”. 图4是完成以上功能的页面截图.
图 4: 将比较贵的products 的价格用文本“please call for a price quote”来代替.
第一步: 创建格式化功能
这个例子里我们需要两个格式化功能,其一是在被停用的product name后面加上“[discontinued]”, 其二是对价格低于$20.00的product高亮显示,其它则显示“please call for a price quote”.我们将在asp.net page的code-behind class 里创建这些功能,并给它们取名为displayproductnameanddiscontinuedstatus 和displayprice.这两个方法都需要返回html,而且为了在asp.net page的声明语法里调用,都需要标记为protected (or public).下面是这两个方法的代码:
protected string displayproductnameanddiscontinuedstatus (string productname, bool discontinued) { // return just the productname if discontinued is false if (!discontinued) return productname; else // otherwise, return the productname appended with the text "[discontinued]" return string.concat(productname, " [discontinued]"); } protected string displayprice(northwind.productsrow product) { // if price is less than $20.00, return the price, highlighted if (!product.isunitpricenull() && product.unitprice < 20) return string.concat("<span class=\"affordablepriceemphasis\">", product.unitprice.tostring("c"), "</span>"); else // otherwise return the text, "please call for a price quote" return "<span>please call for a price quote</span>"; }
注意到displayproductnameanddiscontinuedstatus 方法接收productname 和discontinued 的值.而displayprice 方法接收productsrow (而不是unitprice).如果格式化功能处理可能包含数据库空值(比如unitprice,而productname和discontinued都不允许空)的量值,要特别小心处理.
输入的值可能是一个dbnull而不是你期望的数据类型,因此输入参数的类型必须为object.而且比如检查传进来的值是否为database null.也就是说,如果我们想让displayprice 方法以价格为参数,我们需要以下代码:
protected string displayprice(object unitprice) { // if price is less than $20.00, return the price, highlighted if (!convert.isdbnull(unitprice) && ((decimal) unitprice) < 20) return string.concat("<span class=\"affordablepriceemphasis\">", ((decimal) unitprice).tostring("c"), "</span>"); else // otherwise return the text, "please call for a price quote" return "<span>please call for a price quote</span>"; }
注意输入参数unitprice的类型为object,条件判断语句被修改为判断unitprice 是否为dbnull.而且,由于unitprice是作为object传进来的,所以必须要类型转换为decimal.
第二步: 在datalist 的itemtemplate调用格式化方法
在完成以上代码后,剩下的工作就是在datalist的itemtemplate里调用这些格式化功能.我们需要使用以下代码:
<%# methodname(inputparameter1, inputparameter2, ...) %>
在datalist的itemtemplate里,productnamelabel label通过指定text属性为<%# eval("productname") %>显示的product的name.为了在需要的情况下加上“[discontinued]” ,修改代码,使用displayproductnameanddiscontinuedstatus 方法来指定text属性.我们需要使用eval("columnname") 语法来将product的name和discontinued的值传进去.eval 返回的值为object类型,而displayproductnameanddiscontinuedstatus 的参数为string 和boolean.因此,我们需要将eval 方法返回的值转换为需要的参数类型,代码如下:
<h4> <asp:label id="productnamelabel" runat="server" text='<%# displayproductnameanddiscontinuedstatus((string) eval("productname"), (bool) eval("discontinued")) %>'> </asp:label> </h4>
和显示product的name和“[discontinued]” 文本一样,我们设置unitpricelabel label的属性为displayprice 的返回值来显示价格.我们将productsrow作为参数,而不是unitprice:
<asp:label id="unitpricelabel" runat="server" text='<%# displayprice((northwind.productsrow) ((system.data.datarowview) container.dataitem).row) %>'> </asp:label>
完成以上代码后,在浏览器里看一下页面.你的页面应该和图5看起来差不多.
图 5: 将比较贵的products 的价格用文本“please call for a price quote”来代替
总结
基于数据格式化datalist或repeater有两种方法.一种是为itemdatabound 创建event handler .itemdatabound 在数据源的每条记录绑定到datalistitem 或repeateritem时被激发.在itemdatabound event handler里,可以判断当前item的数据并格式化,而对datalistitem可以格式化整个item.
另一种是通过格式化功能来完成自定义格式化.格式化功能是一种可以从template里调用并返回html显示的方法.通常,通过判断绑定到当前item的值来决定返回什么样的html.这些值或者绑定到item的对象可以传递到格式化功能里.祝编程快乐!
作者简介
scott mitchell,著有六本asp/asp.net方面的书,是4guysfromrolla.com的创始人,自1998年以来一直应用 微软web技术。scott是个独立的技术咨询顾问,培训师,作家,最近完成了将由sams出版社出版的新作,24小时内精通asp.net 2.0。他的联系电邮为,也可以通过他的博客与他联系。
推荐阅读
-
在ASP.NET 2.0中操作数据之三十:格式化DataList和Repeater的数据
-
在ASP.NET 2.0中操作数据之二十九:用DataList和Repeater来显示数据
-
在ASP.NET 2.0中操作数据之三十三:基于DataList和Repeater使用DropDownList过滤的主/从报表
-
在ASP.NET 2.0中操作数据之四十三:DataList和Repeater数据排序(二)
-
在ASP.NET 2.0中操作数据之四十二:DataList和Repeater数据排序(一)
-
在ASP.NET 2.0中操作数据之四十一:DataList和Repeater数据分页
-
在ASP.NET 2.0中操作数据之四十五:DataList和Repeater里的自定义Button
-
在ASP.NET 2.0中操作数据之四十四:DataList和Repeater数据排序(三)
-
在ASP.NET 2.0中操作数据之十:使用 GridView和DetailView实现的主/从报表
-
在ASP.NET 2.0中操作数据之十一:基于数据的自定义格式化