62GridView批量更新数据.docx
《62GridView批量更新数据.docx》由会员分享,可在线阅读,更多相关《62GridView批量更新数据.docx(26页珍藏版)》请在冰豆网上搜索。
62GridView批量更新数据
六十二:
GridView批量更新数据
导言:
在前面的教程,我们对数据访问层进行扩展以支持数据库事务.数据库事务确保一系列的操作要么都成功,要么都失败。
本文我们将注意力转到创建一个批更新数据界面.
在本文,我们将创建一个GridView控件,里面的每一行记录都可以进行编辑(见图1),因此我们没有必要多添加一列来包含Edit,Update,和Cancel按钮,而是在页面包含2个“UpdateProducts”按钮,被点击时,遍历所有的产品并对数据库进行更新.让我们开始吧.
图1:
GridView控件里的每一行记录都可以编辑
注意:
在第37章《DataList批量更新》里我们用一个DataList控件创建了一个批编辑界面,那篇文章与本文的区别之一在于本文使用GridView控件且使用了事务.
考察设置所有GridViewRows可编辑的步骤
就像在第16章《概述插入、更新和删除数据》考察的那样,GridView控件使用内置的编辑功能编辑每一行记录。
在其内部,GridView控件通过EditIndex属性来判断哪一行可编辑.一旦GridView绑定到数据源之后,它就逐行检查,看哪行的index值与EditIndex的值匹配,如果找到的话,该行就呈现为编辑界面.如果是绑定列(BoundFields),则呈现为一个TextBox,其Text值为对应的BoundField的DataField属性的值;如果是模板列(TemplateFields),那么呈现为EditItemTemplate而不是ItemTemplate.
我们知道当某个用户点击某行的Edit按钮时,页面产生回传,将该行的index值为GridView控件的EditIndex属性赋值,再重新绑定数据.当点击某行的Cancel按钮后产生页面回传,在重新绑定数据以前,将EditIndex属性设置为-1.因为,对GridView控件的rows而言,开始时Index值为0,而将EditIndex设为-1的话就变成只读模式了.
如果只对行进行编辑,EditIndex属性工作正常,但不支持批编辑。
要对GridView实施批编辑的话,我们必须使每行都呈现为编辑界面.为此,最简单的方法是将要编辑的列,转换为TemplateField,然后在ItemTemplate模板里创建编辑界面.在接下来的几步,我们将创建一个完整的可批编辑的GridView,在第一步,我们将创建一个GridView及其ObjectDataSource,并将BoundFields和CheckBoxField转换为TemplateFields。
在第二步和第三步,我们将编辑界面从ItemTemplates模板转移到EditItemTemplates.
第一步:
展示Product信息
首先,我们先创建一个显示产品信息的GridView.打开BatchData文件夹里的页面BatchUpdate.aspx,从工具箱拖一个GridView控件到页面,设ID值为ProductsGrid,从其智能标签里绑定到一个名为ProductsDataSource的ObjectDataSource,设其调用ProductsBLLclass类的GetProducts方法.
图2:
设置ObjectDataSourc调用ProductsBLLClass类
图3:
使用GetProducts方法获取产品信息
像GridView一样,该ObjectDataSource调用的方法也只能对每行记录起作用。
为了批更新记录,我们必须在ASP.NET页面的后台代码类里多写些代码,批处理数据并传递给BLL.因此,在ObjectDataSource的UPDATE,INSERT,和DELETE标签里选“(None)”.点Finish完成设置.
图4:
在UPDATE,INSERT,和DELETE标签里选“(None)”
完成设置后,ObjectDataSource控件的声明代码看起来和下面的差不多:
?
1
2
3
4
ObjectDataSourceID="ProductsDataSource"runat="server"
OldValuesParameterFormatString="original_{0}"
SelectMethod="GetProducts"TypeName="ProductsBLL">
ObjectDataSource>
完成设置后,VisualStudio会向GridView控件添加BoundFields以及一个CheckBoxField.就本文而言,我们只允许用户查看和编辑产品的名称、类别、价格、以及discontinued状态.将ProductName,CategoryName,UnitPrice和Discontinued以外的列全部删除,并分别将头3个列的HeaderText属性设置为“Product”,“Category”,“Price”。
最后,启用GridView的分页、排序功能.
此时,GridView控件含有3个BoundFields(ProductName,CategoryName,和UnitPrice)以及一个CheckBoxField(Discontinued).我们希望将这4个列转换为TemplateFields,并将编辑界面从TemplateField的EditItemTemplate模板转移到ItemTemplate模板.
注意:
我们在第20章《定制数据修改界面》里探讨了如何创建并定制TemplateFields.我们将BoundFields和CheckBoxField转换成TemplateFields,然后再在ItemTemplates模板里定制其编辑界面。
如果有什么不清楚的,可参考前面的文章.
从GridView的智能标签里,点“编辑列”,这将打开Fields对话框,然后选中每一列,点击“ConvertthisfieldintoaTemplateField”。
图5:
将现有的BoundFields和CheckBoxField转换为TemplateField
现在每一列都是TemplateField,我们将把编辑界面从EditItemTemplates模板转移到ItemTemplates模板.
第2步:
创建ProductName,UnitPrice,和Discontinued列的编辑界面
创建ProductName,UnitPrice,和Discontinued这3列的编辑界面是比较简单的,因为它们都在TemplateField的EditItemTemplate模板里定义好了的;而创建CategoryName的编辑界面比较麻烦,因为我们需要创建一个DropDownList控件来显示可用的categories,我们将在第3步实现.
我们首先创建ProductName的编辑界面。
在GridView控件的智能标签里点“编辑模板”,再点ProductNameTemplateField的EditItemTemplate项.选中其中的TextBox,将其复制、粘贴到ProductNameTemplateField的ItemTemplate模板.将该TextBox的ID属性设置为ProductName.
然后,在ItemTemplate模板里添加一个RequiredFieldValidator控件,以确保用户输入的产品name不为空.将其ControlToValidate属性设置为“ProductName”;ErrorMessage属性为“Youmustprovidetheproduct'sname.”;Text属性为“*”.添加完后,屏幕看起来应该像图6那样:
图6:
ProductNameTemplateField现在包含一个TextBox控件和一个RequiredFieldValidator控件
对UnitPrice编辑界面而言,先从EditItemTemplate模板里将TextBox拷贝到ItemTemplate模板.然后,在TextBox前面放置一个“$”符合,将其ID属性设置为“UnitPrice”;Columns属性设置为“8”.
然后再添加一个CompareValidator控件,确保用户输入的是大于或等于$0.00的货币值.设其ControlToValidate属性为“UnitPrice”;ErrorMessage属性为“Youmustenteravalidcurrencyvalue.Pleaseomitanycurrencysymbols.”;Text属性为“*”;Type属性为Currency;Operator属性为GreaterThanEqual;ValueToCompare属性为“0”.
图7:
添加一个CompareValidator控件以确保用户输入的是非负的货币值
对DiscontinuedTemplateField而言,直接使用已经在ItemTemplate模板里定义好了的CheckBox,只需要设其ID为“Discontinued”,Enabled属性为true.
第三步:
创建CategoryName的编辑界面
CategoryNameTemplateField的EditItemTemplate模板里的编辑界面里包含一个TextBox,其用来显示CategoryName列的值,我们要将其替换为一个DropDownList控件以显示categories.
注意:
在第20章《定制数据修改界面》里我们详细地探讨了如何用DropDownList控件来替换TextBox控件。
在此我们将过程一略而过,具体创建和设置DropDownList控件的细节可参考第20章.
从工具箱里拖一个DropDownList控件到CategoryNameTemplateField的ItemTemplate模板,设其ID为Categories.通常情况下,我们会通过其智能标签来定义DropDownLists的数据源,来创建一个新的ObjectDataSource.然而,这将在ItemTemplate模板里新添一个ObjectDataSource,后果是每一个GridViewrow都会创建一个ObjectDataSource实例.因此,我们在GridView的TemplateFields外创建ObjectDataSource.结束模板编辑,从工具箱拖一个ObjectDataSource到页面,放置在名为ProductsDataSource的ObjectDataSource控件下面。
将该新O用GetCategoriesMethodbjectDataSource命名为CategoriesDataSource,设其使用CategoriesBLLclass类的GetCategories方法.
图8:
设置该ObjectDataSource使用CategoriesBLL类
图9:
从GetCategories方法获取数据
因为该ObjectDataSource仅仅是用来检索数据,在UPDATE和DELETE标签里选“(None)”. 点Finish完成设置.
图10:
在UPDATE和DELETE标签里选“(None)”
完成设置后,CategoriesDataSource的声明代码看起来根下面的差不多:
?
1
2
3
4
ObjectDataSourceID="CategoriesDataSource"runat="server"
OldValuesParameterFormatString="original_{0}"
SelectMethod="GetCategories"TypeName="CategoriesBLL">
ObjectDataSource>
设置好后,返回CategoryNameTemplateField的ItemTemplate模板,在DropDownList的智能标签里点“ChooseDataSource”,在数据源设置向导里,在第一个下拉列表里选CategoriesDataSource;再下面的2个下拉列表里分别选CategoryName和CategoryID.
图11:
将DropDownList控件绑定到CategoriesDataSource
此时,DropDownList控件虽然列出了所有的categories,但对绑定到GridViewrow里的产品而言,其并没有自动的选择产品对应的category.为此,我们将DropDownList的SelectedValue值设置为产品的CategoryID值。
在DropDownList的智能标签里点“EditDataBindings”,并将SelectedValue属性赋值为CategoryID,如图12:
图12:
将产品的CategoryID值绑定到DropDownList的SelectedValue属性
还有最后一个问题,如果产品的CategoryID为空的话,对SelectedValue的数据绑定将会抛出异常.因为DropDownList只列出了那些指定了CategoryID值的产品,但不会列出那些CategoryID值为NULL的产品.怎样解决呢?
将DropDownList的AppendDataBoundIt属性设为rue,并向DropDownList新添加一个item,忽略其Value属性就像下面的声明代码那样:
?
1
2
3
4
5
DropDownListID="Categories"runat="server"AppendDataBoundItems="True"
DataSourceID="CategoriesDataSource"DataTextField="CategoryName"
DataValueField="CategoryID"SelectedValue='<%#Bind("CategoryID")%>'>
ListItemValue="">--SelectOne--
ListItem>
DropDownList>
我们注意到ListItemValue="">“--SelectOne--”里,将Value属性设置为一个空字符串.为什么要新添该item来处理值为NULL的情况?
为什么要将Value属性设置为一个空字符串呢?
这些疑问可参考前面第20章《定制数据修改界面》
注意:
这里有一个关乎性能的潜在问题要提一下。
因为每行记录都包含一个DropDownList,其数据源为CategoriesDataSource.每次登录页面时,都会调用CategoriesBLLclass类的GetCategories方法N次,这里N为GridView控件里行的数目.对GetCategories的N次调用就会导致对数据库的N次查询.我们可以对返回结果进行缓存以减轻对数据库造成的影响;至于方式嘛,可以运用per-requestcaching策略,也可以在缓存层CachingLayer里使用SQL高速缓存依赖性(SQLcachingdependency)或基于短时间缓存周期(averyshorttime-basedexpiry)的策略。
对per-requestcaching策略的更多信息可参考文章《HttpContext.Items–aPer-RequestCacheStore》(
第四步:
完善编辑界面
在浏览器里查看该页面,就像图13所示,每行都使用ItemTemplate模板,以包含其编辑页面。
图13:
每个GridViewRow都是可编辑的
不过仍有一些问题。
首先,UnitPrice值为四个小数点,为此,返回UnitPriceTemplateField的ItemTemplate模板,在TextBox的智能标签里点“EditDataBindings”,然后,将Text属性格式指定为number.
图14:
将Text格式指定为Number
然后,将Discontinued列里的checkbox控件居中(而不是居左),在GridView的智能标签里点“编辑列”,选取左边方框里的Discontinued,再在右边方框里的ItemStyle里将HorizontalAlign属性设置为Center,如图15所示:
图15:
将Discontinued列里的CheckBox居左
接下来在页面上添加一个ValidationSummar控件,将其ShowMessageBox属性设置为true;ShowSummary属性设置为false.同时再添加一个ButtonWeb控件,用来更新用户所做的更该。
特别的,添加2个,一个在GridView控件上面,一个在下面,将它们的Text属性设置为“UpdateProducts”.由于我们已经在TemplateFields模板定义了编辑界面,那么EditItemTemplates模板就显得多余了,将其删除.
完成上述修改后,你的页面声明代码看起来应该和下面的差不多:
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
ButtonID="UpdateAllProducts1"runat="server"Text="UpdateProducts"/>
GridViewID="ProductsGrid"runat="server"AutoGenerateColumns="False"
DataKeyNames="ProductID"DataSourceID="ProductsDataSource"
AllowPaging="True"AllowSorting="True">
TemplateFieldHeaderText="Product"SortExpression="ProductName">
TextBoxID="ProductName"runat="server"
Text='<%#Bind("ProductName")%>'>
TextBox>
RequiredFieldValidatorID="RequiredFieldValidator1"
ControlToValidate="ProductName"
ErrorMessage="Youmustprovidetheproduct'sname."
runat="server">*
RequiredFieldValidator>
TemplateField>
TemplateFieldHeaderText="Category"
SortExpression="CategoryName">
DropDownListID="Categories"runat="server"
AppendDataBoundItems="True"
DataSourceID="CategoriesDataSource"
DataTextField="CategoryName"
DataValueField="CategoryID"
SelectedValue='<%#Bind("CategoryID")%>'>
ListItem>--SelectOne--
ListItem>
DropDownList>
TemplateField>
TemplateFieldHeaderText="Price"
SortExpression="UnitPrice">
$TextBoxID="UnitPrice"runat="server"Columns="8"
Text='<%#Bind("UnitPrice","{0:
N}")%>'>
TextBox>
CompareValidatorID="CompareValidator1"runat="server"
ControlToValidate="UnitPrice"
ErrorMessage="Youmustenteravalidcurrencyvalue.
Pleaseomitanycurrencysymbols."
Operator="GreaterThanEqual"Type="Currency"
ValueToCompare="0">*
CompareValidator>
TemplateField>
TemplateFieldHeaderText="Discontinued"SortExpression="Discontinued">
CheckBoxID="Discontinued"runat="server"
Checked='<%#Bind("Discontinued")%>'/>
TemplateField>
GridView>
ButtonID="UpdateAllProducts2"runat="server"Text="UpdateProducts"/>
ObjectDataSourceID="ProductsDataSource"runat="server"
OldValuesParameterFormatString="original_{0}"
SelectMethod="GetProduct