在ASP.NET 2.0中操作数据之四十:自定义DataList编辑界面
导言
datalist的编辑界面由edititemtemplate里的标记语言和web控件定义。在目前为止所做的datalist编辑功能的例子里,编辑界面都只包含textbox。在前面一章里,我们通过添加验证控件来增加了用户体验,提高了可用性。
edititemtemplate可以包含除了textbox以外的很多控件,比如dropdownlist, radiobuttonlist, calendar等。和使用textbox一样,使用这些控件自定义编辑界面时,步骤如下:
为edititemtemplate添加控件.
使用绑定语法将相关的字段值赋给控件的属性.
在updatecommand事件处理里, 编程访问web控件的值,并将它传给相关的bll的方法.
本章我们将为datalist创建一个更丰富的编辑界面,它将包含dropdownlist和checkbox。我们将创建一个列出product信息的datalist,用户可以更新它的name,supplier,category和discontinued status。见图1。
图 1: 编辑界面包含一个textbox, 两个 dropdownlists和一个checkbox
第一步: 显示product 信息
在创建datalist的编辑界面前,我们需要先创建一个只读界面。先打开editdeletedatalist文件夹下的customizedui.aspx页,拖一个datalist进来,将id设为products。通过datalist的智能标签,创建一个名为productsdatasource的objectdatasource,用productsbll类的getproducts方法配置它。象前面一章一样,我们将直接通过bll来更新product信息。在update,insert,delete标签里选择none.
图 2: 在update, insert, delete 标签的下拉列表里选择 (none)
配置完objectdatasource后,visual studio会自动创建默认的itemtemplate,列出每个字段的值。将product name用<h4>表示,并添加一个edit button,确保将它的commandname属性设为 “edit”. 我的标记语言如下:
<itemtemplate> <h4> <asp:label id="productnamelabel" runat="server" text='<%# eval("productname") %>' /> </h4> <table border="0"> <tr> <td class="productpropertylabel">category:</td> <td class="productpropertyvalue"> <asp:label id="categorynamelabel" runat="server" text='<%# eval("categoryname") %>' /> </td> <td class="productpropertylabel">supplier:</td> <td class="productpropertyvalue"> <asp:label id="suppliernamelabel" runat="server" text='<%# eval("suppliername") %>' /> </td> </tr> <tr> <td class="productpropertylabel">discontinued:</td> <td class="productpropertyvalue"> <asp:label id="discontinuedlabel" runat="server" text='<%# eval("discontinued") %>' /> </td> <td class="productpropertylabel">price:</td> <td class="productpropertyvalue"> <asp:label id="unitpricelabel" runat="server" text='<%# eval("unitprice", "{0:c}") %>' /> </td> </tr> <tr> <td colspan="4"> <asp:button runat="server" id="editbutton" text="edit" commandname="edit" /> </td> </tr> </table> <br /> </itemtemplate>
上面的标记语言用<h4>表示product name,4列的<table>展示其它字段。前面已经讨论过styles.css里定义的productpropertylabel和productpropertyvalue类。浏览该页,见图3。
图 3: 显示product信息
第二步: 为编辑界面添加web控件
首先向edititemtemplate里添加需要的web控件。我们需要用一个dropdownlist表示category,一个dropdownlist表示supplier,一个checkbox 表示discontinued state。由于本例中不用编辑price,所以仍然用label来表示它。
点击datalist的智能标签上的“edit templates”,选择edititemtemplate,为它添加一个id为categories的edititemtemplate。
图 4: 为categories添加一个dropdownlist
然后从dropdownlist的智能标签里选择“choose data source”,创建一个名为categoriesdatasource的objectdatasource。用categoriesbll类的getcategories()方法配制它(见图5)。数据源配置向导会要求为listitem text和value选择字段。让dropdownlist 显示categoryname,categoryid作为value,见图6。
图 5: 创建 objectdatasource
图 6: 配置dropdownlist的 display 字段和value 字段
重复上面的步骤,为suppliers创建一个id为suppliers的dropdownlist 和一个名为suppliersdatasource的objectdatasource。
然后为discontinued state 添加一个checkbox ,为name添加一个textbox 。将他们的id分别设为discontinued和productname。为product name添加一个requiredfieldvalidator 确保用户必须提供这个值。
最后添加update 和cancel button。记得这两个button的commandname属性必须分别设为“update” 和“cancel”。你可以将编辑界面以你喜欢的方式展示。我选择使用和只读界面一样的界面来显示,见下面的声明代码和截图。
<edititemtemplate> <h4> <asp:label id="productnamelabel" runat="server" text='<%# eval("productname") %>' /> </h4> <table border="0"> <tr> <td class="productpropertylabel">name:</td> <td colspan="3" class="productpropertyvalue"> <asp:textbox runat="server" id="productname" width="90%" /> <asp:requiredfieldvalidator id="requiredfieldvalidator1" controltovalidate="productname" errormessage="you must enter a name for the product." runat="server">*</asp:requiredfieldvalidator> </td> </tr> <tr> <td class="productpropertylabel">category:</td> <td class="productpropertyvalue"> <asp:dropdownlist id="categories" runat="server" datasourceid="categoriesdatasource" datatextfield="categoryname" datavaluefield="categoryid" /> </td> <td class="productpropertylabel">supplier:</td> <td class="productpropertyvalue"> <asp:dropdownlist id="suppliers" datatextfield="companyname" datasourceid="suppliersdatasource" datavaluefield="supplierid" runat="server" /> </td> </tr> <tr> <td class="productpropertylabel">discontinued:</td> <td class="productpropertyvalue"> <asp:checkbox runat="server" id="discontinued" /> </td> <td class="productpropertylabel">price:</td> <td class="productpropertyvalue"> <asp:label id="unitpricelabel" runat="server" text='<%# eval("unitprice", "{0:c}") %>' /> </td> </tr> <tr> <td colspan="4"> <asp:button runat="server" id="updatebutton" commandname="update" text="update" /> <asp:button runat="server" id="cancelbutton" commandname="cancel" text="cancel" causesvalidation="false" /> </td> </tr> </table> <br /> <asp:objectdatasource id="categoriesdatasource" runat="server" oldvaluesparameterformatstring="original_{0}" selectmethod="getcategories" typename="categoriesbll"> </asp:objectdatasource> <asp:objectdatasource id="suppliersdatasource" runat="server" oldvaluesparameterformatstring="original_{0}" selectmethod="getsuppliers" typename="suppliersbll"> </asp:objectdatasource> </edititemtemplate>
图 7: 编辑界面和只读界面的展示差不多
第三步: 创建 editcommand和cancelcommand event handlers
现在在edititemtemplate里除了unitpricelabel外还没有绑定语法(从itemtemplate复制过来的代码)。在添加绑定语法前我们首先为datalist的editcommand和cancelcommand创建事件处理。editcommand事件处理的目标是为了将edit button被点击的item展示为编辑状态,而cancelcommand的目标是将datalist返回到编辑前状态。见下面的代码:
protected void products_editcommand(object source, datalistcommandeventargs e) { // set the datalist's edititemindex property and rebind the data products.edititemindex = e.item.itemindex; products.databind(); } protected void products_cancelcommand(object source, datalistcommandeventargs e) { // return to datalist to its pre-editing state products.edititemindex = -1; products.databind(); }
完成这些后,点击edit button会进入编辑界面,点击cancel button会返回只读模式。见图8。由于现在还没有为编辑界面添加绑定语法,textbox是空白的,checkbox 未被选中,两个dropdownlist里都是第一个item被选中。
图 8: 点击edit button显示编辑界面
第四步: 为编辑界面增加绑定语法
为了让编辑界面显示当前product的值,我们需要使用绑定语法将字段的值赋给web控件。绑定语法可以通过选择web控件的智能标签的“edit databindings”或者直接添加声明语法来实现。
将productname字段的值赋给productname textbox的text属性,categoryid和supplierid字段赋给categories和suppliers dropdownlist的selectedvalue属性,discontinued字段赋给discontinued checkbox的checked属性。完成这些后,浏览页面并点击edit button.见图9。
图 9: 点击edit button 显示编辑界面
第五步: 在updatecommand event handler保存用户的更改
当用户编辑product并点update button后,会postback并激发updatecommand事件。在事件处理里,我们需要从edititemtemplate里读出web控件的值,并和bll交互,然后更新数据库里的product。如我们在前面一章看到的那样,被更新的product的productid可以通过datakeys集合来获取。用户输入的值可以通过findcontrol("controlid")来编程获取,见下面的代码:
protected void products_updatecommand(object source, datalistcommandeventargs e) { // make sure the page is valid... if (!page.isvalid) return; // read in the productid from the datakeys collection int productid = convert.toint32(products.datakeys[e.item.itemindex]); // read in the product name and price values textbox productname = (textbox)e.item.findcontrol("productname"); dropdownlist categories = (dropdownlist)e.item.findcontrol("categories"); dropdownlist suppliers = (dropdownlist)e.item.findcontrol("suppliers"); checkbox discontinued = (checkbox)e.item.findcontrol("discontinued"); string productnamevalue = null; if (productname.text.trim().length > 0) productnamevalue = productname.text.trim(); int categoryidvalue = convert.toint32(categories.selectedvalue); int supplieridvalue = convert.toint32(suppliers.selectedvalue); bool discontinuedvalue = discontinued.checked; // call the productsbll's updateproduct method... productsbll productsapi = new productsbll(); productsapi.updateproduct(productnamevalue, categoryidvalue, supplieridvalue, discontinuedvalue, productid); // revert the datalist back to its pre-editing state products.edititemindex = -1; products.databind(); }
代码首先检查page.isvalid属性来确保所有的验证控件都返回合法值。如果page.isvalid为true,从datakeys集合里读出被编辑的product 的productid的值,并引用edititemtemplate里的web控件。然后将这些控件的值读到变量里,并传给updateproduct方法。完成更新后,datalist会返回到编辑前的状态。
注意:我省略了某章异常处理,目的是为了使本章的代码看起来目的性更强。你可以在完成本章后自己添加异常处理的功能作为练习。
第六步: 处理空的categoryid 和supplierid 值
northwind 数据库允许products表里的categoryid和supplierid列为空。然而我们的编辑界面目前还没有提供可选空值。如果我们试图编辑一个无论是categoryid还是supplierid为空的product,将会产生argumentoutofrangeexception异常。目前我们也没有将product的category或supplier的值从一个非空值转换为空值的方法。
为了在dropdownlists里添加空值,我们需要添加一个listitem。我将listitem里的text显示为"(none)",你可以将它赋为任何你希望的值(比如空字符串)。最后,记得将dropdownlists的appenddatabounditems设为true。如果你没有这么做,绑定到dropdownlist 的categories 和suppliers 会被添加的listitem覆盖。完成这些后,dropdownlists的标记语言看起来应该和下面差不多:
<asp:dropdownlist id="categories" datasourceid="categoriesdatasource" datatextfield="categoryname" datavaluefield="categoryid" runat="server" selectedvalue='<%# eval("categoryid") %>' appenddatabounditems="true"> <asp:listitem value=" selected="true">(none)</asp:listitem> </asp:dropdownlist> ... <asp:dropdownlist id="suppliers" datasourceid="suppliersdatasource" datatextfield="companyname" datavaluefield="supplierid" runat="server" selectedvalue='<%# eval("supplierid") %>' appenddatabounditems="true"> <asp:listitem value=" selected="true">(none)</asp:listitem> </asp:dropdownlist>
注意:为dropdownlist 添加listitems可以通过设计器或者声明语法来完成。当添加一个表示数据库空值的item时,要确保是通过声明语法来完成的。如果你使用设计器的listitem集合编辑器,当赋空字符串时,产生的声明语法会忽略value的设置,产生想<asp:listitem>(none)</asp:listitem>这样的语句。这个看起来并没有什么关系,但是缺少value会让dropdownlist 使用text属性的值作为value。这意味着当null listitem被选择时,“(none)” 会被赋给product的categoryid或supplierid字段,这会引起异常。而显式的将value设为“”,当null listitem被选择时一个空值会被赋给product的categoryid或supplierid字段。现在浏览该页。当编辑product时,注意categories和suppliers dropdownlists 开头都包含一个“(none)”选项。
图 10: categories 和suppliers dropdownlists包含 “(none)”
为了将“(none)” 保存为数据库null值,我们需要回到updatecommand事件处理。将categoryidvalue和supplieridvalue变量设为可为空的整型,并在dropdownlist的selectedvalue的值不为空字符串时才为它们赋值。
int? categoryidvalue = null; if (!string.isnullorempty(categories.selectedvalue)) categoryidvalue = convert.toint32(categories.selectedvalue); int? supplieridvalue = null; if (!string.isnullorempty(suppliers.selectedvalue)) supplieridvalue = convert.toint32(suppliers.selectedvalue);
完成这个后,如果用户在dropdownlist里选择“(none)” 时,一个空值将被传给updateproduct bll方法,它相当于数据库的null值。
总结
本章我们学习了如何为datalist创建一个更复杂的编辑界面,它包含三种不同的web控件— 一个textbox,两个dropdownlists和一个checkbox —并且包含验证控件。当创建编辑界面时,不管使用什么控件,步骤都是一样的:先为edititemtemplate添加控件;将字段值通过绑定语法赋给对应的web控件的属性;在updatecommand事件处理中编程访问web控件和它们的属性,并将值传给bll。
当创建编辑界面时,无论它仅仅是由textbox组成还是由不同的web控件组成,要确保正确的处理数据库null值。当处理null时,不仅需要在编辑界面正确显示已经存在的null值,还需要提供输入null值的方法。对datalists里的dropdownlists 来说,这通常意味着需要添加一个value属性被显式设为空字符串(value="")的listitem,然后在updatecommand事件处理里判断null listitem是否被选中。
祝编程快乐!
作者简介
本系列教程作者 scott mitchell,著有六本asp/asp.net方面的书,是4guysfromrolla.com的创始人,自1998年以来一直应用 微软web技术。大家可以点击查看全部教程《[翻译]scott mitchell 的asp.net 2.0数据教程》,希望对大家的学习asp.net有所帮助。