随笔 - 97  文章 - 0 评论 - 299 trackbacks - 434

原文 | 下载本教程中的编码例子 | 下载本教程的PDF版

导言




在前面的教程里我们学习了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数据绑定过程的大概步骤

  1. DataList的DataBinding event 被激发
  2. DataList
    对数据源的每条记录...
    For each record in the data source…
    1. 创建一个DataListItem 对象
    2. 激发ItemCreated event
    3. 绑定记录到DataListItem
    4. 激发ItemDataBound event
    5. 将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  信息

Before we worry about the formatting, let’s first create a page that uses a DataList to display product information. In the previous tutorial we created a DataList whose ItemTemplate displayed each product’s name, category, supplier, quantity per unit, and price. Let’s repeat this functionality here in this tutorial. To accomplish this, you can either recreate the DataList and its ObjectDataSource from scratch, or you can copy over those controls from the page created in the previous tutorial (Basics.aspx) and paste them into the page for this tutorial (Formatting.aspx).

在学习格式化之前,我们首先创建一个使用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的值,再根据这个值来格式化数据.

Create an ItemDataBound event for the DataList and add the following code:
添加以下代码为DataList创建ItemDataBound事件

C#
1
            2
            3
            4
            5
            6
            7
            8
            9
            10
            11
            12
            13
            14
            15
            
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来完成格式化:

C#
1
            2
            3
            4
            5
            6
            7
            8
            9
            10
            11
            
// 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).下面是这两个方法的代码:

C#
1
            2
            3
            4
            5
            6
            7
            8
            9
            10
            11
            12
            13
            14
            15
            16
            17
            18
            19
            20
            
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 方法以价格为参数,我们需要以下代码:

C#
1
            2
            3
            4
            5
            6
            7
            8
            9
            10
            
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里调用这些格式化功能.我们需要使用以下代码:

ASP.NET
1
            
<%# 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 方法返回的值转换为需要的参数类型,代码如下:

ASP.NET
1
            2
            3
            4
            5
            
<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.NET
1
            2
            3
            
<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。他的联系电邮为mitchell@4guysfromrolla.com,也可以通过他的博客http://ScottOnWriting.NET与他联系。

posted on 2006-10-08 13:16 有些伤感 阅读(...) 评论(...) 编辑 收藏