• 设为首页
  • 点击收藏
  • 手机版
    手机扫一扫访问
    迪恩网络手机版
  • 关注官方公众号
    微信扫一扫关注
    公众号

ASP.NET可編輯嵌套DataGrid(Editable Nested DataGrid)

原作者: [db:作者] 来自: [db:来源] 收藏 邀请


Latest Update

Before using the source code, you have to grant full access to ASPNET account on the folder in which the Northwind.mdb file is placed. I have updated the source code to include Sort and Paging features in Child DataGrid as well.

Introduction

This article explains how to make an ASP.NET Editable Nested DataGrid. With old ASP screens, it was so hard to have a grid with features like Edit, Add and Delete all together in one screen, but with ASP.NET DataGrid it's all a breeze. Here I am going to explain how to provide all those features in one screen, not only one DataGrid but I am going to explain to extend these features to even nested Datagrids (DataGrid inside DataGrid). I have provided the fully functional source code, which is self explanatory.

Using the code

This is a web application with a virtual directory named as EditNestedDataGrid. You may create the same virtual directory to run this application by VS.NET 2003, or create any other virtual directory and map the path to this directory to access it from the browser. As I have used Access 2003 as my database, you need to have MS Access installed on your machine. I have used NorthWind database with some modifications, I have included this also in the code bundle. I have used VB.NET for Codebehind files.

Step by Step Procedures

1. Since this is all about DataGrid, the first step is to create Parent DataGrid. In DataGrid we can have BoundColumns, but BoundColumns can be used only for display purposes, but if we want to have edit features on columns, we must need TemplateColumns.

TemplateColumns contain a number of templates. The template that is rendered for a particular row of the DataGrid depends on the type of the row being added. For example, if the row is an ordinary item row, the ItemTemplate or AlternatingItemTemplate is used, depending on whether or not the row being added is an odd or even row. If the row being added is specified as the EditItemIndex row, then the TemplateColumn's EditItemTemplate is used. Finally, if the row being rendered is the Header or Footer, the HeaderTemplate or FooterTemplate is used.

Since we would like to have Add features on our DataGrid, here we can use FooterTemplate for providing Textbox Web Control in the footer and also we must set the ShowFooter property to True in order to display the Footer in DataGrid.

Below is the DataGrid html in aspx page looks like... with TemplateColumn.

Collapse
DataMember="ParentTable" AllowSorting="True" ShowFooter="True"
BackColor="#f1f1f1" HorizontalAlign="Center" Font-Name="Verdana"
Font-Size="8pt" AutoGenerateColumns="False"
ItemStyle-VerticalAlign="Top" runat="server" AllowPaging="True">
<Columns>
<asp:TemplateColumn
SortExpression="[Customers].[CustomerID] ASC"
HeaderText="Customer ID">
<ItemTemplate><%# Container.DataItem("CustomerID") %>
</ItemTemplate>
<FooterTemplate>
<asp:TextBox ID="add_CustomerID"
width="100%" Columns="5" Runat="Server" />
</FooterTemplate>
<EditItemTemplate>
<asp:TextBox ID="edit_CustomerID"
Text='<%# Container.DataItem("CustomerID") %>'
readonly=True width="100%" Columns="5" Runat="Server" />
</EditItemTemplate>
</asp:TemplateColumn>

The ItemTemplate and EditItemTemplate's are pretty straightforward. The ItemTemplate simply displays the value of the DataSource field this column is to display, while the EditItemTemplate creates a Textbox Web control for the user to edit the data.

Now we have Textboxes in all the columns to edit the values, but we need some kind of action to trigger the actual edit, here we must use EditCommandColumn, which is a built-in DataGrid column that displays the Edit button for each row. When a particular row's Edit button is clicked, the row enters "edit mode" and the row's EditItemTemplate is used when the DataGrid is rendered. Additionally, the EditCommandColumn changes for the row being edited so as to display an Update and Cancel button.

Here is a piece of code on how do we add EditCommandColumn...

<asp:EditCommandColumn ButtonType="PushButton"
UpdateText="Update" HeaderText="Edit"
CancelText="Cancel" EditText="Edit">
</asp:EditCommandColumn> 

The final DataGrid column is used to display a Delete button for the populated data rows and the "Add" button for the DataGrid Footer rows.

<asp:TemplateColumn HeaderText="Delete">
<ItemTemplate>
<asp:Button CommandName="Delete" Text="Delete"
ID="btnDel" Runat="server"/>
</ItemTemplate>
<FooterTemplate>
<asp:Button CommandName="Insert" Text="Add"
ID="btnAdd" width="100%" Runat="server"/>
</FooterTemplate>
</asp:TemplateColumn>

