演练:使用 DataGrid Web 控件读取和写入数据
Web 窗体数据绑定的结构便于在页控件中显示数据。但是,数据绑定不是双向的,也就是说,数据绑定从数据源读取数据但不对其进行更新。更新比显示数据更复杂,因为大多数 Web 窗体页不需要将数据写回源,所以 Web 窗体数据绑定通过不包括更新代码将页大小和页处理保持在最精简的程度。
当然,有时候您要创建更新数据的 Web 窗体页。本演练阐释完成该目的的一种方式。它说明了如何使用 DataGrid 控件显示数据,允许用户对其进行编辑,然后将更改的数据发送回源。
注意 DataGrid 控件本身并不包括创建新记录的功能。本主题结尾处的“下一步”一节包括添加此功能的建议。
当该页运行时,它看起来类似于下面这样:
若要完成本演练,您需要:
- 访问带有 Northwind SQL Server 示例数据库的服务器。SQL Server 可以和 Web 服务器位于同一计算机上,或位于另一服务器上。无论在哪一种情况下,都必须配置访问权限。有关详细信息,请参见从 Web 应用程序访问 SQL Server。
- 充足的权限,以便在 Web 服务器所在的计算机上创建 ASP.NET Web 应用程序项目。有关详细信息,请参见 Visual Studio 中设计时的 Web 应用程序安全性。
总体过程
演练被分成若干较小的部分:
- 创建 Web 窗体页。
- 添加必要的数据组件。
- 添加显示数据的 DataGrid 控件。
- 添加从数据库读取数据并将网格绑定到数据的代码。
- 配置 DataGrid 控件以允许用户编辑数据。
- 添加更新数据的代码。
创建项目和窗体
第一步是创建 Web 应用程序和 Web 窗体页。
创建项目和窗体
- 在“文件”菜单上指向“新建”,然后单击“项目”。
- 在“新建项目”对话框中,请执行以下操作:
- 在“项目类型”窗格中选择“Visual Basic 项目”或“Visual C# 项目”。
- 在“模板”窗格中选择“ASP.NET Web 应用程序”。
- 在“位置”框中,为您的应用程序输入完整的 URL(包含 http://、服务器名称和项目名称)。Web 服务器上必须安装 IIS 5 版(或更高版本)和 .NET Framework。如果计算机上已安装 IIS,可以为服务器指定
http://localhost
。(如果正常使用代理服务器访问 Internet,为了使用本地主机,可能需要配置 Internet Explorer 以绕过代理服务器。)当单击“确定”时,将在您指定的 Web 服务器的根处创建新的 Web 窗体项目。此外,名为 WebForm1.aspx 的新 Web 窗体页将显示在“设计”视图中 Web 窗体设计器上。
提示 如果在创建 Web 应用程序项目时遇到问题,请参见“Web 访问失败”对话框。
创建和配置数据集
在 Web 窗体页中,有多种访问数据的方法选择。一种方法是使用数据集,它是内存中的数据缓存。另外,您可以使用执行 SQL 语句或存储过程的数据命令直接访问数据库。一般情况下,使用数据集便于更新数据,这也正是您将在本演练中使用的方法。有关更多信息,请参见 Web 数据访问策略建议。
您不直接将数据集添加到页,您应执行的是如下步骤:
- 使用向导创建数据适配器。该适配器包含用于读取和写入数据库信息的 SQL 语句。该向导帮助您定义所需的 SQL 语句。如有必要,该向导还创建与数据库的连接。
- 生成数据集架构。在本过程中,您将让 Visual Studio 基于您正在访问的表和列创建一个新的数据集类。在生成数据集类时,您还将向窗体中添加该类的一个实例。
遵循本节中的所有过程很重要。否则,您的页将不具有在本演练的随后部分中将使用的数据集。
有关数据适配器的概述,请参见数据适配器介绍。有关数据集的概述,请参见数据集介绍。
配置数据连接和数据适配器
首先,请创建一个数据适配器,其中包含以后用于填充数据集的 SQL 语句。作为此过程的一部分,定义连接以访问数据库。使用向导配置数据适配器,该向导使创建数据访问所需的 SQL 语句变得容易。
注意 向导完成后,您必须继续下一部分操作,以生成数据集并完成该页的数据访问部分。
创建数据连接和数据适配器
- 单击 WebForm1.aspx,然后将 SqlDataAdapter 对象从“工具箱”的“数据”选项卡拖到页上。
注意 如果您未使用 SQL Server,则使用 OleDbDataAdapter 类型的适配器,它提供对任何与 OLE DB 兼容的数据源的访问。
“数据适配器配置向导”启动,它将帮助您创建连接和适配器。
- 在该向导中,执行下列操作:
- 在第二个窗格中,创建或选择一个指向 SQL Server Northwind 数据库的连接。有关访问数据库的信息,请与您的数据库管理员联系。
注意 您需要对所使用的 SQL Server 具有适当的读/写权限。如果 SQL Server 与 IIS 位于同一台计算机上,建议您在创建此连接时指定 Windows 集成安全性。否则,可以指定用户名和密码并将该信息与此连接保存在一起,但这样做会危及安全性。有关更多信息,请参见从 Web 应用程序访问 SQL Server。
- 在第三个窗格中,指定您要使用 SQL 语句访问数据库。
- 在第四个窗格中创建以下 SQL 语句:
SELECT CategoryID, CategoryName, Description FROM Categories
有关如何生成 SQL 语句的帮助,请单击“查询生成器”启动“查询生成器”对话框。
注意 在本演练中,将使用 categories 表中的所有行来填充数据集。在成品应用程序中,通常通过创建只返回所需列和行的查询来优化数据访问。有关示例,请参见演练:使用参数化查询在 Windows 窗体中显示数据。
- 单击“完成”。
向导创建一个连接(SqlConnection1 或 sqlConnection1),它包含有关如何访问数据库的信息。您还将具有包含一个查询的数据适配器(SqlDataAdapter1 或 sqlDataAdapter1),该查询定义所要访问数据库中的表和列。
- 在第二个窗格中,创建或选择一个指向 SQL Server Northwind 数据库的连接。有关访问数据库的信息,请与您的数据库管理员联系。
向导完成后,您需要基于在该过程中创建的 SQL 查询生成数据集。有关详细信息,请参见下一节。
创建数据集
建立连接到数据库的方法并指定所需的信息(通过数据适配器中的 SQL 命令)后,可以让 Visual Studio 创建数据集。Visual Studio 可以基于您为数据适配器指定的查询自动生成数据集。数据集是基于相应架构(.xsd 文件)的 DataSet 类的一个实例,该架构描述类的元素(表、列和约束)。有关数据集与架构之间关系的详细信息,请参见 ADO.NET 数据访问介绍。
生成数据集
- 从“数据”菜单中选择“生成数据集”。
提示 如果未启用“生成数据集”命令,则单击该页;该页必须具有焦点,命令才会出现。
出现“生成数据集”对话框。
- 选择“新建”选项,将该数据集命名为 dsCategories。
在“选择要添加到数据集中的表”下面的列表中,确保选择了 Categories 表。
- 确保“将此数据集添加到设计器”已选中,然后单击“确定”。
Visual Studio 生成某类型化数据集类 (dsCategories) 和定义该数据集的架构。您将在解决方案资源管理器中看到新的架构 (dsCategories.xsd)。
提示 在“解决方案资源管理器”中,单击“显示所有文件”工具栏按钮以查看架构文件的相关 .vb 或 .cs 文件,该文件包含定义新数据集类的代码。
最后,Visual Studio 将新数据集类 (dsCategories1) 的实例添加到页上。
此刻,为执行从数据库获取信息并转移到数据集的操作所需的全部设置均已完成。
添加显示数据的 DataGrid 控件
在本演练中,您将添加单个控件(DataGrid 控件),该控件可以同时显示数据集中的所有记录,并允许您添加编辑记录的功能。
数据网格必须绑定到数据集才能显示数据。
向窗体添加绑定 DataGrid 控件
- 如果尚未进行该操作,请单击当前窗口顶部的选项卡切换到 Web 窗体设计器。
- 从工具箱的“Web 窗体”选项卡中,将一个 DataGrid 控件拖到窗体上。
- 选择该控件,按 F4 键显示“属性”窗口,在窗口的底部,单击“属性生成器”。
出现“DataGrid1 属性”对话框。
- 在“常规”选项卡中,完成以下设置:
属性 设置 说明 DataSource dsCategories1 将网格绑定到数据集。 DataMember Categories 指定网格应该显示数据集的类别表中的数据。 Data key field CategoryID 指定类别记录的主键是 CategoryID 列。这将允许您稍后确定更新数据集中的哪个记录。 - 单击“确定”关闭“DataGrid1 属性”对话框。
- 如果您要更改网格的外观,请设置 Font、BackColor 和其他属性。
提示 一种简单的设置网格外观的方法是单击“属性”窗口底部的“自动套用格式”,然后选择预定义的外观。
填充数据集并在 DataGrid 控件中显示数据
尽管网格被绑定到所创建的数据集,但是,数据集本身不会被自动填写。相反,您必须自己调用数据适配器方法来填充数据集。有关填充数据集的详细信息,请参见数据集介绍。
即使在数据集被填充后,DataGrid 控件仍不会自动显示数据。您必须将网格显式绑定到它的数据源。有关更多信息,请参见 Web 窗体页中的数据绑定介绍。
填充数据集并在 DataGrid 控件中显示数据
- 双击当前页,在代码编辑器中显示该页的类文件。
- 在 Page_Load 事件处理程序中,调用数据适配器的 Fill 方法并向其传递要填充的数据集:
' Visual Basic SqlDataAdapter1.Fill(DsCategories1) //C# sqlDataAdapter1.Fill(dsCategories1);
- 调用 DataGrid 控件的 DataBind 方法,将该控件绑定到数据集。但是,您不想在页每次进行往返过程时都重新绑定控件,因为如果这样做,将丢失用户已在网格中进行的更改。因此,您应该只在以下这些情况下绑定网格:
- 第一次调用页时。
- 数据集更改时。
现在,您要在第一次调用页时绑定网格,这可以通过测试页的 IsPostBack 属性完成。在调用了适配器的 Fill 方法之后将代码添加到 Page_Load 事件处理程序。完整的处理程序看起来如下所示:
' Visual Basic Private Sub Page_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load 'Put user code to initialize the page here SqlDataAdapter1.Fill(DsCategories1) If Not IsPostBack Then DataGrid1.DataBind() End If End Sub // C# private void Page_Load(object sender, System.EventArgs e) { // Put user code to initialize the page here sqlDataAdapter1.Fill(dsCategories1); if (!IsPostBack) { DataGrid1.DataBind(); } }
添加编辑数据的功能
按照配置,DataGrid 控件将显示 Categories 表中的信息。但您还希望用户可以逐个编辑网格中的行。要实现该目的,向网格的每一行添加一个“编辑”按钮。用户单击该按钮,网格以编辑模式重新显示,用户可以在文本框中逐个编辑列。当行处于编辑模式时,“编辑”按钮由另外两个按钮(一个“更新”按钮和一个“取消”按钮)代替,如下图所示:
您可以设置 DataGrid 控件的属性显示这些按钮。但是,这些按钮不是自动建立连接的;相反,当单击“编辑”、“取消”或“更新”按钮时,它引发您响应的事件。您需要添加短事件处理程序,它们完成以下操作:
- “编辑”按钮 — 将当前行设置为编辑模式。
- “取消”按钮 — 将当前行返回到显示模式。
“更新”按钮的事件处理程序更复杂,这是因为它执行更新,而这正是本演练的核心。阅读下一节,您将了解有关这方面的内容。
向 DataGrid 控件添加编辑功能
- 在 Web 窗体设计器的“设计”视图中,选择 DataGrid 控件,按 F4 键显示“属性”窗口,然后单击该窗口底部的“属性生成器”。
显示“DataGrid1 属性”对话框。
- 单击“列”选项卡。
- 在“列列表”下面,在“可用列”列表中向下滚动,打开“按钮列”节点。
- 选择“编辑、更新、取消”并单击“添加”() 按钮将这些按钮添加到“选定的列”框中。
- 单击“确定”。
DataGrid 控件重新显示,同时在左侧的列中显示“编辑”链接按钮 (LinkButton)。(最初,您看不到“更新”和“取消”按钮。)
既已具有了“编辑”按钮,您需要创建事件处理程序来设置行的编辑模式。若要控制编辑模式,请将 DataGrid 控件的 EditItemIndex 属性设置为要编辑行的索引(从零开始的)。例如,若要将第三行设置为编辑模式,请将该属性设置为 2。若要某行返回到显示模式,请将该属性设置为 –1。在更改了编辑模式后,您必须重新绑定网格使其显示该行中的数据。
您可以通过传递到处理程序的事件对象确定用户当前所在的行。这些事件的事件对象包含一个 Item 属性,该属性表示正在被更新的整个 DataGrid 行。Item 对象又支持多个属性,其中包含 Item.ItemIndex 属性,该属性返回您正在操作的行的索引值。
设置编辑模式
- (在 Visual Basic 中)右击该页并选择“查看代码”以在代码编辑器中打开该页的类文件。
- 或 -
(在 Visual C# 中)在“设计”视图中,选择网格并按 F4 键打开“属性”窗口。
- (在 Visual Basic 中)在代码编辑器上方的左侧下拉列表中选择“DataGrid1”。
- 或 -
(在 Visual C# 中)单击“属性”窗口顶部的“事件”按钮 ()。
- (在 Visual Basic 中)在代码编辑器顶部右侧的下拉列表中选择“EditCommand”。
- 或 -
(在 Visual C# 中)双击“属性”窗口中的“EditCommand”。
创建了一个 DataGrid1_EditCommand 处理程序。
- 为 CancelCommand 事件重复第二步和第三步。
创建了一个 DataGrid1_CancelCommand 处理程序。
- 在 EditCommand 事件处理程序中添加下面的代码:
' Visual Basic DataGrid1.EditItemIndex = e.Item.ItemIndex DataGrid1.DataBind() // C# DataGrid1.EditItemIndex = e.Item.ItemIndex; DataGrid1.DataBind();
- 在 CancelCommand 事件处理程序中添加下面的代码:
' Visual Basic DataGrid1.EditItemIndex = -1 DataGrid1.DataBind() // C# DataGrid1.EditItemIndex = -1; DataGrid1.DataBind();
测试到目前为止具有的功能
现在您具有一个 DataGrid 控件,该控件显示数据集(在从数据库填充了数据集之后)中的数据,并且还对网格进行了配置,每行都有一个“编辑”按钮。虽然页尚未完成,但现在对其进行测试以确保编辑功能可以正常工作是十分有用的。
测试目前具有的功能
- 在解决方案资源管理器中,右击 Web 窗体页并选择“在浏览器中查看”。
当前项目将被编译,Web 窗体页将显示在设计器的浏览器窗格中。如果一切工作正常,则网格将填充有数据,网格的第一列将包含标有“编辑”的链接。
- 单击网格中任何行的“编辑”链接。
将重新显示该行,并带有以下更改:
- “编辑”替换为“更新”和“取消”链接。
- 数据重新显示在 TextBox 控件中。
- 单击“取消”。
该行重新显示在其原始窗体中。
下一节详细介绍如何让“更新”链接将更改从网格写入数据集和数据库。
更新数据集和数据库
至此,DataGrid 控件已完成了仅显示数据所涉及的大部分工作。但是,与其他 ASP.NET 服务器控件一样,该网格不包括自动更新条款(即接受用户在网格中进行的更改,并将更改发送回数据源)。若要执行更新,您需要编写一些代码。
更新实际以两个阶段发生。首先,您必须用在网格中进行的更改更新数据集。然后,您必须将数据集中的更改写回数据库。有关更多信息,请参见数据集更新介绍。
当网格行处于编辑模式时,最左边的一列包含“更新”链接。当用户单击该链接时,它引发一个 UpdateCommand 事件。您将在该事件的处理程序中编写所有更新代码。
创建 UpdateCommand 处理程序
- (在 Visual Basic 中)如果尚未将其打开,则再次打开代码编辑器。
- 或 -
(在 Visual C# 中)在“设计”视图中,选择网格并按 F4 键打开“属性”窗口。
- (在 Visual Basic 中)在代码编辑器上方的左侧下拉列表中选择“DataGrid1”。
- 或 -
(在 Visual C# 中)单击“属性”窗口顶部的“事件”按钮 ()。
- (在 Visual Basic 中)在代码编辑器顶部右侧的下拉列表中选择“UpdateCommand”。
- 或 -
(在 Visual C# 中)双击网格中的“UpdateCommand”。
创建了一个 DataGrid1_UpdateCommand 事件处理程序。
从 DataGrid 控件进行更新的步骤
您将执行的操作的大纲如下:
- 确定 DataGrid 控件中哪一行(根据索引)已被更新。然后,从该网格行获取数据键,以便确定正在更新的行(根据 ID)。
- 从用户更新的网格行获取更改的值。
- 使用数据键值在数据集表中查找对应的行,然后将更改写入该行。此时,您已更新了数据集,但未更新数据库本身。
- 将更改从数据集发送到数据库。这执行将更改从数据集复制到数据库的 SQL 命令或存储过程。
- 刷新 DataGrid 控件的内容。
- 下面一节解释这些步骤中每一步的详细情况。如果愿意,您可以跳过这些解释,直接进入随后的代码。
从 DataGrid 控件进行更新
- 通过获取传入事件对象的行(Item 对象)的 ItemIndex 属性确定哪个 DataGrid 行已被更新。然后使用该索引值从网格的 DataKeys 集合中获取对应的值。
' Visual Basic Dim key As String = DataGrid1.DataKeys(e.Item.ItemIndex).ToString() // C# string key = DataGrid1.DataKeys[e.Item.ItemIndex].ToString();
- 从 DataGrid 行中获取更改的值。若要完成该操作,请:
- 从传入事件对象的项的 Cells 集合中获取适当的单元格(从零开始的)。例如,网格中最左边一列为 Cells(0)。
- 对于每个单元格,获取其 Controls 集合,它包含显示在该单元格中的所有元素。
- 从该集合中获取且仅获取第一个控件,在本例中为 TextBox 控件。若要获取 TextBox,声明一个类型 TextBox 的局部变量,并将 Controls 集合中的对象分配给它。
- 获取 TextBox 控件的值(其 Text 属性)。
下面的示例显示如何执行这些步骤。
' Visual Basic Dim categoryName, categoryDescription As String Dim tb As TextBox tb = CType(e.Item.Cells(2).Controls(0), TextBox) categoryName = tb.Text tb = CType(e.Item.Cells(3).Controls(0), TextBox) categoryDescription = tb.Text // C# string categoryName; string categoryDescription; TextBox tb; tb = (TextBox) e.Item.Cells[2].Controls[0]; categoryName = tb.Text; tb = (TextBox) e.Item.Cells[3].Controls[0]; categoryDescription = tb.Text
- 在数据表中查找对应的行。类型化的 dsCategories 数据集包含一个特殊的 FindBy 方法(在本例中为 FindByCategoryID 方法),该方法通过行的主键定位行,并返回一个对行的引用。创建类型化数据行的变量并调用该方法:
' Visual Basic Dim r As dsCategories.CategoriesRow r = DsCategories1.Categories.FindByCategoryID(key) // C# dsCategories.CategoriesRow r; r = dsCategories1.Categories.FindByCategoryID(int.Parse(key));
- 通过更改您在第三步所在行中的值更新该行,如下面的示例所示:
' Visual Basic r.CategoryName = categoryName r.Description = categoryDescription // C# r.CategoryName = categoryName; r.Description = categoryDescription;
- 通过调用数据适配器的 Update 方法将更改从数据集发送到数据库:
' Visual Basic SqlDataAdapter1.Update(DsCategories1) DataGrid1.DataBind() // C# sqlDataAdapter1.Update(dsCategories1); DataGrid1.DataBind();
- 将网格中的当前行切换出编辑模式。
' Visual Basic DataGrid1.EditItemIndex = -1 // C# DataGrid1.EditItemIndex = -1;
- 数据绑定 DataGrid 控件:
' Visual Basic DataGrid1.DataBind() // C# DataGrid1.DataBind();
下面的代码显示完成的 UpdateCommand 事件处理程序是什么样的。复制该代码并将其粘贴到 Web 窗体页的类文件。
提示 一定要改写先前创建的主干事件处理程序,否则将有两个方法具有相同的名称和签名。
' Visual Basic Private Sub DataGrid1_UpdateCommand(ByVal source As Object, ByVal e As System.Web.UI.WebControls.DataGridCommandEventArgs) Handles DataGrid1.UpdateCommand Dim categoryName, categoryDescription As String ' Gets the value of the key field of the row being updated Dim key As String = DataGrid1.DataKeys(e.Item.ItemIndex).ToString() ' Gets get the value of the controls (textboxes) that the user ' updated. The DataGrid columns are exposed as the Cells collection. ' Each cell has a collection of controls. In this case, there is only one ' control in each cell -- a TextBox control. To get its value, ' you copy the TextBox to a local instance (which requires casting) ' and extract its Text property. ' ' The first column -- Cells(0) -- contains the Update and Cancel buttons. Dim tb As TextBox ' Gets the value the TextBox control in the third column tb = CType(e.Item.Cells(2).Controls(0), TextBox) categoryName = tb.Text ' Gets the value the TextBox control in the fourth column tb = CType(e.Item.Cells(3).Controls(0), TextBox) categoryDescription = tb.Text ' Finds the row in the dataset table that matches the ' one the user updated in the grid. This example uses a ' special Find method defined for the typed dataset, which ' returns a reference to the row. Dim r As dsCategories.CategoriesRow r = DsCategories1.Categories.FindByCategoryID(key) ' Updates the dataset table. r.CategoryName = categoryName r.Description = categoryDescription ' Calls a SQL statement to update the database from the dataset SqlDataAdapter1.Update(DsCategories1) ' Takes the DataGrid row out of editing mode DataGrid1.EditItemIndex = -1 ' Refreshes the grid DataGrid1.DataBind() End Sub // C# private void DataGrid1_UpdateCommand(object source, System.Web.UI.WebControls.DataGridCommandEventArgs e) { string categoryName, categoryDescription; // Gets the value of the key field of the row being updated string key = DataGrid1.DataKeys[e.Item.ItemIndex].ToString(); // Gets get the value of the controls (textboxes) that the user // updated. The DataGrid columns are exposed as the Cells collection. // Each cell has a collection of controls. In this case, there is only one // control in each cell -- a TextBox control. To get its value, // you copy the TextBox to a local instance (which requires casting) // and extract its Text property. // // The first column -- Cells(0) -- contains the Update and Cancel buttons. TextBox tb; // Gets the value the TextBox control in the third column tb = (TextBox)(e.Item.Cells[2].Controls[0]); categoryName = tb.Text; // Gets the value the TextBox control in the fourth column tb = (TextBox)(e.Item.Cells[3].Controls[0]); categoryDescription = tb.Text; // Finds the row in the dataset table that matches the // one the user updated in the grid. This example uses a // special Find method defined for the typed dataset, which // returns a reference to the row. dsCategories.CategoriesRow r; r = dsCategories1.Categories.FindByCategoryID(int.Parse(key)); // Updates the dataset table. r.CategoryName = categoryName; r.Description = categoryDescription; // Calls a SQL statement to update the database from the dataset sqlDataAdapter1.Update(dsCategories1); // Takes the DataGrid row out of editing mode DataGrid1.EditItemIndex = -1; // Refreshes the grid DataGrid1.DataBind(); }
测试
现在您已完成所有操作。若要阐释网格如何工作并确保它正确更新数据,您应该对页进行测试。
对页进行测试
- 在解决方案资源管理器中,右击 Web 窗体页并选择“在浏览器中查看”。
当前项目将被编译,Web 窗体页将显示在设计器的浏览器窗格中。
- 单击网格中任何一行的“编辑”链接,然后使用文本框编辑该行。
- 单击“更新”。
网格重新显示,并带有您的更改。如果检查数据库,您将看到已适当写入更改。