尝试仅使用控件 ID 来引用 Datagrid 项目中的控件
许多人没有认识到,对于 Datagrid 的 TemplateColumn 下的 ItemTemplate 中的控件(例如带有“MyTextBox”ID 的 TextBox 控件),不能在后面的代码或是在 ASPX 页面的 <script> 段中用如下所示的代码来直接调用该控件:
Dim MyValue As String = MyTextBox.Text
该代码将导致可怕的“名称‘MyTextBox’没有声明”错误。
因为 Datagrid 是由多个行(项目)组成的,所以数据源中的每一行实际都会有一个单独的“MyTextBox”实例。ASP.NET 在每个控件的 ID 前面加上该控件层次结构中每个命名容器的 ID,这样 Textbox 将具有唯一的 ID,与页面中所有其他控件的 ID 都不相同。例如,如果 MyTextBox 处于 DataGrid1 中,那么生成的 ID 将是 DataGrid1:_ctl2:MyTextBox。“_ctl2”代表 MyTextBox 所处的当前行。页面中其他 MyTextBox 实例的 ID 可能是 DataGrid1:_ctl3:MyTextBox、DataGrid1:_ctl4:MyTextBox 等等。要检索需要查找的“MyTextBox”值,需要对适当的 DataGridItem 调用 FindControl 方法。该 DataGridItem 用作 TextBox 的父命名容器。
HTML:
<asp:Datagrid runat="server" id="Datagrid1">
<Columns>
<asp:TemplateColumn>
<ItemTemplate>
<asp:TextBox runat="server" id="MyTextBox"/>
</ItemTemplate>
</asp:TemplateColumn>
</Columns>
代码:
Sub DataGrid1_UpdateCommand(sender As Object, e As DataGridCommandEventArgs)
Dim MyValue As String = CType(e.Item.FindControl("MyTextBox"), TextBox).Text
'对 MyValue 执行操作
End Sub
对 FindControl 调用的结果调用 CType,将会把返回值由 Object 类型强制转换成 TextBox 类型,以访问 .Text 属性。
忘记在每个 Datagrid 事件中执行 .DataBind() 调用,从而导致回发
一个常见的问题是:“当我点击 Datagrid 某一行中的 Edit(编辑)链接时,页面回发,且不包含任何数据。这是什么错误?”问题在于数据仅在页面第一次被调用时绑定到网格。在每个 Datagrid 事件(Edit、Update、Cancel、Page 或 Sort)中,请确保设置了 Datagrid 的 Datasource 属性(除非已经在 <asp:Datagrid> 声明中通过声明的方式进行了设置),并对 Datagrid 调用了 DataBind() 方法。
运行时不必要地在 Datagrid 中动态创建 Datagrid 控件或列
在某些业务和技术方案中,在运行时创建 ASP.NET 控件是必要的,也是完全合适的。例如,有时需要在选择其他页面选项后,才能在运行时确定用户界面。或是要创建一个复合服务器控件,其中的每个子控件都需要动态创建,因为无法以声明的方式创建这些子控件。如果遇到这些情况,请注意,提交页面时不要保留这些动态控件。必须在页面生命周期的早期,在每次回发时重新创建动态控件(例如在 Page_Init 事件中)。警言:创建控件要早,创建控件要勤。有关如何动态创建控件的详细信息,请参阅 Microsoft Knowledge Base 文章 HOW TO:Dynamically Create Controls in ASP.NET with Visual Basic .NET。
然而,如果 Datagrid 应用程序中不是一定需要动态创建控件,请避免使用该技术,以免遇到麻烦。尽管可能创建动态 Datagrid,但它们会引发各种事件,这通常都会令人头疼。换句话说,不要动态创建控件,以避免因为创建控件使 ASPX 文件变得散乱。
事件 | 是否依赖于 ViewState? | 要存储在 ViewState 中的字段 |
ItemCreated | 无 | |
ItemDataBound | 无 | |
SortCommand | 是 | SortExpression |
EditCommand | 是 | EditItemIndex |
PageIndexChanged | 是 | CurrentPageIndex |
SelectedIndexChanged | 无 |
清单 1:启用编辑、排序和分页,但禁用 ViewState 的 Datagrid 的示例代码。
Sub Page_Load
If Not ViewState("EditItemIndex") Is Nothing Then
Datagrid1.EditItemIndex = ViewState("EditItemIndex")
End If
If Not ViewState("CurrentPageIndex") Is Nothing Then
Datagrid1.CurrentPageIndex = ViewState("CurrentPageIndex")
End If
BindGrid()
End Sub
Sub BindGrid()
Dim DV As DataView
DV = GetDataSource()
DV.Sort = ViewState("SortExpression")
Datagrid1.DataSource = DV
Datagrid1.DataBind()
End Sub
Sub Datagrid1_SortCommand(s As Object, e As DataGridSortCommandEventArgs)
ViewState("SortExpression") = e.SortExpression
BindGrid()
End Sub
Sub Datagrid1_EditCommand(s As Object, e As DatagridCommandEventArgs)
Datagrid1.EditItemIndex = e.Item.ItemIndex
ViewState("EditItemIndex") = e.Item.ItemIndex
BindGrid()
End Sub
Sub Datagrid1_PageIndexChanged(s as Object, e As DataGridPageChangedEventArgs)
Datagrid1.CurrentPageIndex = e.NewPageIndex
ViewState("CurrentPageIndex") = e.NewPageIndex
BindGrid()
End Sub
使用 ItemDataBound 或 ItemCreated 事件时,忘记检查适当的 ListItemType
Datagrid 控件对每个数据行引发两个事件。首次将每行添加到 Datagrid 时将引发 ItemCreated 事件,将数据绑定到每行时将引发 ItemDataBound 事件。添加单元格到 Datagrid 的表格输出时,这些事件可以用于控制每个单元格的外观或内容。例如,可以基于数值的范围修改单元格的背景颜色。但关键是要记住,这些事件的引发针对的是所有 Datagrid 项目类型,包括页眉、页脚和分页程序项目。如果执行 ItemDataBound 事件期间,没有在引用项目的数据之前仔细检查项目类型,第一个项目(通常是标题行)就将发生错误。如果 Datagrid 启用了分页,且将其设置为在顶端显示,那么第一个项目就会成为分页程序项目。以下示例代码显示如何在引用项目数据之前进行正确的 ListItemType 检查。不要忘了 AlternatingItem!
……