2. Till now we have created parent DataGrid. Now we are going to extend these features in a child DataGrid as well.

Before adding another DataGrid, we must understand how DataGrid emits its content as HTML tags, for an IE browser, DataGrid is like a regular Table with TR and TD tags so if we can manipulate the parent DataGrid to forcibly close a row and emit Child DataGrid as another ROW we are done with it.

What is displayed is some HTML table cell and row tags that effectively intercept the current output that will be generated by the DataGrid with our own special implementation, namely we are telling the table (when its drawn) to close the current cell, the current row, and now add a new row with one blank column (for spacing) and another column (spanning 7 columns) that we'll use to display a second DataGrid.

Here is the piece of code which explains this:

Collapse
.................Parent DataGrid columns.....................
<asp:TemplateColumn>
<ItemTemplate>
<asp:PlaceHolder ID="Expanded" Runat="server" Visible="False">
</td></tr>
<tr>
<td width="9"> </td>
<td colspan="7">
<asp:DataGrid
DataSource='<%# Container.DataItem.
CreateChildView("ParentTable_ChildTable")%>'
runat=server id=DataGrid2 CellPadding="2" BackColor="#f1f1f1"
Font-Name="Verdana" Font-Size="8pt" AutoGenerateColumns=false
ItemStyle-VerticalAlign="Top" ShowFooter="True"
OnItemCommand="DataGrid2_ItemCommand" EnableViewState=true>
<AlternatingItemStyle BackColor="White"></AlternatingItemStyle>
<HeaderStyle Font-Bold="True" HorizontalAlign="Center" ForeColor="White"
BackColor="#0083C1"></HeaderStyle>
<Columns>
...............ChildDataGrid columns............................

The Last column of the parent DataGrid contains a PlaceHolder object called Expanded that basically is hidden (Visible="False"), but we need to have some action on parent DataGrid which will show its child DataGrid in next row. For that keeping in mind we will add a column at the beginning of the parent DataGrid with column heading as "+"...

...................DataGrid1 declaration........................
<Columns>
<asp:TemplateColumn>
<HeaderStyle Width="9px"></HeaderStyle>
<ItemTemplate>
<asp:ImageButton 
Width="9px" Height="9px" ImageUrl="~/Images/Plus.gif"
CommandName="Expand"></asp:ImageButton>
</ItemTemplate>
....................remaining DataGrid1 columns.................

3. We have so far completed the HTML portion (ASPX) of the code; we need to populate the data into both grids. I have created a procedure BindData which will be called in many places to bind the data to grid.

Here in this BindData procedure we need a query to bring the data from the database. Retrieving data from databases and binding is very simple using ADO .NET, but here in our case we have a child grid which also needs to be bound. To handle this I am using the ADO .NET DataRelation object, which adds relation on parent query and child query using a common column.

Here is the BindData procedure which explains this:

Collapse
'This procedure binds the DataGrid with Query Details
Private Sub BindData(Optional ByVal astrSort As String = "")
Dim lstrSQL As String
Dim ldtbParentTable As New DataTable
Dim ldtbChildTable As New DataTable
Dim ds As New DataSet
Dim lobjDataRelation As DataRelation
Dim lintPageSize As Int32
Dim llngTotalRec As Int32
Dim conn
Dim dcCust
Dim daCust
Dim dcOrder
Dim daOrder
Try
'//Prepare the connection object
conn = New OleDbConnection("Provider=Microsoft.Jet." & _
"OLEDB.4.0;data source=" & _
Server.MapPath(
ConfigurationSettings.AppSettings("MDBPATH")))
conn.open()
'Query to get Customer Details - Parent Query
lstrSQL = "SELECT [Customers].[CustomerID], " & _
"[Customers].[CompanyName], "& _
"[Customers].[ContactName]," & _
"[Customers].[ContactTitle], " & _
"[Customers].[Address] FROM [Customers] "
'//Sort order provided
If astrSort <> "" Then
lstrSQL = lstrSQL & " ORDER BY " & astrSort
Else
'Default sort order
lstrSQL = lstrSQL & _
" ORDER BY [CUSTOMERS].[CUSTOMERID] ASC"
End If
dcCust = New OleDbCommand(lstrSQL, conn)
daCust = New OleDbDataAdapter(dcCust)
daCust.Fill(ldtbParentTable)
ldtbParentTable.TableName = "ParentTable"
'Query to get Order Details - Child Query
lstrSQL = "SELECT [Orders].[CustomerID] as" & _
" ChildCustomerID,[Orders]." & _
"[OrderID],[Orders].[ShipAddress]," & _
"[Orders].[Freight]," & _
"[Orders].[ShipName] FROM [Orders]"
dcOrder = New OleDbCommand(lstrSQL, conn)
daOrder = New OleDbDataAdapter(dcOrder)
daOrder.Fill(ldtbChildTable)
ldtbChildTable.TableName = "ChildTable"
'Add both these tables to the dataset
ds.Tables.Add(ldtbParentTable)
ds.Tables.Add(ldtbChildTable)
'//Create relation and this relation
'  name should be used on CreateChildView
Dim dr As New DataRelation("ParentTable_ChildTable", _
ldtbParentTable.Columns("CustomerID"), _
ldtbChildTable.Columns("ChildCustomerID"), False)
dr.Nested = True
ds.Relations.Add(dr)
'Set the datasource to parent datagrid
DataGrid1.DataSource = ds
DataGrid1.DataBind()
Catch exc As Exception
LogMessage(exc)
Exit Sub
Finally
daCust.Dispose()
dcCust.Dispose()
daOrder.Dispose()
dcOrder.Dispose()
ds.Dispose()
conn.close()
End Try
End Sub

