考察DataGrid控件 Part 6
考察DataGrid控件 Part 6 (上)
本文英文原版:
http://aspnet.4guysfromrolla.com/articles/071002-1.aspx
名词解释
inline:一种处理数据的方法,即这些数据事先不必经过编辑或分类排序,按照它们到达 的顺序,按次序排队处理,参见http://www.iciba.com/search?s=inline
导言:
在前面的文章里我们考察了如何对DataGrid的结果进行排序,以及如何自定义HTML输出格式.此外,我们可以通过DataGrid方便地检索XML数据。本文,我们考察DataGrid的另一项有用的特点—inline编辑(inline editing)
在很多情况下,我们需要在DataGrid里编辑数据。比如,在一个data-driven网站,你可能有一组administrative Web pages页面,这些页面包含了各种不同类型的数据库表(database tables),并允许管理员修改这些表格里的数据。通过DataGrid,我们可以很轻松的将数据库表的信息显示出来(更多信息请参考 Part 2和Part 5),同样,我们也可以用DataGrid来编辑数据!
使用DataGrid的编辑功能(editing features)是很简单的,相比较而言,为了达到相同的目的,DataGrid所需要的代码比经典ASP页面要少地多.此外,在阅读本文之前,你应该对SQL有所了解,也即相关的构建和更新命令(可以参考官方的SQL在线书籍:http://www.microsoft.com/sql/techinfo/productdoc/2000/books.asp)同样的,你也应该对DataGrid控件本身比较了解,理解其事件(events)处理机制,为次可参考本系列的Part 3部分。
首先:显示数据
在我们将注意力集中到编辑DataGrid里的数据之前,我们首先要将数据用DataGrid显示出来。在前面的文章我们已经深入的探讨过这个问题,在此我不打算花过多笔墨。在本例中我们使用的是GrocerToGo数据库(它是一种Access 2000的数据库,可以在本文结束处下载).在此,我们将要显示(并允
是Products里的数据。
我们将在DataGrid里显示Products表里的如下几个列:ProductID, UnitPrice, ProductName,以及ProductDescription,为了使DataGrid看起来比较美观,还包含了一些界面格式代码,下面的代码将Products表的数据显示在DataGrid:
<%@ Import Namespace="System.Data" %>
<%@ Import Namespace="System.Data.OleDb" %>
<script runat="server">
Sub Page_Load(sender as Object, e as EventArgs)
If Not Page.IsPostBack
BindData()
End If
End Sub
Sub BindData()
'1. Create a connection
Const strConnStr as String = "Provider=Microsoft.Jet.OLEDB.4.0;" & _
"Data Source=C:/GrocerToGo.mdb"
Dim objConn as New OleDbConnection(strConnStr)
objConn.Open()
'2. Create a command object for the query
Const strSQL as String = "SELECT * FROM Products"
Dim objCmd as New OleDbCommand(strSQL, objConn)
'3. Create/Populate the DataReader
Dim objDR as OleDbDataReader
objDR = objCmd.ExecuteReader()
dgProducts.DataSource = objDR
dgProducts.DataBind()
End Sub
</script>
<asp:DataGrid id="dgProducts" runat="server"
AutoGenerateColumns="False" CellPadding="4"
HeaderStyle-BackColor="Black"
HeaderStyle-ForeColor="White"
HeaderStyle-HorizontalAlign="Center"
HeaderStyle-Font-Bold="True">
<Columns>
<asp:BoundColumn HeaderText="Product ID" DataField="ProductID" />
<asp:BoundColumn HeaderText="Price" DataField="UnitPrice"
ItemStyle-HorizontalAlign="Right"
DataFormatString="{0:$#,###.##}" />
<asp:BoundColumn HeaderText="Name" DataField="ProductName" />
<asp:BoundColumn HeaderText="Description"
DataField="ProductDescription" />
</Columns>
</asp:DataGrid>
上述代码浅显易懂,BindData()函数读取Products表的数据到一个DataReader,然后绑定到一个名为dgProducts的DataGrid。
留意Page_Load事件处理器
我们注意到在Page_Load事件处理器里,只有当初次登录页面时,才调用BindData()子程序.也就是说,当页面产生回传时并不会调用BindData()子程序,这是非常关键的!如何你进行修改,每次登录页面时都调用BindData(),你编辑的数值就不会保存在数据库里。欲知详情请参考常见问题答疑:Why Your DataGrid's Updates Don't Show Up。(相关链接为http://datawebcontrols.com/faqs/Editing/EditedValuesNotSaved.shtml)
现在我们将数据显示在DataGrid里,接下来我们要对数据进行编辑。在DataGrid里有一个名为EditCommandColumn的控件,其在DataGrid的每一行显示一个"Edit" 按钮。EditCommandColumn控件是如何允许最终用户编辑某一特定行的呢?我们将在下半部分阐述。
考察DataGrid控件 Part 6 (中)
本文英文原版:
http://aspnet.4guysfromrolla.com/articles/071002-1.2.aspx
在上半部分我们阐述了如何将Products表的数据显示在DataGrid里,在本文我们考察如何允许用户编辑DataGrid里的某一特定行。
允许最终用户编辑DataGrid里的某一行
为了让用户编辑DataGrid里的某一行,我们需要一些途径供用户选择他想编辑的行。就像在Part 3 探讨的一样,我们可以使用ButtonColumn控件为DataGrid里的每一行提供一个可点击的按钮。诚然,这样也可以达到编辑的目的,但看起来会有一点凌乱。幸好,DataGrid提供了EditCommandColumn控件以实现该功能。
EditCommandColumn控件在DataGrid里新添一列,为每一行记录放置一个编辑按钮,当点击该按钮时,将引起页面回传并触发EditCommand事件。就像稍后你将看到的那样,我们需要为这个事件写代码。现在让我们看看如何用EditCommandColumn控件为DataGrid添加编辑按钮。很简单,就像添加BoundColumn或ButtonColumn控件一样:
<asp:DataGrid id="dgProducts" runat="server"
AutoGenerateColumns="False" CellPadding="4"
HeaderStyle-BackColor="Black"
HeaderStyle-ForeColor="White"
HeaderStyle-HorizontalAlign="Center"
HeaderStyle-Font-Bold="True">
<Columns>
<asp:EditCommandColumn EditText="Edit Info"
ButtonType="PushButton"
UpdateText="Update" CancelText="Cancel" />
<asp:BoundColumn HeaderText="Product ID" DataField="ProductID"
ReadOnly="True" />
<asp:BoundColumn HeaderText="Price" DataField="UnitPrice"
ItemStyle-HorizontalAlign="Right"
DataFormatString="{0:$#,###.##}" />
<asp:BoundColumn HeaderText="Name" DataField="ProductName" />
<asp:BoundColumn HeaderText="Description"
DataField="ProductDescription" />
</Columns>
</asp:DataGrid>
EditCommandColumn控件有很多可选属性,比如:ButtonType,它指定按钮类型为超链接(默认为LinkButton)或PushButton;以及为Edit, Update和Cancel按钮指定text的 EditText, UpdateText和CancelText属性;此外,还有其它各种的显示属性,比如HeaderText, ItemStyle等等。
如果你仔细的阅读上述代码,你可能会很吃惊EditCommandColumn控件居然会有UpdateText和CancelText属性。毕竟我只提及过EditCommandColumn控件提供编辑按钮。准确的说,除了要编辑的那一行外,每一行都包含一个编辑按钮,而要编辑的那一行却显示的是2个按钮,Update"和"Cancel"按钮。
选择一行供编辑
DataGrid有一个名为EditItemIndex的属性,它指定那一行记录要编辑。DataGrid从0开始为它包含的行记录排顺序。默认情况下,当没有哪行记录要编辑时EditItemIndex的值为-1.当我们点击某行记录的"Edit"时,将触发EditCommand事件处理器。在该事件处理器里,我们将EditItemIndex属性与要编辑的那一行对应起来,然后重新绑定DataGrid的数据(调用BindData()),代码看起来像下面的这样:
Sub dgProducts_Edit(sender As Object, e As DataGridCommandEventArgs)
dgProducts.EditItemIndex = e.Item.ItemIndex
BindData()
End Sub
将该事件处理器与EditCommand事件对应起来,在DataGrid里进行指定,如下:
<asp:DataGrid id="dgProducts" runat="server"
...
OnEditCommand="dgProducts_Edit"
... >
<Columns>
...
</Columns>
</asp:DataGrid>
有了这些代码,当用户点击"Edit"按钮时,页面发生回传,待编辑行的"Edit" 按钮替换成了"Update"和"Cancel"按钮。另外,方格里的数值由文本值(textual value)自动的转换成处于可编辑的textbox类型的数值了。就像下面图像显示的那样。
如果你自己在做练习的话,你可能会发现结果与图片有2处不同。首先,在你的机器上编辑行的所有列都转变成了textbox,而图片的ProductID列却不是textbox,也即不可编辑。显然,在某些情况下我们想让某些数据处于只读状态,好比ProductID,它是Products表的关键字段。要想使DataGrid里的某些列不可编辑,我们只需要将其ReadOnly属性设置为True。比如:
asp:DataGrid id="dgProducts" runat="server"
... >
<Columns>
<asp:BoundColumn HeaderText="Product ID" DataField="ProductID"
ReadOnly="True" />
...
</Columns>
</asp:DataGrid>
第二个不同点是,本文图片里处于编辑状态的行的颜色要稍微浅淡点。你可以为处于编辑状态的行设置各种各样的格式属性,方法是使用DataGrid的EditItemStyle属性。比如:
<asp:DataGrid id="dgProducts" runat="server"
...
EditItemStyle-BackColor="#eeeeee"
... >
<Columns>
...
</Columns>
</asp:DataGrid
自此,我们考察了如何选择一行来编辑,接下来我们考察当用户对处于编辑状态的行进行更新或取消更新时,如何添加代码应对。我们将在下一节探讨。
考察DataGrid控件 Part 6 (下)
本文英文原版:
http://aspnet.4guysfromrolla.com/articles/071002-1.3.aspx
在上节我们考察了编辑某行所必需的代码,在这一节我们探讨用户更新或取消更新所必需的代码.取消“Cancel”按钮如前所述,当点击某行的"Edit"按钮时,它会被替换为"Update"和"Cancel" 按钮."Cancel" 意味着DataGrid恢复到“非编辑”状态,并不会保存所做的任何修改。当点击"Cancel"按钮时触发CancelCommand事件.在对应的事件处理器里我们需要设置DataGrid返回到“预编辑”(pre-editing)状态。为此,我们只需要将EditItemIndex属性设为“-1”,并重新对DataGrid进行绑定(调用BindData()):
Sub dgProducts_Cancel(sender As Object, e As DataGridCommandEventArgs)
dgProducts.EditItemIndex = -1
BindData()
End Sub
更新“Update” 按钮
当点击“Update” 按钮时触发UpdateCommand事件。在对应的事件处理器里,我们需要读取用户键入textboxe的值,然后构造一个恰当的SQL语句。“Update” 按钮的事件处理器接受2个输入参数,一个为Object,另一个为DataGridCommandEventArgs.并且后者有一个Item属性,它是DataGridItem的一个实例,与要编辑的那个DataGrid row相对应。DataGridItem包含一个单元集(Cells collection),可以通过它来访问DataGrid里各种列包含的text或控件.通过DataGridItem对象,我们可以探测所编辑行的ProductID,甚至用户输入textboxe里的price, name,description值:
Sub dgProducts_Update(sender As Object, e As DataGridCommandEventArgs)
'Read in the values of the updated row
Dim iProductID as Integer = e.Item.Cells(1).Text
Dim dblPrice as Double = CType(e.Item.Cells(2).Controls(0),
TextBox).Text
Dim strName as String = CType(e.Item.Cells(3).Controls(0),
TextBox).Text
Dim strDesc as String = CType(e.Item.Cells(4).Controls(0),
TextBox).Text
...
在编辑模式里,ProductID列并不是textbox类型的(因为它被标明为只读ReadOnly)。我们注意到在代码里ProductID的值可以通过e.Item.Cells(1)的Text属性来获取。Cells(1)代表的是DataGrid row里的第二个表格单元(TableCell)。而第一个表格单元(也就是Cells(0))就是包含 "Update"和 "Cancel"按钮的那个列。
要获取price, name,description的值就要稍微复杂一些,因为这些值是包含在表格单元里的textbox的文本值。因此,我们要用CType函数将相应表格单元的第一个控件(Controls(0))转换(cast)为一个TextBox控件,然后引用它的Text属性。(如果你用的是C#,你要这样转换:((TextBox) e.Items.Cells[2].Controls[0]).Text,而CType()函数是VB.NET的内置函数)
获取这些值以后我们创建一个SQL UPDATE命令。在前面的文章里我们用的是动态SQL语句(dynamic SQL statement),不过在这里我用的是带参数的形式,因为我觉得这样要更简洁一些——当然,你喜欢用哪种都可以。
...
'Construct the SQL statement using Parameters
Dim strSQL as String = _
"UPDATE [Products] SET [ProductName] = @ProdName, " & _
"[UnitPrice] = @UnitPrice, [ProductDescription] = @ProdDesc " &
_
"WHERE [ProductID] = @ProductID"
Const strConnString as String = _
"Provider=Microsoft.Jet.OLEDB.4.0;Data
Source=C:/GrocerToGo.mdb"
Dim objConn as New OleDbConnection(strConnString)
objConn.Open()
Dim myCommand as OleDbCommand = new OleDbCommand(strSQL, objConn)
myCommand.CommandType = CommandType.Text
' Add Parameters to the SQL query
Dim parameterProdName as OleDbParameter = _
new OleDbParameter("@ProdName", OleDbType.VarWChar, 75)
parameterProdName.Value = strName
myCommand.Parameters.Add(parameterProdName)
Dim parameterUnitPrice as OleDbParameter = _
new OleDbParameter("@UnitPrice", OleDbType.Currency)
parameterUnitPrice.Value = dblPrice
myCommand.Parameters.Add(parameterUnitPrice)
Dim parameterProdDesc as OleDbParameter = _
new OleDbParameter("@ProdDesc", OleDbType.VarWChar)
parameterProdDesc.Value = strDesc
myCommand.Parameters.Add(parameterProdDesc)
Dim parameterProdID as OleDbParameter = _
new OleDbParameter("@ProductID", OleDbType.Integer)
parameterProdID.Value = iProductID
myCommand.Parameters.Add(parameterProdID)
myCommand.ExecuteNonQuery() 'Execute the UPDATE query
objConn.Close() 'Close the connection
...
现在,数据库已经对用户的编辑进行了更新,剩下要做的是使DataGrid返回预编辑状态(通过设置EditItemIndex为-1),然后对DataGrid进行绑定(通过BindData())。
...
'Finally, set the EditItemIndex to -1 and rebind the DataGrid
dgProducts.EditItemIndex = -1
BindData()
End Sub
最后,完善代码,你需要将CancelCommand和UpdateCommand事件与相应的事件处理器对应起来,像下面这样:
<asp:DataGrid id="dgProducts" runat="server"
...
OnUpdateCommand="dgProducts_Update"
OnCancelCommand="dgProducts_Cancel"
... >
<Columns>
...
</Columns>
</asp:DataGrid>
结语:
综上所述,DataGrid控件不仅可以显示数据,也可以对数据进行编辑。为DataGrid添加该功能相对而言要简单一些,我们真正要话功夫的是编写"Update" 按钮触发的事件的代码。不过话说回来,要实现相同的功能,经典ASP应用程序需要写的代码要多的多。