可以使用 datagrid 创建列表数据而没有使用
我知道您不会再使用如下所示的代码,但 asp.net 领域中许多守旧的用户仍在继续使用它们:
response.write("<table>")
while mydatareader.read()
response.write("<tr>")
response.write("<td>")
response.write(mydatareader(0))
response.write("</td>")
response.write("</tr>")
loop
response.write("</table>")
可以对以上代码进行简化,使其仅为:
<asp:datagrid runat="server" datasource="mydatareader"/>
并调用 .databind() 方法。即使需要对html输出进行特殊的控制,您也可以在用户界面上记录集的内容重复出现的情况下,使用某个数据 web 控件。
忘记在 page_load 事件中检查 ispostback
最常见的错误之一是忘记在数据绑定之前检查页面的 ispostback 条件。例如,datagrid 处于“edit”(编辑)模式时,忽略该项检查将导致已编辑的值被数据源中的原始值覆盖。然而,该规则至少有一个主要的例外,请参阅持续使用大型 viewstate。
以下是包含 ispostback 检查的一个典型 page_load 事件。bindgrid() 是一个例程,用于导入并设置 datagrid 的数据源,并调用 databind() 方法。
sub page_load
if not ispostback then
bindgrid()
end if
end sub
需要更大的灵活性时,仍坚持使用自动生成的列
如果 datagrid 所处的环境需要任何一种特殊格式,或是需要使用 datagrid 中的其他任何 web 控件,那么必须关闭 autogeneratecolumns。将 autogeneratecolumns 属性的设置保持为“true”(默认设置)的做法,仅在最简单的 datagrid 方案中有效。但对几乎所有实际的应用程序,必须将该属性设置为“false”,并在 datagrid 声明的 <columns></columns> 段中明确地指定列。microsoft visual studio? .net 用户可以使用属性生成器以图形化的方式创建这些列。
注意:如果将 autogeneratecolumns 的设置保持为“true”,并且在 datagrid 的 <columns> 段中指定了列,那么最终将得到对列的重复设置。系统将首先显示特别声明的列,随后是所有自动生成的列。
尝试仅使用控件 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
datagrid 控件会在页面中添加大量的 viewstate,这一点令人讨厌,因为这会导致呈现给用户的页面的总体大小急剧增加。要使页面大小不增加,最简单的方法是无论对整个页面,还是单独对某些特定的控件,都禁用 viewstate。例如,如果页面不产生回发,那么对整个页面禁用 viewstate 是安全的。否则,请对两次回发之间状态信息不会发生更改的各个控件禁用 viewstate,或者对不需要隐藏字段来跟踪自身状态的那些控件禁用 viewstate。
对 datagrid 控件或包含 datagrid 的页面禁用 viewstate 时,如果 datagrid 会启动回发事件,那么需要执行一些特殊的步骤。首先,必须在每次回发时在 page_load 中重新绑定 datagrid。这有违常规做法(以及上述第二个问题中的描述)。但如果禁用 viewstate,该步骤是必需的,这样在执行 page_load 后可以正确地引发其他 datagrid 事件。如果要处理以下 datagrid 事件中的任何一部分(或全部),那么还需要在 viewstate 中手动存储一些 datagrid 属性。例如,在禁用了 viewstate 的 datagrid 中进行编辑时,只要是在 page_load 中第一次绑定 datagrid 之前重新存储 edititemindex,且 datagrid 处于编辑模式,那么只需将 edititemindex 储存到 viewstate 就够了。
表 1:datagrid 事件与 viewstate 的依赖关系
事件 是否依赖于 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!
sub datagrid1_itemdatabound(source as object,e as datagriditemeventargs)
if (e.item.itemtype = listitemtype.item or e.item.itemtype = listitemtype.alternatingitem) then
if e.item.dataitem("forumdate") < datetime.today then
e.item.cells(1).backcolor =system.drawing.color.fromname("#ffccff")
end if
end if
end sub
需要对生成的 html 有更多的控制时,过多地使用了 datagrid(repeater 也许是更好的选择)
如果懒散的程序员喜欢 datagrid 控件(因为 datagrid 控件为他们完成了很多工作),那么有着极强控制欲的程序员必定喜欢 repeater 控件。如果需要或希望完全控制创建的所有 html,请使用 repeater 控件,它能帮助您完成该任务。repeater 控件在性能上也略占优势,因为它不像 datagrid 控件的所有内置功能那样占用系统资源。也可以考虑使用折衷的 datalist 控件,它具备编辑和排序功能,同时还具有在一行内重复显示记录的功能。
我知道您不会再使用如下所示的代码,但 asp.net 领域中许多守旧的用户仍在继续使用它们:
response.write("<table>")
while mydatareader.read()
response.write("<tr>")
response.write("<td>")
response.write(mydatareader(0))
response.write("</td>")
response.write("</tr>")
loop
response.write("</table>")
可以对以上代码进行简化,使其仅为:
<asp:datagrid runat="server" datasource="mydatareader"/>
并调用 .databind() 方法。即使需要对html输出进行特殊的控制,您也可以在用户界面上记录集的内容重复出现的情况下,使用某个数据 web 控件。
忘记在 page_load 事件中检查 ispostback
最常见的错误之一是忘记在数据绑定之前检查页面的 ispostback 条件。例如,datagrid 处于“edit”(编辑)模式时,忽略该项检查将导致已编辑的值被数据源中的原始值覆盖。然而,该规则至少有一个主要的例外,请参阅持续使用大型 viewstate。
以下是包含 ispostback 检查的一个典型 page_load 事件。bindgrid() 是一个例程,用于导入并设置 datagrid 的数据源,并调用 databind() 方法。
sub page_load
if not ispostback then
bindgrid()
end if
end sub
需要更大的灵活性时,仍坚持使用自动生成的列
如果 datagrid 所处的环境需要任何一种特殊格式,或是需要使用 datagrid 中的其他任何 web 控件,那么必须关闭 autogeneratecolumns。将 autogeneratecolumns 属性的设置保持为“true”(默认设置)的做法,仅在最简单的 datagrid 方案中有效。但对几乎所有实际的应用程序,必须将该属性设置为“false”,并在 datagrid 声明的 <columns></columns> 段中明确地指定列。microsoft visual studio? .net 用户可以使用属性生成器以图形化的方式创建这些列。
注意:如果将 autogeneratecolumns 的设置保持为“true”,并且在 datagrid 的 <columns> 段中指定了列,那么最终将得到对列的重复设置。系统将首先显示特别声明的列,随后是所有自动生成的列。
尝试仅使用控件 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
datagrid 控件会在页面中添加大量的 viewstate,这一点令人讨厌,因为这会导致呈现给用户的页面的总体大小急剧增加。要使页面大小不增加,最简单的方法是无论对整个页面,还是单独对某些特定的控件,都禁用 viewstate。例如,如果页面不产生回发,那么对整个页面禁用 viewstate 是安全的。否则,请对两次回发之间状态信息不会发生更改的各个控件禁用 viewstate,或者对不需要隐藏字段来跟踪自身状态的那些控件禁用 viewstate。
对 datagrid 控件或包含 datagrid 的页面禁用 viewstate 时,如果 datagrid 会启动回发事件,那么需要执行一些特殊的步骤。首先,必须在每次回发时在 page_load 中重新绑定 datagrid。这有违常规做法(以及上述第二个问题中的描述)。但如果禁用 viewstate,该步骤是必需的,这样在执行 page_load 后可以正确地引发其他 datagrid 事件。如果要处理以下 datagrid 事件中的任何一部分(或全部),那么还需要在 viewstate 中手动存储一些 datagrid 属性。例如,在禁用了 viewstate 的 datagrid 中进行编辑时,只要是在 page_load 中第一次绑定 datagrid 之前重新存储 edititemindex,且 datagrid 处于编辑模式,那么只需将 edititemindex 储存到 viewstate 就够了。
表 1:datagrid 事件与 viewstate 的依赖关系
事件 是否依赖于 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!
sub datagrid1_itemdatabound(source as object,e as datagriditemeventargs)
if (e.item.itemtype = listitemtype.item or e.item.itemtype = listitemtype.alternatingitem) then
if e.item.dataitem("forumdate") < datetime.today then
e.item.cells(1).backcolor =system.drawing.color.fromname("#ffccff")
end if
end if
end sub
需要对生成的 html 有更多的控制时,过多地使用了 datagrid(repeater 也许是更好的选择)
如果懒散的程序员喜欢 datagrid 控件(因为 datagrid 控件为他们完成了很多工作),那么有着极强控制欲的程序员必定喜欢 repeater 控件。如果需要或希望完全控制创建的所有 html,请使用 repeater 控件,它能帮助您完成该任务。repeater 控件在性能上也略占优势,因为它不像 datagrid 控件的所有内置功能那样占用系统资源。也可以考虑使用折衷的 datalist 控件,它具备编辑和排序功能,同时还具有在一行内重复显示记录的功能。
|