In the above code snippet, I have used the DataRelation object, with Relation name as "ParentTable_ChildTable", if you remember we have used this same name in DataGrid2 DataSource property as DataSource='<%# Container.DataItem.CreateChildView("ParentTable_ChildTable") %>'. This name must match in order to have the DataGrid bind child records itself.

If we are using XML file as a DataSource instead of database, then we don't need to create the DataRelation object, when the XML files are read through DataSet.ReadXml() property it creates its own relation, but this depends on how the XML file is formed.

For example if we write the dataset (ds) into XML file after having the DataRelation, it will be like below:

Collapse

4. We have data in both the parent and child grid, now we need to implement the corresponding DataGrid events in Codebehind pages, In order to handle the case when the user clicks the "Expand", "Add", "Edit", "Update" and "Delete" buttons, we must have an even handler for the DataGrid's ItemCommand event.

This event handler, DataGrid1_ItemCommand with Handles DataGrid1.ItemCommand, must contain an If statement to check the CommandName property.

The source code which I have attached is self explanatory on this DataGrid1 event handling. However, I am going to explain how we handle the "Expand" command, which is triggered when the user clicks the "+" button on the parent grid.

Case "Expand"
img = e.Item.Cells(0).FindControl("ImageButton1")
If img.ImageUrl = "~/Images/Plus.gif" Then
img.ImageUrl = "~/Images/Minus.gif"
Else
img.ImageUrl = "~/Images/Plus.gif"
End If
exp = e.Item.Cells(2).FindControl("Expanded")
exp.Visible = Not exp.Visible

In the code snippet, here we are finding the image button which is placed in first column, based on the image url we are dynamically expanding or hiding the PlaceHolder object.

5. Similarly to handle "Insert", "Edit", "Update" and "Delete" events on child DataGrid, we must have an Event Handler for DataGrid2 as well, but here is the trick, we can't have a handler like we had for DataGrid1 with handles DataGrid1.ItemCommand.

Since DataGrid2 is a child of DataGrid1 and also in runtime will have many child Datagrids generated, we can't really use the ID of child grid as "DataGrid2", so in this case we explicitly specify the OnItemCommand property of the child DataGrid as "DataGrid2_ItemCommand"

As I said above we can't really use the ID of child grid as DataGrid2, we must derive the runtime id of the child grid using the source object of the ItemCommand handler.

Public Sub DataGrid2_ItemCommand(ByVal source As Object, _
ByVal e As System.Web.UI.WebControls.DataGridCommandEventArgs)
....................
.....................
Dim dgTemp As New DataGrid
dgTemp = CType(source, DataGrid)
dgUniqueID = dgTemp.UniqueID

In the above code snippet we determine the runtime UniqueID of the child DataGrid and will be using that value in ItemDataBound handler of the main grid.


鲜花

握手

雷人

路过

鸡蛋
该文章已有0人参与评论

请发表评论

全部评论

专题导读
热门推荐
热门话题
阅读排行榜

扫描微信二维码

查看手机版网站

随时了解更新最新资讯

139-2527-9053

在线客服(服务时间 9:00~18:00)

在线QQ客服
地址:深圳市南山区西丽大学城创智工业园
电邮:jeky_zhao#qq.com
移动电话:139-2527-9053

Powered by 互联科技 X3.4© 2001-2213 极客世界.|Sitemap