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

Sample Image - EditNestedDataGrid.jpg


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.

minus.gif Collapse
<asp:DataGrid id="DataGrid1"
style="Z-INDEX: 101; LEFT: 8px; POSITION: absolute; TOP: 32px"
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:

minus.gif 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 id="ImageButton1" runat="server"
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:

minus.gif 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:

minus.gif Collapse
<?xml version="1.0" standalone="yes"?>
<NewDataSet>
<ParentTable>
<CustomerID>ALFKI</CustomerID>
<CompanyName>Alfreds Futterkisten</CompanyName>
<ContactName>Maria Anders</ContactName>
<ContactTitle>Sales Representative</ContactTitle>
<Address>Obere Str. 57</Address>
<ChildTable>
<CustomerID>ALFKI</CustomerID>
<OrderID>10643</OrderID>
<ShipAddress>Obere Str. 57</ShipAddress>
<Freight>29.46</Freight>
<ShipName>Alfreds Futterkiste</ShipName>
</ChildTable>
<ChildTable>
<CustomerID>ALFKI</CustomerID>
<OrderID>10692</OrderID>
<ShipAddress>Obere Str. 57</ShipAddress>
<Freight>61.02</Freight>
<ShipName>Alfreds Futterkiste</ShipName>
</ChildTable>
..................
..................
</ParentTable>
<ParentTable>
<CustomerID>ANATR</CustomerID>
<CompanyName>Ana Trujillo Emparedados y helados</CompanyName>
<ContactName>Ana Trujillo</ContactName>
<ContactTitle>Owner</ContactTitle>
<Address>Avda. de la Constitución 2222</Address>
<ChildTable>
...................

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.

EditNestedDataGrid1.jpg

EditNestedDataGrid2.jpg

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.

转载于:https://www.cnblogs.com/godwar/archive/2007/07/09/811032.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值