添加和响应 GridView 控件的按钮
本文档是 Visual Basic 教程 (切换到 Visual C# 教程)
本教程中我们将了解如何为 GridView 控件和 DetailView 控件的模板和字段添加自定义按钮。另外,我们还将单独创建一个界面,这个界面拥有一个 FormView 控件,用户通过这个控件可以浏览各个供应商。
简介许多报表方案都涉及到报表数据的只读访问,对于那些具有能够根据显示的数据来完成各种操作功能的报表来说也很常见。通常这涉及到使用报表中所示的每一条记录添加一个按钮、链接按钮(LinkButton) 或 Web 位图按钮控件,当这些控件或按钮被单击后,会引起回传或激活某些服务端的代码。最常见的例子是按逐条记录的原则编辑和删除数据。事实上,从数据插入、更新和删除概述教程开始,编辑和删除就非常常见,所以无需写一行代码, GridView 、 DetailsView 和 FormView 控件就可以支持这些功能。 除了编辑按钮和删除按钮,GridView 、DetailsView 和FormView 控件还包含一些按钮、链接按钮 (LinkButton) 或位图按钮 (ImageButton) ,当这些按钮被单击后,会执行一些用户自定义的服务端逻辑。本教程中我们将了解如何为 GridView 控件和 DetailView 控件的模板和字段添加自定义按钮。另外,我们还将单独创建一个界面,这个界面拥有一个 FormView 控件,用户通过这个控件可以浏览各个供应商。对于一个特定供应商, FormView 控件会显示该供应商的相关信息和一个 Web 按钮控件,如果单击该控件,当产品断货时 FormView 控件会标记出所有的相关产品。另外,Gridview 会列出这些由选定的供应商提供的产品,并且每一列都包含“增加价格”和“折扣价格”按钮,单击这些按钮后将会增加或降低产品单元价格的百分之十。(请参见插图 1 ) 图1 :包含用于执行自定义操作的按钮的 FormView 控件和 GridView 控件 步骤1 :添加按钮教程 Web 页面在探求如何添加自定义按钮之前,让我们先花一些时间在本教程需要的 Web 站点项目中创建一个 ASP.NET 页面。开始先添加一个文件夹,命名为 CustomButtons 。接下来,将以下 ASP.NET 页面添加到该文件夹,确保每个页面与 Site.master 主页页面相关联:
图2 :为与自定义按钮相关的教程添加 ASP.NET 页面 和其他文件夹中的一样,在 CustomButtons 文件夹中的 Default.aspx 页面将会列出它所在章节的教程。回想一下, SectionLevelTutorialListing.ascx 用户控件提供本功能。所以通过拖拉的方法将该用户控件从解决方案管理器中拖拉到页面设计视图中,从而将该用户控件添加到 Default.aspx 页面中。 图3 :向 Default.aspx 添加 SectionLevelTutorialListing.ascx 用户控件 最后,将页面按条目添加到 Web.sitemap 文件中。具体来说,在进行分页和排序之后添加以下标示文字 <siteMapNode> :
更新Web.sitemap 后,花点时间用浏览器查看一下教程网站。现在,左边的菜单包含用于编辑、插入和删除教程的各项。 图4 :包含自定义按钮教程入口的站点地图 步骤2 :添加一个 FormView 控件,列出各个供应商本教程中,让我们从添加一个 FormView 控件,并列出各个供应商开始。正如介绍一节中所介绍的那样,FormView 控件允许用户浏览各个产品供应商,并在 GridView 中显示由各个供应商提供的产品。另外,FormView 控件还拥有一个按钮,单击这个按钮后,断货时会标记出供应商的所有产品。在我们关心如何为 FormView 控件添加自定义按钮前,首先让我们先创建一个 FormView 控件,以便显示供应商信息。 开始,先打开 CustomButtons 文件夹中的 CustomButtons.aspx 页面,为该页面添加一个 FormView 控件(通过拖拉的方法将其从工具箱中拖拉到设计器中),并为其设置 ID 属性和供应商。从 FormView 控件的智能标记中选择创建一个新的对象数据源 ( ObjectDataSource ),命名为 SuppliersDataSource 。 图5 :创建一个名称为 SuppliersDataSource 的新的 ObjectDataSource 需要配置这个新的对象数据源,因为使用 SupplierBLL 类的 GetSupplier() 方法时要用到该数据源(请参见图6 )因为 FormView 控件不提供用于更新供应商信息的界面,所以请在 UPDATE 选项卡上的下拉菜单列表中选择选项 (None) 。 图6 :配置数据源,以使用 SuppliersBLL 类的 GetSuppliers () 方法 对象数据源(ObjectDataSource) 配置完成后,Visual Studio 会为 FormView 控件生成一个插入条目模板(InsertItemTemplate) 编辑条目模板 (EditItemTemplate) 和条目模板 (ItemTemplate) . 移除插入条目模板和编辑条目模板并修改条目模板,这样 FormView 控件就只显示供应商的公司名称和电话号码。最后,在它的智能标记上检查选中允许分页检查框 (或将其 AllowPaging 属性赋值为真 ( TRUE ),为 FormView 控件打开分页支持。完成这些更改之后,您的页面的公开的标示文字应该和以下文字相似:
图 7 通过浏览器浏览 CustomButtons.aspx 页面 图7 :FormView 控件列出了当前选中的供应商的公司名称和电话号码字段 步骤3 :添加一个GridView 控件列出所选供应商的各种产品在我们为FormView 控件的模板添加“Discontinue All Products” 按钮之前,先让我们在FormView 控件的下面添加一个 GridView 控件,列出所选供应商的各种产品。为了完成这个过程,您需要为页面添加一个 GridView 控件,将其 ID 属性赋值为供应商产品 ( SuppliersProducts ),并且还需要添加一个数据源对象,并将其命名为供应商产品数据源 ( SuppliersProductsDataSource )。 . 图8 :创建一个新的对象数据源 (ObjectDataSource) ,将其命名为供应商产品数据源 (SuppliersProductsDataSource) 配置该对象数据源,来使用 ProductsBLL 类的GetProductsBySupplierID (supplierID) 方法(请参见图 9 )。尽管考虑到 GridView 控件可能需要对产品价格进行调整,但是它不会使用 GridView 控件内置的编辑或删除功能。因此,我们可以将对象数据源的 UPDATE 、 INSERT 和 DELETE 选项卡上的下拉菜单列表选项设置为 ( None )。 图9 :配置数据源,以使用 ProductsBLL 类的GetProductsBySupplierID (supplierID) 方法 因为 GetProductsBySupplierID (supplierID) 方法需要一个输入参数,所以对象数据源向导会提示我们为该数据源提供一个参数值。为了从 FormView 控件中传递 SupplierID 参数值,请将参数源下拉菜单列表设置为 Control ,将 ControID 下拉菜单列表设置为 Suppliers (在步骤 2 中创建了 FormView 控件的 ID )。 图10 :指示supplierID 应该来自供应商 FormView 控件 完成对象数据源向导后,GridView 将会为产品的每一个数据字段包含一个BoundField 或 CheckBoxField 。让我们把这个裁掉只显示 ProductName 、 UnitPrice BoundFields 以及 Discontinued CheckBoxField ;并且将 UnitPrice BoundField 进行格式化处理,将其格式化转换为货币单位。您的 GridView 控件和SuppliersProductsDataSource 对象数据源的公开标示文字应该和以下文字相似:
在这里,我们的教程手册显示了一个主/ 明细报表 ,允许用户从顶端的 FormView 控件中捡取一个供应商并通过底部的 GridView 控件浏览该供应商提供的各种产品。 图11 显示从 FormView 控件中选择 Tokyo Traders 时的一个截屏画面 图11 :在 GridView 控件中显示选定的供应商的产品 步骤4 :创建DAL 和BLL 方法,停售某个供应商的所有产品我们可以为FormView 控件添加一个按钮,单击该按钮,停售该供应商的所有产品,要做到这一步,我们首先应该为 DAL 和 BLL 添加一个方法来执行这个操作。具体来说,该方法命名为 DiscontinueAllProductsForSupplier (supplierID) 。单击 FormView 控件的按钮后,将会在业务逻辑层激活该方法,并传递选定的供应商的 SupplierID ;接着 BLL 会调用到相应的数据访问层方法,该方法会调用 UPDATE 语句更新数据库从而停售指定供应商的产品。 如同在前面所做的那样,我们可以使用自底而上手段来创建 DAL 方法,接着再创建 BLL ,最终在 ASP.NET 页面中执行该功能。打开 App_Code/DAL 文件夹中的 Northwind.xsd 强类型 DataSet,为 ProductsTableAdapter 添加一个新的方法(在 ProductsTableAdapter 上单击右键,选择 Add Query )。这样做会引出 TableAdapter Query 配置向导,该向导会知道我们完成这个新方法的添加过程。首先需要指出的是我们的 DAL 方法会使用特别的 SQL 语句。 图12 :使用 Ad-Hoc SQL 语句创建 DAL 方法 接下来,向导会提示我们创建何种类型的查询 (Query) 。因为 DiscontinueAllProductsForSupplier (supplierID) 方法需要更新产品数据库表,将所有由指定的supplierID 供应商提供的产品的 Discontinued 字段赋值为 1 ,所以我们需要创建一个查询 ( Query ) 用来更新数据。 图13 :选择 UPDATE 查询类型 下一个向导画面显示的是 TableAdapter 的已有的 UPDATE 语句,这个语句可以更新产品数据表中定义的每一个字段。将这条查询语句替换为以下语句:
输入该条语句后,单击下一步 (Next) ,最后一个向导画面会要求您提供新方法的名称,请使用 DiscontinueAllProductsForSupplier 。单击完成按钮完成整个向导。返回到 DataSet 设计器,你会在 ProductsTableAdapter 中看到一个名字为 DiscontinueAllProductsForSupplier (@SupplierID) 的新方法。 图14 :将新的 DAL 方法命名为 DiscontinueAllProductsForSupplier 有了在数据访问层创建的DiscontinueAllProductsForSupplier (supplierID) 方法,我们的下一个任务就是在业务逻辑层 (BLL) 创建DiscontinueAllProductsForSupplier (supplierID) 方法。添加这个方法需要打开 ProductsBLL 类文件,加入以下语句:
该方法可以在数据访问层完全调用DiscontinueAllProductsForSupplier (supplierID) 方法, 并传递提供的supplierID 参数值。如果有业务规则规定供应商的产品只能在某些情况下断货,那么这些规则应该在 BLL 中执行。 注意:与在 ProductsBLL 类中重载的 UpdateProduct 方法不同,DiscontinueAllProductsForSupplier (supplierID) 方法 符号并不包含 DataObjectMethodAttribute 属性(<System.ComponentModel.DataObjectMethodAttribute (System.ComponentModel.DataObjectMethodType.Update, Boolean) >) 。这样就可以在 UPDATE 选项卡中将 DiscontinueAllProductsForSupplier (supplierID) 方法 从对象数据源配置数据向导的下拉菜单列表中排除。我已经忽略了这个属性,因为我们直接从 ASP.NET 页面的 Event Handler 中调用 DiscontinueAllProductsForSupplier (supplierID) 方法。 步骤5:为FormView 控件添加“Discontinue All Products” 按钮在业务逻辑层和数据访问层中创建DiscontinueAllProductsForSupplier (supplierID) 方法后,为选定的供应商添加停售所有产品功能的最后一步是为FormView 控件的条目模板添加 Web 按钮控件。在供应商的电话号码下方添加该按钮,按钮上的文字为 “Discontinue All Products” ,按钮的 ID 属性值为 DiscontinueAllProductsForSupplier 。您可以通过设计器单击 FormView 控件的智能标记(请参见图 15 )中的编辑模板或直接通过声明语句来添加 Web 按钮控件。 图15 :为 FormView 控件的条目模板添加 “Discontinue All Products” Web 按钮 控件 当访问该页面的用户单击该按钮时,会发生回传操作,紧接着FormView 控件的 ItemCommand 事件被触发。为了能够在单击该按钮时执行自定义代码,我们可以为该事件创建一个 Event Handler。可以这样理解,只要 FormView 控件内的任何按钮、链接按钮或位图按钮被单击,ItemCommand 事件都会被触发。这意味着当用户在 FormView 控件内从一个页面转移到另一个页面时,ItemCommand 事件会被触发。同样当用户单击支持插入、更新或删除的 FormView 控件内的新建 ( New )、编辑 ( Edit ) 或删除 ( Delete ) 按钮时,ItemCommand 事件也会被触发。 因为在 Event Handler 中,不管什么按钮被单击,ItemCommand 事件都会被触发,所以我们需要一种方法来确定是 “Discontinue All Products” 按钮被单击还是别的其它的按钮被单击。要做到这一点,我们可以将 Web 按钮控件的 CommandName 属性设置成某个标识值。当该按钮被单击后,该 CommandName 值被传递给 ItemCommand Event Handler,这样我们就可以判断是否是 “Discontinue All Products” 按钮被单击。将 “Discontinue All Products” 按钮的 CommandName 属性设置为 “DiscontinueProducts” 。 最后,我们可以使用一个客户端的确认对话框来确认用户是否确实想停售选定用户的产品。正如我们在删除时添加客户端确认 教程中所看到的那样,这个功能也可以使用很少的 JavaScript 代码来实现。 特别需要注意的是需要将 Web 按钮控件的 OnClientClick 属性设置为 “return confirm('This will mark _all_ of this supplier\'s products as discontinued. Are you sure you want to do this?');" 完成这些操作后,FormView 控件的声明语句看起来类似下面的语句:
接下来,为FormView 控件的ItemCommand 事件创建一个 Event Handler 。在这个 Event Handler 中我们首先需要判断“Discontinue All Products” 按钮是否被单击。如果被单击,我们要创建一个 ProductsBLL 类的实例,并激活它的 DiscontinueAllProductsForSupplier (supplierID) 方法,传递选定的 FormView 控件的 SupplierID 值:
注意,可以使用FormView 控件的 SelectedValue 属性 来访问当前在 FormView 控件中选定的 SupplierID 。SelectedValue 属性返回FormView 控件中显示的记录的第一数据键值。 FormView 控件的 DataKeyNames 属性, 指示说明了数据键值来自哪些数据字段,并且在前面的步骤 2 中当将对象数据源绑定到 FormView 控件时 Visual Studio 可以将该属性自动设置为 SupplierID 。 创建 ItemCommand Event Handler 后,花点时间测试一下页面。浏览 Cooperativa de Quesos 'Las Cabras' 供应商(对我来说他是 FormView 控件中的第十五个供应商)。该供应商提供两种产品,Queso Cabrales 和 Queso Manchego La Pastora ,这两种产品都没有断货。 假设 Cooperativa de Quesos 'Las Cabras' 已经停止该产品的生产业务,那么他的产品将会断货。单击 “Discontinue All Products” 按钮。将会显示客户端的确认对话框 ( 请参见图 16) 。 图16:Cooperativa de Quesos ‘Las Cabras 提供两种现行的产品 如果您单击客户端的确认对话框中的 OK 按钮,那么会继续提交表格,并引起回传,在该回传操作中,FormView 控件的 ItemCommand 事件将会被触发。接着我们创建的Event Handler 将会被执行,激活 DiscontinueAllProductsForSupplier (supplierID) 方法并停售 Queso Cabrales 和 Queso Manchego La Pastora 这两个产品。 如果您已经关闭了 GridView 控件的浏览状态,那么 GridView 控件会被重新绑定到存储在每一个回传操作上的底层数据上,并且会被立刻更新,以便及时反映这两个产品现在断货 ( 请参见图 17) 。但是如果您没有关闭 GridView 控件的视图状态,您需要在完成这些更改之后手动将数据重新绑定到 GridView 控件上。要做到这一点,您只需在激活 DiscontinueAllProductsForSupplier (supplierID) 方法后立刻调用 GridView 控件 DataBind () 方法即可。 图17 :单击 “Discontinue All Products” 按钮后 , 供应商的产品被相应的更新 步骤6 :在业务逻辑层创建一个 UpdateProduct 重载来调整产品价格与FormView 控件具有 “Discontinue All Products” 按钮一样,为了增加用于提高或降低 Gridview 控件产品价格的按钮,我们首先需要添加合适的数据访问层和业务逻辑层方法。因为我们已经有一个方法能够更新数据访问层中的一行产品,所以我们可以在业务逻辑层 ( BLL ) 为 UpdateProduct 方法创建一个新的重载函数来提供该功能。 我们前面进行 UpdateProduct 重载包含了一些复合产品字段,将这些字段作为输入值,接着为指定产品更新这些字段。对于这个重载,我们稍微更改一下这个标准,而是传递产品的 ProductID 和调整 UnitPrice 的百分数(与传递的新建的已调整的 UnitPrice 本身相反)。这种方法可以简化需要我们写入 ASP.NET 页面代码文件类,因为我们不用再费功夫来确定当前产品的 UnitPrice 。 本教程中,UpdateProduct 方法重载如下:
这个重载可以通过数据访问层的 GetProductByProductID (productID) 方法获取指定产品的信息。接着检查产品的UnitPrice 是否被赋予了一个数据库 NULL 值。如果赋予了一个 NULL 值,那么价格照旧不变。如果是一个非 NULL 的 UnitPrice 值,该方法会按照指定百分数更新产品的 UnitPrice (unitPriceAdjustmentPercent) . 步骤7 :为GridView 控件添加增加(Increase) 和降低(Decrease) 按钮GridView 控件和 DetailsView 控件都是由一个字段集组成的。除了 BoundFields 字段、 CheckBoxFields 字段和 TemplateFields 字段外, ASP.NET 页面还包括 ButtonField 字段,该字段正如它的名字所指的那样,为每一行呈递一列按钮、链接按钮或位图按钮。与 FormView 控件相似,单击 GridView 控件内的任何按钮:分页按钮、编辑按钮或删除按钮,筛选按钮等等,都会引起数据回发操作,并产生 GridView 控件的 RowCommand 事件 。 ButtonField 字段有一个 CommandName 属性 , 该属性为其每一个按钮的 CommandName 属性分配一个指定的值。与 FormView 控件一样, RowCommand Event Handler使用 CommandName 值来确定哪一个按钮被单击。 让我们为 GridView 控件添加两个新的按钮字段,一个按钮为 “Price +10%” ,另一个按钮为 “Price -10%” 。要添加这些 ButtonFields 字段,请单击 GridView 控件智能标记上的 Edit Columns 链接,从左上角的列表中选择 ButtonField 字段类型,然后单击 Add 按钮。 图18 :为 GridView 添加两个 ButtonFields 按钮 首先移动这两个 ButtonFields 字段,这样它们就会成为 GridView 控件的前两个字段。接下来,将这两个字段的文本属性分别设置为 “Price +10%” 和 “Price -10%” ,将 CommandName 属性分别设置为 “IncreasePrice” 和 “DecreasePrice” 。默认情况下, ButtonField 会将它的按钮列作为链接按钮 ( LinkButtons )。当然,您可以更改这个设置,您只需通过 ButtonField 字段的 ButtonType 属性 就可以更改。现在我们要把这两个 ButtonFields 作为普通的按压按钮 ( Push Button ),所以需要为它设置 ButtonType 属性。图 19 显示的是字段对话框,该对话框出现在这些变更完成之后,接下来的是 GridView 控件的声明标记。 图19:配置 ButtonFields 的文本、CommandName 和 ButtonType 属性
这些 ButtonFields 创建之后,最后一步就是为GridView 控件的RowCommand 事件创建一个Event Handler 。如果因为单击了 “Price +10%” 按钮或 “Price -10%” 按钮,触发了该 Event Handler,那么该句柄需要确定按钮被单击的那一行的 ProductID ,然后激活 ProductsBLL 类的 UpdateProduct 方法,并传递对应的 UnitPrice 调整百分数和 ProductID 。以下代码执行这些任务:
为了确定“Price +10%” 按钮和 “Price -10%” 按钮被单击的那一行的 ProductID ,我们需要查阅 GridView 控件的 DataKeys 集合。该集合为 GridView 的每一行保存了指定的 DataKeyNames 属性中的各个字段的值。因为在将对象数据源绑定到 Gridview 控件时,GridView 控件的 DataKeyNames 属性被 Visual Stdio 设置为 ProductID ,所以 DataKeys (rowIndex).Value 为 ProductID 提供指定的 rowIndex 索引。 ButtonField 会自动通过 e.CommandArgument 参数传递按钮被单击的那一行的rowIndex 索引 。所以,为了确定是哪一行的 “Price +10%” 按钮或 “Price -10%” 按钮被单击的 ProductID ,我们使用:Convert.ToInt32(SuppliersProducts.DataKeys(Convert.ToInt32(e.CommandArgument)).Value) 。 如同 “Discontinue All Products” 按钮,如果您关闭了 GridView 控件的视图状态,那么 GridView 控件会被重新绑定到每一个存储在回传操作上的底层数据上,并且会被立刻更新,以显示价格变化(因为单击其中一个按钮产生的变化)。但是如果您没有关闭 GridView 控件的视图状态,您需要在完成这些更改之后手动将数据重新绑定到 GridView 控件上。要完成这个绑定过程,您只需在激活 UpdateProduct 方法后立刻调用 GridView 控件的 DataBind() 方法即可。 图20 图示了由 Grandma Kelly's Homestead 供应的产品。图 21 图示了为 Grandma's Boysenberry Spread 单击两次 “Price +10%” 按钮的结果和为Northwoods Cranberry Sauce 单击两次 "Price -10%” 按钮的结果。 图20 :含有 “Price +10%” 按钮和 “Price -10%” 按钮的 GridView 控件 图21 :第一个和第三个产品的价格已经通过 “Price +10%” 按钮和“Price -10%” 按钮进行了更新 注意:GridView 控件(和 DetailsView 控件)也可以将按钮、链接按钮或位图按钮添加到他们的TemplateFields 字段中。如同 BoundField 字段,单击这些按钮之后会引起回传操作,并产生 GridView 控件的 RowCommand 事件。尽管如此,当您在 TemplateField 字段中添加按钮时,按钮的 CommandArgument 不能自动设置为行的索引(当该按钮正在使用 ButtonFields 时)。如果您需要在 RowCommand Event Handler中确定被单击的按钮的行索引时,您需要在位于 TemplateField 内的声明语句中手动设置按钮的 CommandArgument 属性,使用代码如下: <asp:Button runat="server" ...CommandArgument='<%# CType(Container, GridViewRow).RowIndex %>' />. 小结GridView 控件、DetailsView 控件和 FormView 控件都可以包含按钮、链接按钮 (LinkButtons) 或位图按钮 (ImageButtons) 。当单击这些按钮时,都会引起回传操作并会在FormView 控件和 DetailsView 控件中产生相应的 ItemCommand 事件,在 GridView 控件中产生 RowCommand 事件。这些 Web 数据控件具有一些内置的功能,这些功能可以处理一些常见的与命令相关的操作,例如删除或编辑记录。另外,我们还可以使用自定义的按钮,当单击这些按钮时会自动执行我们自己定义的代码,并做出响应。 要做到这一点,我们需要为 ItemCommand 事件或 RowCommand 事件创建一个 Event Handler 。在该 Event Handler 中,我们首先要检查引入的 CommandName 值,通过该值来确定哪一个按钮被单击,接着执行相应的用户自定义操作。在本教程中,我们了解了怎样使用按钮和 ButtonFields 来停售指定供应商的所有产品和按百分之十的幅度增加或减少每特定产品的价格。 快乐编程!
|