简介
不得不承认,为 Microsoft? ASP.NET 编写 Datagrid 代码包括大量的重复工作。尽管我深受该控件的困扰,但我还是不断寻找简化这类任务的捷径。谁都不愿意做重复的工作,对不对?为了摆脱这种烦人的工作,我们要将多个项目中的重复代码封装到一个可重复使用的程序包中。这才是面向对象的编程工作所要解决的问题,使用 Datagrid 也不例外。对于 Datagrid 控件来说,要实现该目的,需要将常用的代码放到一个内置的列类型中(将在第一个示例中使用),然后创建一个可以在多个 Datagrid 对象中重复使用的自定义列类型。
本文介绍使用标准 TemplateColumn 在一个 Datagrid 列中使用 DropDownList 控件的过程,然后将该逻辑转换为您的自定义 Datagrid 列类型,我称其为 DropDownColumn。已经为您创建了一些免费的 Datagrid 列,您可以到 MetaBuilders.com 下载所需的类型。
重复使用的情况
如果您的小组与许多组织一样,已经将业务逻辑和/或数据访问逻辑分成单独的组件或组件集,而剩下的 ASP.NET 文件、ASPX 及其代码只包含纯粹的表示逻辑。(“纯粹”是一个相对的词语。)但是,即使是表示层的逻辑有时也会重复使用,这样,下次某个用户来到您门前说,“我想让我的应用程序在‘财务’方面看起来与苏茜的一样”时,您可以重复使用“财务”应用程序的部分表示内容快速为其构建一个这样的应用程序。您可能还想将一些逻辑打包,在 Web 上销售或在您的 Web 站点分发。ASP.NET 使这一切比以往更容易实现,因为它使您可以创建自己的服务器控件,或从现有的类型导出列类型,从而获得所需的功能。
DropDownList 方案
假设您正在本地 Microsoft SQL Server? 中编辑 Northwind 数据库(或做其他工作),您想使您的用户(我们称之为 Ed,仓库保管员)可以编辑 Orders 表。其中一个字段包含运输信息 (ShipVia),Ed 要能够修改该字段的信息。在显示模式下,运输公司应显示为纯文本。当 Ed 单击 Edit(编辑)按钮时,您不光要为他提供一个 TextBox 以编辑运输方式代码(1、2 或 3),还要为他提供一个包含可以选择不同运输公司的 DropDownList。(因为 Ed 记不住哪个运输公司对应哪个号码,所以,DropDownList 方案可以帮助他解决这个问题。)
图 1:选择运输公司
内置的 Datagrid 列
了解问题的大概情况后,现在我们后退一步,看一下 ASP.NET 中构建的 5 种 Datagrid 列类型及其父类型 DataGridColumn。
BoundColumn。这是文本字段的标准显示。它显示为纯文本,但是当 Datagrid 处于“编辑”模式时,它将转换为 TextBox。还可以选择格式化选项。
HyperlinkColumn。用于显示文本数据,还代表一个 Web 地址 (URL)。URL 可以与显示文本相同,也可以不同,它们都可以单独设置。它显示为 <a href=...> 标记。
ButtonColumn。它使用户能够按行与网格进行交互。它可以显示为超链接 LinkButton (<a href=...>) 或 Pushbutton (<input type="button">)。单击该按钮时将触发 PostBack,而在 Datagrid 上触发 ItemCommand 事件。
EditCommandColumn。它与 ButtonColumn 类似,但是它自动创建用于编辑 Datagrid、取消或提交更改的按钮。触发 ItemCommand 事件,以及所单击按钮的特定事件:EditCommand、CancelCommand 或 UpdateCommand。
TemplateColumn。用于完全控制显示给用户的控件,分为多种模板,例如 ItemTemplate 和 EditItemTemplate。任何 ASP.NET 或 HTML 控件或控件组都可以放置在这些模板中。
注意:直接使用这些列类型之前,请关闭 AutoGenerateColumns(运行时自动生成列)。然后,您可以在属性生成器中使用这些列类型,或者直接在 ASPX 文件的 HTML 代码中使用。
图 2:从 DataGridColumn 中继承的 5 种内置列
尽管这些列类型非常有用,它们不过是了解 Datagrid 列内容的开始。
传统方法:TemplateColumn 中的 DropDownList
在研究如何创建新列类型之前,首先让我们看一下如何通过直接在 TemplateColumn 内使用 DropDownList 解决下拉列表的问题,而不用自定义列。ItemTemplate 将只包含表示当前值的纯文本表示,而 EditItemTemplate 包含一个需要在运行时管理的 <asp:DropDownList> 控件。
<asp:DataGrid id="DataGrid1" runat="server" CssClass="grid" AutoGenerateColumns="False">
<Columns>
<asp:EditCommandColumn EditText="Edit" CancelText="Cancel" UpdateText="Update" />
<asp:BoundColumn DataField="OrderID" ReadOnly="True" HeaderText="Order ID" />
<asp:BoundColumn DataField="ShipName" HeaderText="Ship to" ReadOnly="True" />
<asp:BoundColumn DataField="ShipCountry" HeaderText="Country" ReadOnly="True" />
<asp:TemplateColumn HeaderText="Ship Method">
<ItemTemplate>
<%#Container.DataItem("ShipVia")%>
</ItemTemplate>
<EditItemTemplate>
<asp:DropDownList runat="server" ID="Dropdownlist1"/>
</EditItemTemplate>
</asp:TemplateColumn>
</Columns>
</asp:DataGrid>
绑定 Datagrid 的代码:
Sub BindGrid()
Dim SQL As String = "SELECT OrderID,
ShipName, ShipCountry, ShipVia FROM Orders"
Dim DA As SqlDataAdapter = New SqlDataAdapter(SQL, ConnStr)
Dim DS As New DataSet
DA.Fill(DS, "Orders")
DataGrid1.DataSource = DS.Tables("Orders").DefaultView
DataGrid1.DataBind()
End Sub
当前编辑的项目是在触发 Datagrid 的 ItemDataBound 事件时绑定到 DropDownList 的。使用 ItemDataBound 事件时,请检查当前项目的 ListItemType,否则您可能会发现您正在使用 HeaderItem 或其他不适用的项目类型。为 EditItem 引用 DropDownList 控件。在下面的代码中,我直接使用单元格控件集进行说明(为了与后面的示例保持一致),但是,您可以采用简单的方法,直接为 DropDownList 控件指定 ID,并使用 Datagrid 项目的 FindControl 方法定位控件引用。由于 Datagrid 被绑定到 DataTable 的默认视图,而该视图的元素属于 DataRowView 类型,所以您可以将当前项目的 DataItem 属性转换为一个 DataRowView 实例。这样,您可以按字段名直接引用 DataItem 中的字段。使用这种方法,将“ShipVia”的当前值保存到该记录中,并使用它选择相应的下拉列表项。
Private Sub DataGrid1_ItemDataBound(ByVal sender As Object, _
ByVal e As System.Web.UI.WebControls.DataGridItemEventArgs) _
Handles DataGrid1.ItemDataBound
If e.Item.ItemType = ListItemType.EditItem Then
Dim DRV As DataRowView = CType(e.Item.DataItem, DataRowView)
Dim CurrentShip As String = DRV("ShipVia")
Dim DDL As DropDownList = CType(e.Item.Cells(4).Controls(1), DropDownList)
Dim SQL As String = "SELECT ShipperID, CompanyName FROM Shippers ORDER BY ShipperID"
Dim DA As SqlDataAdapter = New SqlDataAdapter(SQL, ConnStr)
Dim DS As New DataSet
Dim item As ListItem
DA.Fill(DS, "Shippers")
DDL.DataSource = DS.Tables("Shippers").DefaultView
DDL.DataTextField = "CompanyName"
DDL.DataValueField = "ShipperID"
DDL.DataBind()
item = DDL.Items.FindByValue(CurrentShip)
If Not item Is Nothing Then item.Selected = True
End If
End Sub
最后,编写从 DropDownList 中检索当前选定值的代码,并执行数据库更新:
Private Sub DataGrid1_UpdateCommand(ByVal source As Object, _
ByVal e As System.Web.UI.WebControls.DataGridCommandEventArgs) _
Handles DataGrid1.UpdateCommand
Dim DDL As DropDownList = CType(e.Item.Cells(4).Controls(1), DropDownList)
Dim NewShip As Integer = DDL.SelectedValue
Dim OrderID As Integer = Int32.Parse(e.Item.Cells(1).Text)
Dim SQL As String = "UPDATE Orders SET ShipVia=@Ship WHERE OrderID=@ID"
Dim Conn As SqlConnection = New SqlConnection(ConnStr)
Dim Cmd As New SqlCommand(SQL, Conn)
Cmd.Parameters.Add(New SqlParameter("@Ship", NewShip))
Cmd.Parameters.Add(New SqlParameter("@ID", OrderID))
Conn.Open()
Cmd.ExecuteNonQuery()
Conn.Close()
DataGrid1.EditItemIndex = -1
BindGrid()
End Sub
所有列的基础:DataGridColumn
我们回顾一下所有内置列类型的父类型 DataGridColumn。(参见图 1。)它包含所有 Datagrid 列中常用的属性和方法。带星号的类型表示您创建自定义列类型时要使用的类型。
DataGridColumn 属性
FooterStyle (TableItemStyle)
FooterText(字符串)
HeaderImageUrl(字符串)
HeaderStyle (TableItemStyle)
HeaderText(字符串)
ItemStyle (TableItemStyle)
SortExpression(字符串)
Visible(布尔值)
DataGridColumn 方法
Initialize
InitializeCell
LoadViewState
OnColumnChanged
SaveViewState
TrackViewState
可重复使用的方法:创建 DropDownColumn
首先要在 Microsoft Visual Studio .NET 中创建一个新类库,并将其命名为 MyCustomColumn。添加一个新类 DropDownColumn,并确保在您的类定义中添加命名空间,这样您的初始代码应如下所示:
Namespace MyCustomColumn
Public Class DropDownColumn
Inherits DataGridColumn
Public DataSource As ICollection
Public DataField As String
Public DataTextField As String
Public DataValueField As String
End Class
End Namespace
我还声明了 4 个公共属性,如下所示:
DataSource。它是用来填充 DropDownList 的数据集。可以是实现 ICollection 接口的任何内容。在本文的示例中,我使用 ArrayList 和 DataView。
DataField。它是父 Datagrid 数据源中的字段,它与从下拉列表中选定的数据相对应。例如,如果DataSource 包含一个状态集,DataField 将类似于“StateCode”,也可以在表格中使用状态随意命名字段。
DataTextField。这是要显示在下拉列表中的文本,可以是下面的值,也可以不是。
DataValueField。这是表示特殊下拉选项的值。DataValueField 通常是一个整数值或其他代码,而 DataTextField 是对用户来说更有意义的文本说明。
接下来,覆盖 InitializeCell,它是 Datagrid 列的一个固有事件。列中的所有单元格都将发生 InitializeCell,它与直接使用 Datagrid 时的 ItemCreated 事件非常相似。您可以使用它来管理单元格内容,例如设置 HeaderText,添加您将向其中添加数据的 DropDownList 控件。我已经为单元格的 DataBinding 事件添加了处理程序,需要根据当前是否正在编辑行来采取不同的处理方式。每个 System.Web.UI.Control 都有一个 DataBinding 事件,当数据被绑定到控件时,您可以从这里访问底层的数据,本例中为 Datagrid 中的 TableCell 对象。
Public Overrides Sub InitializeCell(ByVal cell As TableCell, _
ByVal columnIndex As Integer, _
ByVal itemType As ListItemType)