Buttonrunat="server"ID="CancelButton"Text="Cancel"CommandName="Cancel"
CausesValidation="False"/>
创建EditCommand和CancelCommandEventHandlers
现在我们已经创建完ItemTemplate和EditItemTemplate,我们还需要加工eventhandlers来使ItemTemplate和EditItemTemplate之间可以切换。
象在前面讨论的一样,EditCommand事件处理里需要将EditItemIndex设为被点击Editbutton的product的index。
相反的,CancelCommand事件处理需要将EditItemIndex设为一个不存在的productindex(比如-1)。
C#
protectedvoidProducts_EditCommand(objectsource,DataListCommandEventArgse)
{
Products.EditItemIndex=e.Item.ItemIndex;
Products.DataBind();
}
protectedvoidProducts_CancelCommand(objectsource,DataListCommandEventArgse)
{
Products.EditItemIndex=-1;
Products.DataBind();
}
现在浏览该页。
开始会看到所有product的name和price显示,如图3。
点Editbutton会将选择的product展示为编辑模式。
现在Updatebutton还没有任何作用,Cancelbutton会将DataList返回到编辑前的状态,见图4。
图3:
所有Product的Name和Price被显示
图4:
点EditButton会将选择的Product的EditItemTemplate显示出来
第三步:
编辑时记下原始值
当使用开放并发控制编辑记录时,必须记下所有的编辑字段的原始值,这样用户完成编辑并点Update时,原始值可以用来和当前数据库的值进行比较以便判断其他用户是否有修改过数据。
GridView在编辑时会自动记下这些原始值。
而对DataList来说,需要我们编码来记下这些值。
GridView在postback过程里使用controlstate来记下这些原始值。
如综叙:
在DataList里编辑和删除数据 里讨论的,controlstate是和viewstate相似的用来在postback过程里记录值的方法。
controlstate不能被开发者禁用,而viewstate可以。
对DataList来说我们可以在ItemDataBoundeventhandler里手工的将原始值保存到viewstate里。
这样在postback过程里它们的值将被记住。
然后我们创建UpdateCommandeventhandler,将这些值传给BLL,它将只更新那些原始值和目前数据库值一样的那些product。
使用Page类的ViewStateproperty来写viewstate。
为ItemDataBound创建eventhandler,以便将原始值保存在viewstate里。
C#
protectedvoidProducts_ItemDataBound(objectsender,DataListItemEventArgse)
{
if(e.Item.ItemType==ListItemType.EditItem)
{
//Readtheediteditem'soriginalvaluesintothePage'sviewstate
TextBoxProductName=(TextBox)e.Item.FindControl("ProductName");
TextBoxUnitPrice=(TextBox)e.Item.FindControl("UnitPrice");
ViewState["original_productName"]=ProductName.Text;
ViewState["original_unitPrice"]=UnitPrice.Text;
}
}
最开始的条件保证了仅仅只将那些被编辑的item的原始值保存下来。
然后从被编辑的product的EditItemTemplate里读出ProductName和UnitPriceTextBox的值。
FindControl("controlID")方法用来引用TextBox。
这些值被保存在viewstate变量original_productName和original_unitPrice里。
我们的DataList只有两个可编辑的输入—product的name和price—因此我们仅仅只需要两个viewstate变量。
一般来说,对DataList里每个可编辑的项,使用FindControl("controlID")引用EditItemTemplate里的控件然后将值存在viewstate变量里。
第四步:
将原始值和新的值传给UpdateProduct
当用户点编辑按钮来更新product时,最新的数据从数据库获得并显示在选择的product的EditItemTemplate里。
这些值被保存在两个viewstate变量里。
在用户完成修改后点更新按钮,产生postback,激发UpdateCommand。
我们需要创建一个eventhandler来将新的product的值从EditItemTemplate里读出来,并和原始值一起传到BLL。
回到实现开放式并发里,我们学习了如何创建一个天生支持开放并发的类型化DataSet,并创建了一个这样的DAL(NorthwindOptimisticConcurrency.xsd)。
在那章里我们也创建了ProductsOptimisticConcurrencyBLL类来和NorthwindOptimisticConcurrency.xsdDAL一同工作。
在DataList的例子里我们将直接通过UpdateCommand和ProductsOptimisticConcurrencyBLL交互。
ProductsOptimisticConcurrencyBLL类的UpdateProduct方法有两个参数,分别表示新值和原始值。
因此UpdateCOmmand需要读更新的值和原始值,见下面的代码:
C#
protectedvoidProducts_UpdateCommand(objectsource,DataListCommandEventArgse)
{
//Makesurethevalidatorsonthepagearevalid
if(!
Page.IsValid)
return;
//ReadintheProductID
intproductID=Convert.ToInt32(Products.DataKeys[e.Item.ItemIndex]);
//Readintheoriginalvalues
stringoriginal_productName=null;
decimal?
original_unitPrice=null;
if(!
string.IsNullOrEmpty((string)ViewState["original_productName"]))
original_productName=(string)ViewState["original_productName"];
if(!
string.IsNullOrEmpty((string)ViewState["original_unitPrice"]))
original_unitPrice=
decimal.Parse((string)ViewState["original_unitPrice"],
System.Globalization.NumberStyles.Currency);
//Readinthenewvalues
stringnew_productName=null;
decimal?
new_unitPrice=null;
TextBoxProductName=(TextBox)e.Item.FindControl("ProductName");
TextBoxUnitPrice=(TextBox)e.Item.FindControl("UnitPrice");
if(ProductName.Text.Trim().Length>0)
new_productName=ProductName.Text.Trim();
if(UnitPrice.Text.Trim().Length>0)
new_unitPrice=
decimal.Parse(UnitPrice.Text.Trim(),
System.Globalization.NumberStyles.Currency);
//CalltheUpdateProductmethodinProductsOptimisticConcurrencyBLL
ProductsOptimisticConcurrencyBLLoptimisticProductsAPI
=newProductsOptimisticConcurrencyBLL();
optimisticProductsAPI.UpdateProduct(new_productName,new_unitPrice,
productID,original_productName,original_unitPrice,productID);
//ReturntheDataListtoitspre-editingstate
Products.EditItemIndex=-1;
Products.DataBind();
}
UpdateCommand首先判断page是否合法。
如我们在在编辑和插入界面里添加验证控件里讨论的那样,它在处理用户输入的数据前判断Page.IsValid是否返回True。
(由于客户端验证可能因为用户的浏览器不支持或禁用了JavaScrip而被跳过)
接着,被从DataKeys集合里读出编辑的行的ProductID。
然后从ViewState集合里将原始值读到局部变量里,然后从EditItemTemplate里的TextBox里读出新的值。
然后创建ProductsOptimisticConcurrencyBLL的实例,调用UpdateProduct方法,将原始值和新值传进去。
最后,更新完后,DataList返回到编辑前状态。
完成UpdateCommand事件处理后,浏览该页。
点Chai的编辑按钮,然后将price修改为$20.00.由于你是唯一一个编辑这条记录的,因此更新会成功,你的感觉和没有使用开放并发控制的更新DataList是一样的。
打开另一个浏览器窗口并浏览OptimisticConcurrency.aspx页。
在两个页面里都点Chai的编辑按钮。
在第一个页里将product的name改为“ChaiTea”然后点更新。
这样会更新数据库并返回到DataList的编辑前状态,这时productname会显示为"ChaiTea"。
在第二个页里(它将继续保存编辑状态)将price改为$25.50,并将productname保留为“Chai”。
点击更新按钮,这时会抛出DBCo