在ASP.NET 2.0中操作数据之五十一:从GridView的页脚插入新记录
导言:
正如教程《》里探讨过的一样, gridview, detailsview和formview web控件都有内置的修改数据的功能。当声明绑定到数据源控件时,可以快速而方便地修改数据——甚至不用写一行代码。不幸的是,只有detailsview和formview控件提供了内置的插入、编辑、删除功能,而 gridview控件只支持编辑、删除功能。不过,稍许努力,我们就能使gridview控件包含一个插入界面。
为了给gridview添加插入功能,我们要决定如何添加新记录:创建插入界面,编码插入数据。在本教程,我们将为gridview的页脚行(footer row )添加插入界面(见图1)。其中每一列包含相应的用户界面元件(比如在textbox里输入产品名称,在dropdownlis里选择供应商等等),同时我们需要一个"add"按钮,当点击时,发生页面回传,将新记录添加到表products里。
图1:页脚行提供了一个添加新记录的界面
第一步:在gridview控件里展示产品信息
首先添加一个展示产品的gridview控件。打开enhancedgridview文件夹里的insertthroughfooter.aspx页面,在上面添加一个gridview控件,设其id为products,然后,在其智能标签里绑定到一个名为productsdatasource的objectdatasource 。
图2:创建一个名为productsdatasource的新objectdatasource
设置该objectdatasource调用productsbll类的getproducts()方法获取产品信息。在本教程里,我们只关注于添加插入功能,与编辑和删除无关。所以,确保在“插入”选项卡里选addproduct()方法。而在“编辑”和“删除”里选“(none)”。
图3:将 objectdatasource的insert()方法设置为addproduct()
图4:在update和delete选项里选“(none)”
完成设置后,visual studio会自动添加相关列。现在,我们暂时不管这些列,在教程后续部分,我们将移除一些列,因为在添加新记录时我们不需指定这些列的值。
因为数据库中大概有80个产品,所以我们最好还是启用分页功能,以便使插入界面更直观、更易操作。回到页面,在gridview的智能标签里启用分页。
现在,gridview和objectdatasource的声明代码看起来和下面的差不多:
<asp:gridview id="products" runat="server" autogeneratecolumns="false" datakeynames="productid" datasourceid="productsdatasource" allowpaging="true" enableviewstate="false"> <columns> <asp:boundfield datafield="productid" headertext="productid" insertvisible="false" readonly="true" sortexpression="productid" /> <asp:boundfield datafield="productname" headertext="productname" sortexpression="productname" /> <asp:boundfield datafield="supplierid" headertext="supplierid" sortexpression="supplierid" /> <asp:boundfield datafield="categoryid" headertext="categoryid" sortexpression="categoryid" /> <asp:boundfield datafield="quantityperunit" headertext="quantityperunit" sortexpression="quantityperunit" /> <asp:boundfield datafield="unitprice" headertext="unitprice" sortexpression="unitprice" /> <asp:boundfield datafield="unitsinstock" headertext="unitsinstock" sortexpression="unitsinstock" /> <asp:boundfield datafield="unitsonorder" headertext="unitsonorder" sortexpression="unitsonorder" /> <asp:boundfield datafield="reorderlevel" headertext="reorderlevel" sortexpression="reorderlevel" /> <asp:checkboxfield datafield="discontinued" headertext="discontinued" sortexpression="discontinued" /> <asp:boundfield datafield="categoryname" headertext="categoryname" readonly="true" sortexpression="categoryname" /> <asp:boundfield datafield="suppliername" headertext="suppliername" readonly="true" sortexpression="suppliername" /> </columns> </asp:gridview> <asp:objectdatasource id="productsdatasource" runat="server" insertmethod="addproduct" oldvaluesparameterformatstring="original_{0}" selectmethod="getproducts" typename="productsbll"> <insertparameters> <asp:parameter name="productname" type="string" /> <asp:parameter name="supplierid" type="int32" /> <asp:parameter name="categoryid" type="int32" /> <asp:parameter name="quantityperunit" type="string" /> <asp:parameter name="unitprice" type="decimal" /> <asp:parameter name="unitsinstock" type="int16" /> <asp:parameter name="unitsonorder" type="int16" /> <asp:parameter name="reorderlevel" type="int16" /> <asp:parameter name="discontinued" type="boolean" /> </insertparameters> </asp:objectdatasource>
图5:在一个启用了分页功能的gridview里,显示产品的所有数据项
第2步:添加一个页脚行
gridview控件包含页眉行、数据行和页脚行。gridview控件showheader和showfooter属性决定了是否显示页眉行和页脚行。如果要显示页脚行,我们需要将 showfooter属性设置为true。如图6所示:
图6:设showfooter属性为true,添加页脚行
我们注意到页脚行的背景色是深红色,这是由于我们在教程《使用objectdatasource展现数据》里创建了一个名为datawebcontrols的主题,并将其应用为所有的页面底色。特别的,皮肤文件gridview.skin设置footerstyle属性使用footerstyle css ,其代码如下:
.footerstyle { background-color: #a33; color: white; text-align: right; }
注意:在以前的教程我们提到过使用gridview的页脚行。如果不清楚的话,请查阅教程第15章《在gridview的页脚中显示统计信息》
设置showfooter属性为true后,在浏览器里观看效果。当前的页脚行并不包含任何的文字或web控件。在第3步,我们将修改其包含相应的插入界面。
图7:页脚行显示为空白
第3步:自定义页脚行
回顾教程《在gridview控件中使用templatefield》,在那篇教程我们探讨了如何对gridview的某一列使用templatefields(而不是boundfields或checkboxfields) ,从而实现自定义显示样式;而在教程《》里我们看到如何在gridview里使用templatefields定制编辑界面。一个templatefield是由诸如itemtemplate、edititemtemplate等模板构成的。比如,itemtemplate模板显示的数据行为只读状态;而edititemtemplate模板定制了一个编辑行界面。
除了itemtemplate、edititemtemplate等模板外,templatefield也包含一个名为footertemplate的模板,它为容器指定页脚行。所以我们可以在footertemplate模板里添加插入界面要用到的web控件。让我们开始吧,首先,我们将gridview控件里的所有列转换成templatefields。在gridview控件的智能标签里点击“编辑列”,在左边选中每个域,再点击“convert this field into a templatefield” 。
图8:将每个域转换为一个templatefield
点击“convert this field into a templatefield”的话,将当前类型的域转换成相应的templatefield。比如,每个boundfield将转换成这样的templatefield,它的itemtemplate包含一个label控件来显示相应的数据域;它的edititemtemplate使用一个textbox控件来显示相应的数据域。例如,在这里,名为productname的boundfield将被转换为如下所示的templatefield :
<asp:templatefield headertext="productname" sortexpression="productname"> <edititemtemplate> <asp:textbox id="textbox1" runat="server" text='<%# bind("productname") %>'></asp:textbox> </edititemtemplate> <itemtemplate> <asp:label id="label2" runat="server" text='<%# bind("productname") %>'></asp:label> </itemtemplate> </asp:templatefield>
同样的,名为discontinued的checkboxfield转换为templatefield后,其itemtemplate 和 edititemtemplate 模板都将包含一个checkbox web控件(只是itemtemplate模板里的checkbox不可用);而处于“只读”状态的productid boundfield转换成templatefield后,其itemtemplate 和 edititemtemplate 模板都包含一个label控件。简而言之,将gridview里的某一列转换为一个 templatefield,是定制自定义模板的一种又快又容易的方法,且不会丧失该列应有的功能。
由于我们不需要gridview支持编辑功能,将每个templatefield的edititemtemplate模板删除,只留下itemtemplate模板。完成后, gridview的代码看起来应和下面的差不多:
<asp:gridview id="products" runat="server" autogeneratecolumns="false" datakeynames="productid" datasourceid="productsdatasource" allowpaging="true" enableviewstate="false" showfooter="true"> <columns> <asp:templatefield headertext="productid" insertvisible="false" sortexpression="productid"> <itemtemplate> <asp:label id="label1" runat="server" text='<%# bind("productid") %>'></asp:label> </itemtemplate> </asp:templatefield> <asp:templatefield headertext="productname" sortexpression="productname"> <itemtemplate> <asp:label id="label2" runat="server" text='<%# bind("productname") %>'></asp:label> </itemtemplate> </asp:templatefield> <asp:templatefield headertext="supplierid" sortexpression="supplierid"> <itemtemplate> <asp:label id="label3" runat="server" text='<%# bind("supplierid") %>'></asp:label> </itemtemplate> </asp:templatefield> <asp:templatefield headertext="categoryid" sortexpression="categoryid"> <itemtemplate> <asp:label id="label4" runat="server" text='<%# bind("categoryid") %>'></asp:label> </itemtemplate> </asp:templatefield> <asp:templatefield headertext="quantityperunit" sortexpression="quantityperunit"> <itemtemplate> <asp:label id="label5" runat="server" text='<%# bind("quantityperunit") %>'></asp:label> </itemtemplate> </asp:templatefield> <asp:templatefield headertext="unitprice" sortexpression="unitprice"> <itemtemplate> <asp:label id="label6" runat="server" text='<%# bind("unitprice") %>'></asp:label> </itemtemplate> </asp:templatefield> <asp:templatefield headertext="unitsinstock" sortexpression="unitsinstock"> <itemtemplate> <asp:label id="label7" runat="server" text='<%# bind("unitsinstock") %>'></asp:label> </itemtemplate> </asp:templatefield> <asp:templatefield headertext="unitsonorder" sortexpression="unitsonorder"> <itemtemplate> <asp:label id="label8" runat="server" text='<%# bind("unitsonorder") %>'></asp:label> </itemtemplate> </asp:templatefield> <asp:templatefield headertext="reorderlevel" sortexpression="reorderlevel"> <itemtemplate> <asp:label id="label9" runat="server" text='<%# bind("reorderlevel") %>'></asp:label> </itemtemplate> </asp:templatefield> <asp:templatefield headertext="discontinued" sortexpression="discontinued"> <itemtemplate> <asp:checkbox id="checkbox1" runat="server" checked='<%# bind("discontinued") %>' enabled="false" /> </itemtemplate> </asp:templatefield> <asp:templatefield headertext="categoryname" sortexpression="categoryname"> <itemtemplate> <asp:label id="label10" runat="server" text='<%# bind("categoryname") %>'></asp:label> </itemtemplate> </asp:templatefield> <asp:templatefield headertext="suppliername" sortexpression="suppliername"> <itemtemplate> <asp:label id="label11" runat="server" text='<%# bind("suppliername") %>'></asp:label> </itemtemplate> </asp:templatefield> </columns> </asp:gridview>
现在, 每个gridview列都已经转换成一个templatefield,我们在其footertemplate里添加适当的插入界面。然而,有些列没有插入界面(比如productid),其它列的templatefield模板将包含web控件,供用户输入产品信息。
在gridview的智能标签里点击“edit templates”,从下拉列表里选择某列的 footertemplate模板,从工具箱里拖一个适当的控件到页面上。
图9:在每列的footertemplate里添加适当的插入界面。
下面列出了gridview的所有列,并指定每列添加哪些插入界面:
productid – 无
productname –添加一个textbox,id为newproductname;再添加一个
requiredfieldvalidator控件,防止用户未输入产品名。
supplierid –无
categoryid – 无
quantityperunit – 添加一个textbox,id为newquantityperunit
unitprice – 添加一个textbox,id为newunitprice,再添加一个comparevalidator控件,确保用户输入的是货币值,且>=0
unitsinstock –添加一个textbox,id为newunitsinstock,再添加一个comparevalidator控件,确保用户输入的是整数值,且>=0
unitsonorder – 添加一个textbox,id为newunitsonorder,再添加一个comparevalidator控件,确保用户输入的是整数值,且>=0
reorderlevel –添加一个textbox,id为newreorderlevel,再添加一个comparevalidator控件,确保用户输入的是整数值,且>=0
discontinued–添加一个checkbox,id为newdiscontinued
categoryname ––添加一个dropdownlist控件,id为newcategoryid。将其绑定到一个名为categoriesdatasource的objectdatasource控件,设置它调用categoriesbll类的getcategories() 方法。设置dropdownlist控件显示categoryname,并将dropdownlist控件的values设置为categoryid
suppliername –添加一个dropdownlist控件,id为newsupplierid.将其绑定到一个名为suppliersdatasource的objectdatasource控件,设置它调用suppliersbll类的getsuppliers()方法.设置dropdownlist控件显示companyname ,并将dropdownlist控件的values设置为supplierid.
将每个validation控件的forecolor属性清空,以便用在footerstyle css类定义的白色背景色取代默认的红色;同时将errormessage设置为详细的错误提示;将text属性设置为星号。在每个footertemplates里,只要包含有validation控件,将其wrap属性设置为false。最后,在gridview控件下面添加一个validationsummary 控件,设showmessagebox属性为true;showsummary属性为false。
当添加一个新产品时,我们需要给出categoryid和supplierid值。页面上的2个dropdownlist控件显示的是categoryname 和suppliername,但传递的是我们需要的
categoryid和supplierid值。为什么不直接显示categoryid和supplierid值呢?因为最终用户对categoryname 和suppliername更感兴趣。既然现在可以在显示categoryname 和suppliername的插入界面获取对应的categoryid和supplierid值,我们将categoryid 和supplierid 2个templatefields从gridview移除。
同样,当添加新产品时我们不需要productid,那么我们也可以删除productid templatefield,不过,在这里我们保留它。除了textboxes,dropdownlists、
checkboxes以及validation控件外,我们还需要在插入界面添加一个“add”按钮。当点击该按钮时,将新记录添加到数据库。在第4步,我们将在productid templatefield的footertemplate模板添加一个“add”按钮。
按你喜欢的方式改进外观。比如,将unitprice值格式化为货币形式;将unitsinstock, unitsonorder和reorderlevel三列放在右边;修改templatefields的headertext属性等。
在footertemplates里完成插入界面的修改后,移除supplierid 和 categoryid templatefields,最终,你的gridview控件的声明代码看起来应该和下面的差不多:
<asp:gridview id="products" runat="server" autogeneratecolumns="false" datakeynames="productid" datasourceid="productsdatasource" allowpaging="true" enableviewstate="false" showfooter="true"> <columns> <asp:templatefield headertext="productid" insertvisible="false" sortexpression="productid"> <itemtemplate> <asp:label id="label1" runat="server" text='<%# bind("productid") %>'></asp:label> </itemtemplate> <itemstyle horizontalalign="center" /> </asp:templatefield> <asp:templatefield headertext="product" sortexpression="productname"> <itemtemplate> <asp:label id="label2" runat="server" text='<%# bind("productname") %>'></asp:label> </itemtemplate> <footertemplate> <asp:textbox id="newproductname" runat="server"></asp:textbox> <asp:requiredfieldvalidator id="requiredfieldvalidator1" runat="server" controltovalidate="newproductname" display="dynamic" forecolor="" errormessage="you must enter a name for the new product."> * </asp:requiredfieldvalidator> </footertemplate> <footerstyle wrap="false" /> </asp:templatefield> <asp:templatefield headertext="category" sortexpression="categoryname"> <itemtemplate> <asp:label id="label10" runat="server" text='<%# bind("categoryname") %>'></asp:label> </itemtemplate> <footertemplate> <asp:dropdownlist id="newcategoryid" runat="server" datasourceid="categoriesdatasource" datatextfield="categoryname" datavaluefield="categoryid"> </asp:dropdownlist> <asp:objectdatasource id="categoriesdatasource" runat="server" oldvaluesparameterformatstring="original_{0}" selectmethod="getcategories" typename="categoriesbll"> </asp:objectdatasource> </footertemplate> </asp:templatefield> <asp:templatefield headertext="supplier" sortexpression="suppliername"> <itemtemplate> <asp:label id="label11" runat="server" text='<%# bind("suppliername") %>'></asp:label> </itemtemplate> <footertemplate> <asp:dropdownlist id="newsupplierid" runat="server" datasourceid="suppliersdatasource" datatextfield="companyname" datavaluefield="supplierid"> </asp:dropdownlist><asp:objectdatasource id="suppliersdatasource" runat="server" oldvaluesparameterformatstring="original_{0}" selectmethod="getsuppliers" typename="suppliersbll"> </asp:objectdatasource> </footertemplate> </asp:templatefield> <asp:templatefield headertext="qty/unit" sortexpression="quantityperunit"> <itemtemplate> <asp:label id="label5" runat="server" text='<%# bind("quantityperunit") %>'></asp:label> </itemtemplate> <footertemplate> <asp:textbox id="newquantityperunit" runat="server"></asp:textbox> </footertemplate> </asp:templatefield> <asp:templatefield headertext="price" sortexpression="unitprice"> <itemtemplate> <asp:label id="label6" runat="server" text='<%# bind("unitprice", "{0:c}") %>'></asp:label> </itemtemplate> <footertemplate> $<asp:textbox id="newunitprice" runat="server" columns="8" /> <asp:comparevalidator id="comparevalidator1" runat="server" controltovalidate="newunitprice" errormessage="you must enter a valid currency value greater than or equal to 0.00. do not include the currency symbol." forecolor="" operator="greaterthanequal" type="currency" valuetocompare="0" display="dynamic"> * </asp:comparevalidator> </footertemplate> <itemstyle horizontalalign="right" /> <footerstyle wrap="false" /> </asp:templatefield> <asp:templatefield headertext="units in stock" sortexpression="units in stock"> <itemtemplate> <asp:label id="label7" runat="server" text='<%# bind("unitsinstock") %>'></asp:label> </itemtemplate> <footertemplate> <asp:textbox id="newunitsinstock" runat="server" columns="5" /> <asp:comparevalidator id="comparevalidator2" runat="server" controltovalidate="newunitsinstock" display="dynamic" errormessage="you must enter a valid numeric value for units in stock that's greater than or equal to zero." forecolor="" operator="greaterthanequal" type="integer" valuetocompare="0">*</asp:comparevalidator> </footertemplate> <itemstyle horizontalalign="right" /> <footerstyle wrap="false" /> </asp:templatefield> <asp:templatefield headertext="units on order" sortexpression="unitsonorder"> <itemtemplate> <asp:label id="label8" runat="server" text='<%# bind("unitsonorder") %>'></asp:label> </itemtemplate> <footertemplate> <asp:textbox id="newunitsonorder" runat="server" columns="5" /> <asp:comparevalidator id="comparevalidator3" runat="server" controltovalidate="newunitsonorder" display="dynamic" errormessage="you must enter a valid numeric value for units on order that's greater than or equal to zero." forecolor="" operator="greaterthanequal" type="integer" valuetocompare="0">*</asp:comparevalidator> </footertemplate> <itemstyle horizontalalign="right" /> <footerstyle wrap="false" /> </asp:templatefield> <asp:templatefield headertext="reorder level" sortexpression="reorderlevel"> <itemtemplate> <asp:label id="label9" runat="server" text='<%# bind("reorderlevel") %>'></asp:label> </itemtemplate> <footertemplate> <asp:textbox id="newreorderlevel" runat="server" columns="5" /> <asp:comparevalidator id="comparevalidator4" runat="server" controltovalidate="newreorderlevel" display="dynamic" errormessage="you must enter a valid numeric value for reorder level that's greater than or equal to zero." forecolor="" operator="greaterthanequal" type="integer" valuetocompare="0">*</asp:comparevalidator> </footertemplate> <itemstyle horizontalalign="right" /> <footerstyle wrap="false" /> </asp:templatefield> <asp:templatefield headertext="discontinued" sortexpression="discontinued"> <itemtemplate> <asp:checkbox id="checkbox1" runat="server" checked='<%# bind("discontinued") %>' enabled="false" /> </itemtemplate> <footertemplate> <asp:checkbox id="newdiscontinued" runat="server" /> </footertemplate> <itemstyle horizontalalign="center" /> <footerstyle horizontalalign="center" /> </asp:templatefield> </columns> </asp:gridview>
在浏览器里查看该页面时,gridview控件的页脚行显示为一个比较完善的插入界面(如图10所示)。此时,插入界面并不包含一种方法将输入的数据添加进数据库。并将我们也没有阐述那些键入的数据是如何转换成一条记录的。在第4步,我们将看到如何添加一个“add ”按钮,在页面回传后如何执行代码。在第5步,我们看如何将键入的数据转换成一条记录添加到数据库。
图10:gridview的页脚行提供了添加新记录的界面
第4步:在插入界面添加add按钮
如前所述我们需要在界面添加一个add按钮,我们可以在某个footertemplate里或另外增加一列来放置该按钮来达到这个目的。在本教程,我们在productid templatefield的footertemplate里添加该按钮。
点击gridview的智能标签中的“编辑模板”,选择productid对应的 footertemplate ,添加一个button web控件(linkbutton 或是imagebutton,只要你喜欢), 设id为addproduct;commandname属性为insert;text属性为“add”,如图11所示:
图11:将add button放在productid templatefield的footertemplate模板
添加按钮后,在浏览器查看该页面。如果我们在界面输入无效的数据,再点add按钮时,页面回转中断,同时validationsummary控件详细的列出了那些无效数据(如图12)。当输入适当的数据后,再点按钮,将引发页面回传,但是没有记录添加到数据库里。我们需要编写代码实现插入数据的功能。
图12:如果输入的是无效数据,将会使页面回转中断
注意:界面里的validation控件未被设置为一组,当页面中只有插入界面包含validation控件的话,运行没问题。但是,如果在页面中还有其它的validation控件的话(比如,如果还存在一个编辑界面,其中也包含validation控件),我们应该将插入界面里的validation控件和add按钮的validationgroup属性设置为同一个值,使其为一个特定的确认组。
第5步:向表products添加一条新记录
当使用gridview控件的内置的编辑功能时,gridview会自动的处理编辑产品所必要的工作。当点击编辑按钮时,它把在编辑页面键入的数据拷贝到objectdatasource的updateparameters参数集包含的参数,再调用objectdatasource控件的update()方法执行更新。由于gridview没有提供内置的功能供插入数据,我们必须编写代码调用objectdatasource控件的insert()方法,将在插入界面键入的数据复制到 objectdatasource控件的insertparameters集合里。
就像在教程28章《gridview里的button》里探讨的一样,任何时候,只要点击 gridview控件里的button, linkbutton或imagebutton,发生页面回转时引发gridview的rowcommand事件。不管这些button, linkbutton、imagebutton控件是显式添加的(比如,在页脚行添加的add按钮),或者是gridview控件自动添加的(比如启用分页功能或排序功能时,顶部的出现的那些linkbutton)。
为相应用户点击add按钮,我们要为gridview的rowcommand事件创建一个事件处理器。由于任何时候点击gridview控件的任何button, linkbutton或imagebutton都会触发该事件,我们必须指定当传入事件处理器的commandname属性值与add按钮的一致时(即:“insert”),并且键入的数据无误时,才执行插入操作。代码如下:
protected void products_rowcommand(object sender, gridviewcommandeventargs e) { // insert data if the commandname == "insert" // and the validation controls indicate valid data... if (e.commandname == "insert" && page.isvalid) { // todo: insert new record... } }
注意:你可能会很奇怪为什么还要检查page.isvalid属性呢?毕竟,如果在插入界面输入了无效的数据时,页面回传会中断。检查page.isvalid属性是为了防止用户未启用javascript或巧妙的绕过客户端验证的情况。简而言之,如果没有进行客户端进行有效性验证的话,在处理数据以前必须在服务器端再进行一次有效性验证。
在第1步,objectdatasource控件productsdatasource的insert()方法映射的是productsbll类的addproduct方法。为了在表products里添加新记录,我们只需要简单的调用objectdatasource的insert()方法:
protected void products_rowcommand(object sender, gridviewcommandeventargs e) { // insert data if the commandname == "insert" // and the validation controls indicate valid data... if (e.commandname == "insert" && page.isvalid) { // insert new record productsdatasource.insert(); } }
现在可以调用insert()方法,剩下的步骤是把在插入界面键入的值传递给productsbll类的addproduct方法中的参数。就像在教程17章《》探讨的一样,可以通过objectdatasource控件的inserting事件来实现。在inserting事件里,我们需要编程访问页脚行里的控件,将其值赋给e.inputparameters集合。当用户忽略了某个值时——比如使reorderlevel文本框为空,我们应该指定该值为null。因为addproducts方法允许那些nullable类型的列接收null值。代码如下:
protected void productsdatasource_inserting (object sender, objectdatasourcemethodeventargs e) { // programmatically reference web controls in the inserting interface... textbox newproductname = (textbox)products.footerrow.findcontrol("newproductname"); dropdownlist newcategoryid = (dropdownlist)products.footerrow.findcontrol("newcategoryid"); dropdownlist newsupplierid = (dropdownlist)products.footerrow.findcontrol("newsupplierid"); textbox newquantityperunit = (textbox)products.footerrow.findcontrol("newquantityperunit"); textbox newunitprice = (textbox)products.footerrow.findcontrol("newunitprice"); textbox newunitsinstock = (textbox)products.footerrow.findcontrol("newunitsinstock"); textbox newunitsonorder = (textbox)products.footerrow.findcontrol("newunitsonorder"); textbox newreorderlevel = (textbox)products.footerrow.findcontrol("newreorderlevel"); checkbox newdiscontinued = (checkbox)products.footerrow.findcontrol("newdiscontinued"); // set the objectdatasource's insertparameters values... e.inputparameters["productname"] = newproductname.text; e.inputparameters["supplierid"] = convert.toint32(newsupplierid.selectedvalue); e.inputparameters["categoryid"] = convert.toint32(newcategoryid.selectedvalue); string quantityperunit = null; if (!string.isnullorempty(newquantityperunit.text)) quantityperunit = newquantityperunit.text; e.inputparameters["quantityperunit"] = quantityperunit; decimal? unitprice = null; if (!string.isnullorempty(newunitprice.text)) unitprice = convert.todecimal(newunitprice.text); e.inputparameters["unitprice"] = unitprice; short? unitsinstock = null; if (!string.isnullorempty(newunitsinstock.text)) unitsinstock = convert.toint16(newunitsinstock.text); e.inputparameters["unitsinstock"] = unitsinstock; short? unitsonorder = null; if (!string.isnullorempty(newunitsonorder.text)) unitsonorder = convert.toint16(newunitsonorder.text); e.inputparameters["unitsonorder"] = unitsonorder; short? reorderlevel = null; if (!string.isnullorempty(newreorderlevel.text)) reorderlevel = convert.toint16(newreorderlevel.text); e.inputparameters["reorderlevel"] = reorderlevel; e.inputparameters["discontinued"] = newdiscontinued.checked; }
添加完inserting事件处理器后,我们就可以通过gridview控件的页脚行添加记录了。开始吧,尝试添加几个产品。
优化并自定义add操作
一般来说,点击add按钮后,就将为数据库添加一个新记录。但是没有任何直观的提示反映成功地添加了记录。的确,应该用一个label web控件或客户端的消息框提示用户已经成功地添加了产品,我把它作为一个练习留给读者。
本文使用的gridview控件没有对所显示的产品进行任何排序,也未允许最终用户对数据排序。因此,产品依它们在数据库中的次序排序——依主键值顺序。由于每条新添加的记录的productid值比上一条的值大,所以,当添加新记录时,它就自然地排到最后一位了。因此,当添加新记录时,你希望自动地转到gridview控件的最后一页。怎么才能办到呢?在rowcommand事件处理器里,调用productsdatasource.insert()方法后,紧接着添加如下一行代码,它说明当数据绑定到gridview后将转到最后一页:
// indicate that the user needs to be sent to the last page sendusertolastpage = true;
其中sendusertolastpage是页面层(page-level)的布尔变量,其初始值为false。在gridview控件的databound事件处理器中,如果sendusertolastpage为false(译注:应该是true),pageindex属性将使用户转到最后一页。
protected void products_databound(object sender, eventargs e) { // send user to last page of data, if needed if (sendusertolastpage) products.pageindex = products.pagecount - 1; }
为什么我们要在databound事件处理器(而不是在rowcommand事件处理器)里设置pageindex属性呢?如果在rowcommand里设置pageindex属性的话,它返回的是在添加新记录之前的pageindex值。在大多数情况下,这样做是没有问题的,但是,如果新添加的记录刚好落到新的一页(译注:比如原本有80个产品,分为8页显示,此时末页的pageindex为7,当添加第81条记录时,新添加的产品变成第9页第1条记录了,此时末页的pageindex为8,而不是添加产品前的7),而我们使用rowcommand里设置的pageindex值话,页面将跳往倒数第2页,而不是我们期望的末页。而databound事件是在添加产品且重新绑定以后才发生,我们在databound事件处理器里设置的pageindex值才是真正的末页的pageindex值。
最后,本文用到的gridview看起来相当宽,因为添加产品信息要用到很多列。因此,最好将它设置为竖向排列。另外我们可以减少输入列来缩小整体宽度,也许我们添加新产品时用不到unitsonorder、unitsinstock、reorderlevel这几项,那么在gridview里将其移除即可。
删除unitsonorder、unitsinstock、reorderlevel列后需要做调整,有2种方法:
1.仍然使用addproduct方法,不过其需要传入unitsonorder、unitsinstock、reorderlevel列的值。我们可以在inserting事件处理器中,对上述3列使用“硬编码”值或默认值。
2.在productsbll类里对addproduct方法重载,使其不需要传入unitsonorder、unitsinstock、reorderlevel列的值。然后,在asp.net page页面设置objectdatasource使用重载的addproduct方法。
以上2种方法都能奏效。在以前的教程里我们使用的是后者,对productsbll类的updateproduct方法多次重载。
总结:
detailsview和formview控件拥有内置的inserting插入数据功能,而gridview没有。不过我们可以使用gridview控件的页脚行来达到此目的。要显示页脚行只需要设置showfooter属性为true。我们可以这样对页脚行进行用户定制:将每一列转换成templatefield,并在其footertemplate模板定制插入界面。正如我们在本章看到的那样,footertemplate 模板可以包含buttons,textboxes, dropdownlists,checkboxes, data source controls,validation controls等控件,除此以外,为了便于用户输入,add按钮, linkbutton或imagebutton等也是必需的。
当点击add按钮后,将调用objectdatasource控件的insert()方法,进而使用其映射的插入数据方法(具体到本文,即为productsbll类的addproduct方法),在调用具体的插入数据方法前,我们需要将插入界面里键入的数据传递objectdatasource控件的insertparameters集合。要达到该目的,我们应该在objectdatasource控件的inserting事件处理器里,编程访问插入界面的web控件。
本教程探讨了优化gridview外观的技巧。接下来的一系列教程,我们看如何使用2进制数据——比如images, pdfs, word documents等等,当然还有data web控件。
祝编程快乐!
作者简介
本系列教程作者 scott mitchell,著有六本asp/asp.net方面的书,是4guysfromrolla.com的创始人,自1998年以来一直应用 微软web技术。大家可以点击查看全部教程《[翻译]scott mitchell 的asp.net 2.0数据教程》,希望对大家的学习asp.net有所帮助。