在ASP.NET 2.0中操作数据之三十六:在DataList里编辑和删除数据概述
导言
概述插入、更新和删除数据 里我们已经学习了如何使用gridview等控件来插入,更新删除数据。通过objectdatasource和其它数据控件仅仅只需要在智能标签里勾一下checkbox就完成了,不需要写任何代码。而datalist没有这些内置的功能。我们可以使用1.x 里的方法来实现这些功能。在本章我们将看到,datalist提供了一些事件和属性来完成我们的目的,为此我们需要写一些代码。
本章我们首先学习如何创建一个支持编辑和删除数据的datalist。后面的教程里我们将学习一些高级的编辑和删除方法,包括验证,dal和bll的异常处理等。
注意:和datalist一样,repeater也不提供内置的这些功能。而且repeater里没有datalist里提供的那些事件和属性。因此本章和后面的几章我们仅仅只讨论datalist。
第一步: 创建编辑和删除教程页
首先创建本章和后面几章需要用到的页。添加一个名为editdeletedatalist的文件夹。然后添加下面的页。确保每页都包含了site.master。
default.aspx
basics.aspx
batchupdate.aspx
errorhandling.aspx
uivalidation.aspx
customizedui.aspx
optimisticconcurrency.aspx
confirmationondelete.aspx
userlevelaccess.aspx
图 1: 添加页
和别的文件夹一样,default.aspx列出教程章节。记得sectionleveltutoriallisting.ascx用户控件提供了这个功能。从解决方案里将它拖到我们的页里。
图 2: 添加sectionleveltutoriallisting.ascx 用户控件
最后将这些页添加到web.sitemap里。在master/detail reports with the datalist and repeater<sitemapnode>之后添加下面的标记:
<sitemapnode title="editing and deleting with the datalist" description="samples of reports that provide editing and deleting capabilities" url="~/editdeletedatalist/default.aspx" > <sitemapnode title="basics" description="examines the basics of editing and deleting with the datalist control." url="~/editdeletedatalist/basics.aspx" /> <sitemapnode title="batch update" description="examines how to update multiple records at once in a fully-editable datalist." url="~/editdeletedatalist/batchupdate.aspx" /> <sitemapnode title="error handling" description="learn how to gracefully handle exceptions raised during the data modification workflow." url="~/editdeletedatalist/errorhandling.aspx" /> <sitemapnode title="adding data entry validation" description="help prevent data entry errors by providing validation." url="~/editdeletedatalist/uivalidation.aspx" /> <sitemapnode title="customize the user interface" description="customize the editing user interfaces." url="~/editdeletedatalist/customizedui.aspx" /> <sitemapnode title="optimistic concurrency" description="learn how to help prevent simultaneous users from overwritting one another s changes." url="~/editdeletedatalist/optimisticconcurrency.aspx" /> <sitemapnode title="confirm on delete" description="prompt a user for confirmation when deleting a record." url="~/editdeletedatalist/confirmationondelete.aspx" /> <sitemapnode title="limit capabilities based on user" description="learn how to limit the data modification functionality based on the user s role or permissions." url="~/editdeletedatalist/userlevelaccess.aspx" /> </sitemapnode>
更新了web.sitemap后,浏览一下。
图 3: 站点导航现在包含了编辑和删除datalist 教程
第二步: 探讨更新和删除数据所要用到的技术
使用gridview来编辑和删除数据之所以很简单,是因为gridview和objectdatasource在底层非常一致。如研究插入、更新和删除的关联事件里所讨论的,当更新按钮被点击时,gridview自动将字段的值赋给objectdatasource的updateparameters集合,然后激发objectdatasource的update()方法。我们现在需要确保将合适的值赋给objectdatasource的参数,然后调用update()方法。datalist提供了以下的属性和事件来帮助我们完成这些:
datakeyfield property — 更新或删除时,我们需要唯一确定datalist里的每个item。将这个属性设为显示的数据的主健。这样做会产生datalist的 datakeys collection ,每个item都有一个指定的 datakeyfield .
editcommand event — 当commandname属性设为“edit”的button, linkbutton, 或 imagebutton 被点时激发.
cancelcommand event — 当commandname属性设为“cancel”的button, linkbutton, 或 imagebutton 被点时激发. updatecommand event — 当commandname属性设为“update”的button, linkbutton, 或 imagebutton 被点时激发. deletecommand event — 当commandname属性设为“delete”的button, linkbutton, 或 imagebutton 被点时激发.
使用以上的属性和事件,我们有四种方法来更新和删除数据:
使用asp.net 1.x 的技术— datalist先于asp.net 2.0 和objectdatasources 存在,可以直接通过编程来实现编辑和删除。这种方法需要我们在显示数据或者更新删除记录时,直接在bll层将数据绑定到datalist。
使用一个单独的objectdatasource 来实现选择,更新和删除 — datalist没有gridview内置的编辑删除功能,并不意味着我们不能添加这些功能。我们象在gridview的例子里那样,使用 objectdatasource,但是在设置objectdatasource的参数并调用update()方法时,需要为datalist的updatecommand事件创建一个 event handler。 using an objectdatasource control for selecting, but updating and deleting directly against the bll — 使用第二种方法时我们需要为updatecommand事件和参数赋值等写一些代码。其实我们可以用objectdatasource来实现selecting ,更新和删除直接调用bll(象第一种方法)。我的意见是,直接调用bll会使代码可读性更好。 使用多个objectdatasources —前面的三种方法都需要一些代码。如果你宁愿坚持使用尽可能多的声明语法的话,最后一种方法是使用多个objectdatasources 。第一个objectdatasource 从bll获取数据,并绑定到 datalist. 为更新添加另一个 objectdatasource, 直接添加到 datalist的 edititemtemplate.同样对删除也是如此。三个 objectdatasource通过 controlparameters 声明语法直接将参数绑定到objectdatasource 的参数 (而不是在 datalist的 updatecommand event handler编程处理). 这种方法也需要一些编码 — 我们需要调用 objectdatasource内置的 update() 或 delete() — 但是比起其它三种方法,代码少的多。这种方法的劣势是多个 objectdatasources 使页面看起来混乱。
我喜欢选择第一种方法,因为它提供了更好的可扩展性,而设计datalist的本意就是使用这种方式。当扩展datalist使它和asp.net 2.0的数据源控件一起工作时,它没有“正式”的asp.net 2.0数据控件( gridview, detailsview, 和formview)的那些可扩展性或特性。当然其它方法也不是没有优点。
这几章关于编辑和删除的教程会使用objectdatasource 来显示数据,然后直接调用bll来实现编辑和删除(第三种方法)
第三步: 添加datalist并配置它的objectdatasource
本章我们将创建一个datalist用来列出product的信息,并提供用户编辑其name和price,删除的功能。我们使用objectdatasource来显示数据,调用bll来实现更新和删除的功能。首先我们来实现一个只读的显示product的页。由于在前面的教程里已经实现过这样的功能,在这里我们很快带过。
打开editdeletedatalist文件夹下的basics.aspx页,添加一个datalist。然后通过智能标签创建一个objectdatasource。在select标签里使用productsbll类的getproducts()方法配置它。
图 4: 使用productbll类配置objectdatasource
图 5: 选择getproducts()
在insert,update和delete标签里都选择none。
图 6: 在insert, update, 和delete 标签里选择(none)
完成配置后回到设计界面。如我们在以前的例子里看到的那样,visual studio 会自动创建itemtemplate,显示数据。将itemtemplate改为只显示product的name和price。然后将repeatcolumns设为2。
注意:象以前讨论的那样,当使用objectdatasource修改数据时,我们在声明标记里需要移除oldvaluesparameterformatstring (或重新设为缺省值,{0})。而本章objectdatasource仅仅只用来获取数据,因此不需要那样做(当然那样做了也没关系)。完成后你的页代码看起来应该和下面差不多:
<asp:datalist id="datalist1" runat="server" datakeyfield="productid" datasourceid="objectdatasource1" repeatcolumns="2"> <itemtemplate> <h5> <asp:label runat="server" id="productnamelabel" text='<%# eval("productname") %>'></asp:label> </h5> price: <asp:label runat="server" id="label1" text='<%# eval("unitprice", "{0:c}") %>' /> <br /> <br /> </itemtemplate> </asp:datalist> <asp:objectdatasource id="objectdatasource1" runat="server" selectmethod="getproducts" typename="productsbll" oldvaluesparameterformatstring="original_{0}"> </asp:objectdatasource>
浏览一下页面。如图7,datalist以两列的方式显示product的name和price。
图 7: datalist显示products的 names and prices
注意:datalist有一些属性是编辑和删除需要用到的,这些值都存在view state里。因此创建支持编辑和删除功能的datalist时,datalist的view state需要开启。
聪明的读者应该记起来在创建可编辑的gridview,detailsviews和formviews的时候,view state是禁用的。这是因为asp.net 2.0 控件包含了control state,它在postback时状态是连续的。
在gridview里禁用了view state仅仅只是忽略了无关紧要的状态信息,但是维持了control state(它包含了编辑和删除需要的状态)。而datalist是 asp.net 1.x时代创建的,并没有使用control state,因此view state必须开启。更多的control state的目的以及和view state的区别信息见control state vs. view state
第四步: 添加一个编辑界面
gridview控件是由字段集合组成的(boundfields, checkboxfields, templatefields等)。这些字段能根据模式来调整标记。比如,在只读模式下,boundfield 将字段值显示为文本,而在编辑模式下,它将显示为一个textbox,这个textbox的text属性被赋予字段的值。
另一方面,datalist用template来展现它的item。只读的item用itemtemplate来展现。而当在编辑模式下时,item用edititemtemplate来展示。现在我们的datalist只有一个itemtemplate。我们需要添加一个edititemtemplate来支持编辑功能。本章我们使用textbox来编辑product的name和price。可以通过声明语言或设计视图(datalist的智能标签的edittemplate选项)来创建edititemtemplate。
图 8:选择edititemtemplate
然后输入“product name:” 和“price:” ,再拖两个textbox到edititemtemplate。将textbox的id属性设为productname和unitprice。
图 9: 添加textbox
我们需要将product的字段绑定到关联的textbox。在textbox的智能标签上点击edit databindings,然后将text属性和适当的字段关联。见图10。
注意:将unitprice绑定到textbox时,你可以用({0:c})将它 格式化为货币值,或用({0:n})表示为普通数字,或者不格式化。
图 10:绑定字段到textboxes
注意:图10里的edit databindings对话框里并不包含“双向数据绑定”的checkbox,而在编辑gridview或detailsview里的templatefield,或者formview里的template里时是有这个checkbox的。双向数据绑定允许在插入或更新数据时,输入控件的值自动赋给相关联的objectdatasource的insertparameters或updateparameters。datalist并不支持双向绑定—我们在后面会看到,在用户作出更改,准备更新数据时,我们需要编程将textbox的text的值传给productsbll类的updateproduct方法。
最后我们在edititemtemplate里加入update和cancel按钮。象前面看到的那样,当设置了commandname的repeater或datalist里的button,linkbutton或imagebutton被点击时,repeater或datalist的itemcommand事件被激发。对datalist来说,如果commandname设为某个值,另外一个事件也会被激发,如下:
“cancel” — 激发 cancelcommand event
“edit” — 激发 editcommand event
“update” — 激发updatecommand event
记住除了itemcommand外,这些事件会激发。
为edititemtemplate添加两个button,一个commandname设为"update",一个设为"cancel"。完成后,设计界面看起来应该和下面差不多:
图 11: 为 edititemtemplate 添加update 和cancel 按钮
你的标记语言看起来应该和下面差不多:
<asp:datalist id="datalist1" runat="server" datakeyfield="productid" datasourceid="objectdatasource1" repeatcolumns="2"> <itemtemplate> <h5> <asp:label runat="server" id="productnamelabel" text='<%# eval("productname") %>' /> </h5> price: <asp:label runat="server" id="label1" text='<%# eval("unitprice", "{0:c}") %>' /> <br /> <br /> </itemtemplate> <edititemtemplate> product name: <asp:textbox id="productname" runat="server" text='<%# eval("productname") %>' /><br /> price: <asp:textbox id="unitprice" runat="server" text='<%# eval("unitprice", "{0:c}") %>' /><br /> <br /> <asp:button id="updateproduct" runat="server" commandname="update" text="update" /> <asp:button id="cancelupdate" runat="server" commandname="cancel" text="cancel" /> </edititemtemplate> </asp:datalist>
第五步: 添加进入编辑模式的入口
现在我们的datalist有一个编辑界面了。然而现在还没有办法来体现出用户需要编辑product信息。我们需要为每个product加一个edit button,当点击时,将datalist item展示为编辑模式。同样的可以通过设计器或直接声明代码来添加。确保将edit button的commandname属性设为"edit".添加完后,浏览一下页面。
图 12: 添加edit buttons
点击button会引起postback,但是并没有进入product的编辑模式。为了完成这个,我们需要:
设置datalist的 edititemindex property 为 被点击了edit button的 datalistitem的 index .
重新绑定数据到 datalist. 当 datalist 重新展现时, 和datalist的edititemindex相关的datalistitem 会展现edititemtemplate.
由于在点edit button时,datalist的editcommand事件被激发,使用下面的代码创建一个editcommand event handler :
protected void datalist1_editcommand(object source, datalistcommandeventargs e)
{
// set the datalist's edititemindex property to the
// index of the datalistitem that was clicked
datalist1.edititemindex = e.item.itemindex;
// rebind the data to the datalist
datalist1.databind();
}
editcommand event handler 的第二个参数类型为datalistcommandeventargs ,它是被点击的edit button的datalistitem的引用(e.item).首先设置datalist的edititemindex为想编辑的datalistitem的itemindex,然后重新绑定数据。完成后再浏览页面。点edit button,现在product变成了可编辑的。见图13。
图 13: 点edit button 使product 可编辑
第六步: 保存用户的更改
现在点product的update或cancel button不会有任何反应。为了完成目标我们需要为datalist的updatecommand和cancelcommand创建event handler。首先创建cancelcommand event handler,它在product的cancel button点击时执行,使datalist返回编辑之前的状态。使datalist以只读模式展示item,我们需要:
设置datalist的 edititemindex property 为一个不存在的datalistitem index -1是一个好的选择。(由于datalistitem index从0开始) 重新绑定数据到datalist。由于没有datalistitem itemindex和datalist的edititemindex关联,整个datalist会展现为只读模式。 这些可以通过以下代码完成:
protected void datalist1_cancelcommand(object source, datalistcommandeventargs e) { // set the datalist's edititemindex property to -1 datalist1.edititemindex = -1; // rebind the data to the datalist datalist1.databind(); }
现在点击cancel button会返回到datalist编辑前的状态。
最后我们来完成updatecommand event handler,我们需要:
编程获取用户输入的product name和price,还有productid.
调用productsbll类里的合适的updateproduct重载方法.
设置datalist的edititemindex property 为一个不存在的datalistitem index. -1 是一个好的选择。
重新帮顶数据。
第一和第二步负责保存用户的更改。第三步返回到datalist编辑前的状态(和cancelcommand event handler一样)。
我们需要使用findcontrol方法来获取product的name和price(当然包括productid)。当最初将objectdatasource绑定到datalist时,visual studio 将datalist的datakeyfield 属性赋为数据源的主键值(productid)。这个值可以通过datalist的datakey集合来获取。花点时间验证一下datakeyfield 是否设置为productid。
下面的代码完成了上面的功能:
protected void datalist1_updatecommand(object source, datalistcommandeventargs e) { // read in the productid from the datakeys collection int productid = convert.toint32(datalist1.datakeys[e.item.itemindex]); // read in the product name and price values textbox productname = (textbox)e.item.findcontrol("productname"); textbox unitprice = (textbox)e.item.findcontrol("unitprice"); string productnamevalue = null; if (productname.text.trim().length > 0) productnamevalue = productname.text.trim(); decimal? unitpricevalue = null; if (unitprice.text.trim().length > 0) unitpricevalue = decimal.parse(unitprice.text.trim(), system.globalization.numberstyles.currency); // call the productsbll's updateproduct method... productsbll productsapi = new productsbll(); productsapi.updateproduct(productnamevalue, unitpricevalue, productid); // revert the datalist back to its pre-editing state datalist1.edititemindex = -1; datalist1.databind(); }
首先从datakeys集合里读出product的productid。然后将两个textbox的text属性存起来。我们用decimal.parse() 方法去读unitprice textbox的值,以便在这个值有货币符号时可以正确的转换。
注意:只有在textbox的text指定了值的情况下,productnamevalue和unitpricevalue变量才会被赋值。否则,会在更新数据时使用一个null值。也就是说我们的代码会将空字符串转换为null值,而在gridview,detailsview和formview控件的编辑界面里null是缺省值。获取值后,调用productsbll类的updateproduct方法,将product的name,price和productid传进去。使用和cancelcommand event handler里同样的逻辑返回到datalist编辑前状态。完成了editcommand,cancelcommand和updatecommand event handler后,用户可以编辑product的name和price了。见图14。
图 14: 浏览页时所有的products都是只读模式
图 15: 点击edit button
图 16: 改变值后,点击 update返回只读模式
第七步: 增加删除功能
增加删除功能的步骤和增加编辑功能差不多。简单来说我们需要在itemtemplate里添加一个delete button,当点击时:
从datakeys 集合里读取关联的proudct的productid .
调用productsbll class的deleteproduct 方法执行删除操作.
重新绑定数据到 datalist.
首先来增加一个delete button.
当点击一个commandname为“edit”, “update”, 或“cancel”的button时,会激发datalist的itemcommand事件和另外一个事件(比如,使用“edit”时editcommand 事件会被激发)。同样的,commandname为“delete”时会激发deletecommand 事件(和itemcommand一起)。
在edit button后面增加一个delete button ,将commandname属性设为“delete”. 完成后声明代码如下:
<itemtemplate> <h5> <asp:label runat="server" id="productnamelabel" text='<%# eval("productname") %>' /> </h5> price: <asp:label runat="server" id="label1" text='<%# eval("unitprice", "{0:c}") %>' /> <br /> <asp:button runat="server" id="editproduct" commandname="edit" text="edit" /> <asp:button runat="server" id="deleteproduct" commandname="delete" text="delete" /> <br /> <br /> </itemtemplate>
然后为datalist的deletecommand事件创建一个event handler,见下面的代码:
protected void datalist1_deletecommand(object source, datalistcommandeventargs e) { // read in the productid from the datakeys collection int productid = convert.toint32(datalist1.datakeys[e.item.itemindex]); // delete the data productsbll productsapi = new productsbll(); productsapi.deleteproduct(productid); // rebind the data to the datalist datalist1.databind(); }
点击delete button 会引起postback,并激发datalist的deletecommand事件。在事件处理中,被点击的product的productid的值通过datekeys集合来获取。然后,调用productsbll类的deleteproduct方法来删除product。删除product后,要将数据重新绑定到datalist(datalist1.databind()),否则datalist里还会看到刚才删除的product。
总结
通过少量的代码,datalist也可以拥有gridview的编辑删除功能。在本章我们学习了如何创建一个两列的显示product的页,并且可以编辑name和price,删除product。增加编辑和删除功能需要在itemtemplate和edititemtemplate里增加合适的控件,创建对应的事件处理,读取用户的输入和主键值,然后调用bll。
虽然我们为datalist增加了基本的编辑和删除功能,它还是缺少一些高级特性。比如,没有输入字段验证- 如果用户输入的price太“贵”,decimal.parse在转换为decimal时会抛出异常。同样的,如果在更新数据时,bll或dal里有异常,用户会看到系统错误。在删除时没有任何确认,很可能会误删数据。在后面的教程里我们会学习如何改善这些问题。
祝编程快乐!
作者简介
本系列教程作者 scott mitchell,著有六本asp/asp.net方面的书,是4guysfromrolla.com的创始人,自1998年以来一直应用 微软web技术。大家可以点击查看全部教程《[翻译]scott mitchell 的asp.net 2.0数据教程》,希望对大家的学习asp.net有所帮助。