I was standing on the surprisingly biting concrete at TechEd 2009 and a customer asked: “does your ASPxGridView support n-nesting?” My response was why, yes, yes it does. Unfortunately I didn’t have an example handy, but yesterday at the end of the day I whipped one up and the ASPX is provided here with some basic instructions as to how to create n-nesting.
To summarize, our ASPxGridView supports nesting. Add two or more grids to a page in design mode, wire each up to a data source, and then drag and drop the grids to be nested into the DetailRow template region and you are pretty much finished. N-nesting is supported as an almost completely no code solution. You do have to correlate the keys between the parent and child grids and set a property or two, but that’s it. (For the finished result see Figure 1.)
Figure 1: The run-time view of a grid nested three levels deep.
Wiring up One Grid
There are two parts to grid nesting. The first part—wiring the grid to data—is the same for all grids in the nesting relationship. Each grid in the nesting relationship has to have a source for its data. For the example right click the Smart tag associated with an ASPxGridView and select Choose Data Source|New data source to run the data source wizard. The Configure Data Source wizard will walk you through building a connection string, selecting your data source (and designing a query), and wiring up the resultant data source to associated ASPxGridView.
Figure 2: Batgirl gives away Batman’s secret identity. I am Batman! (What happens at TechEd stays at TechEd.)
Correlating Child and Parent Grids
The only programming you need to do is a line of code that correlates a parent grid to a child grid. When you ware walking through the Configure Data Source wizard for the child grids you need to complete an extra step. Parent and child relationships can be defined with joins or WHERE clauses. For the Configure Data Source wizard use the WHERE button on the Configure the Select Statement (see Figure 3) step to specify how data are correlated to the parent data. For example, Northwind.Customers is related Northwind.Orders by CustomerID. To nest Orders in Customers click the WHERE button when configuring the Orders table and configure a WHERE condition for the foreign key CustomerID of the Orders table (use Figure 4 as a visual guide).
Figure 3: On the Configure the Select Statement click the WHERE button to correlate the parent grids with the child grids.
Figure 4: Configure a column that represents the implicit WHERE-clause join column for child data sets.
For the demo Orders is nested in the Customers grid and Order Details is nested in the Orders grid. You will need to configure a WHERE condition when configuring the data source for the Orders table and the Orders Detail table. The Column value (see Figure 4) needs to be the Foreign key for the child table. The Operator needs to be equal and the Source value of Session means the primary key will be stored in the Session.
Next you need to store the primary key of the parent table in Session. So when a parent row is expanded the nested grid knows what parent row is being displayed. For the example, Customers grid contains Orders and the Orders grid contains Order Details. The only code you need to write to make the basic nesting work is a BeforePerformDataSelect event handler for the Orders grid and Order Detail grid. In each of these events the sender argument represents the parent grid. Cast sender as an ASPxGridView and invoke the GetMasterRowKeyValue method. Store the result in Session using the column defined in the Add WHERE Clause dialog (see Listing 1).
Listing 1: BeforePerformDataSelect gets the key from the parent row and stores it for the child grid, correlating the parent and child grids.
protected void ASPxGridView2_BeforePerformDataSelect(object sender, EventArgs e)
{
Session["CustomerID"] = ((ASPxGridView)sender).GetMasterRowKeyValue();
}
protected void ASPxGridView3_BeforePerformDataSelect(object sender, EventArgs e)
{
Session["OrderID"] = ((ASPxGridView)sender).GetMasterRowKeyValue();
} Completing the Nesting Effect
To complete the nesting effect select the second to the last grid. In our example, select the Orders grid. From the Smart tags menu select Edit Templates and pick the DetailRow template. Drag and drop the grid containing the Order Details into the grid containing Orders DetailRow template (shown in Figure 5). Repeat the process to drag the grid containing the Orders (which now also contains the Order Details grid) data into the DetailRow template for the Customers grid.
Figure 5: The grid containing the Order Details data shown nested in the grid containing the Orders data.
Finally, for the Orders and Order Details grids set the SettingsDetail.IsDetailGrid to true. For the Customers and Orders grids set the SettingsDetail.ShowDetailRow property to true. (Listing 2 contains all of the ASPX for the demo). That’s pretty much all there is to it. To recap, you need to:
- Correlate every child to its parent with a WHERE clause
- Implement a BeforePerformDataSelect method to store the parent grid’s master key in Session based on the named-value specified in the Add WHERE Clause step of the data source wizard
- Place each child grid in the DetailRow template of its parent
- Each grid needs its own data source
- Every grid that has a detail grid needs the SettingsDetail.ShowDetailRow to true
- Every grid that is a detail grid needs the SettingsDetail.IsDetailGrid to true
Listing 2: THE ASPX for this sample was all generated by the IDE and the Developer Express controls. Here it is.
<%@ Page Language="C#" AutoEventWireup="true" CodeFile="Default.aspx.cs" Inherits="_Default" %>
<%@ Register assembly="DevExpress.Web.ASPxGridView.v9.1, Version=9.1.2.0, Culture=neutral, PublicKeyToken=b88d1754d700e49a" namespace="DevExpress.Web.ASPxGridView" tagprefix="dxwgv" %>
<%@ Register assembly="DevExpress.Web.ASPxEditors.v9.1, Version=9.1.2.0, Culture=neutral, PublicKeyToken=b88d1754d700e49a" namespace="DevExpress.Web.ASPxEditors" tagprefix="dxe" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<title></title>
</head>
<body>
<form id="form1" runat="server">
<div>
<dxwgv:ASPxGridView ID="ASPxGridView1" runat="server"
AutoGenerateColumns="False" DataSourceID="SqlDataSource1"
KeyFieldName="CustomerID">
<Templates>
<DetailRow>
<dxwgv:ASPxGridView ID="ASPxGridView2" runat="server"
AutoGenerateColumns="False" DataSourceID="SqlDataSource2"
KeyFieldName="OrderID"
onbeforeperformdataselect="ASPxGridView2_BeforePerformDataSelect">
<Templates>
<DetailRow>
<dxwgv:ASPxGridView ID="ASPxGridView3" runat="server"
AutoGenerateColumns="False" DataSourceID="SqlDataSource3"
KeyFieldName="OrderID"
onbeforeperformdataselect="ASPxGridView3_BeforePerformDataSelect">
<Columns>
<dxwgv:GridViewDataTextColumn FieldName="OrderID" ReadOnly="True"
VisibleIndex="0">
</dxwgv:GridViewDataTextColumn>
<dxwgv:GridViewDataTextColumn FieldName="ProductID" ReadOnly="True"
VisibleIndex="1">
</dxwgv:GridViewDataTextColumn>
<dxwgv:GridViewDataTextColumn FieldName="UnitPrice" VisibleIndex="2">
</dxwgv:GridViewDataTextColumn>
<dxwgv:GridViewDataTextColumn FieldName="Quantity" VisibleIndex="3">
</dxwgv:GridViewDataTextColumn>
<dxwgv:GridViewDataTextColumn FieldName="Discount" VisibleIndex="4">
</dxwgv:GridViewDataTextColumn>
</Columns>
</dxwgv:ASPxGridView>
</DetailRow>
</Templates>
<Columns>
<dxwgv:GridViewDataTextColumn FieldName="OrderID" ReadOnly="True"
VisibleIndex="0">
<EditFormSettings Visible="False" />
</dxwgv:GridViewDataTextColumn>
<dxwgv:GridViewDataTextColumn FieldName="CustomerID" VisibleIndex="1">
</dxwgv:GridViewDataTextColumn>
<dxwgv:GridViewDataTextColumn FieldName="EmployeeID" VisibleIndex="2">
</dxwgv:GridViewDataTextColumn>
<dxwgv:GridViewDataDateColumn FieldName="OrderDate" VisibleIndex="3">
</dxwgv:GridViewDataDateColumn>
<dxwgv:GridViewDataDateColumn FieldName="RequiredDate" VisibleIndex="4">
</dxwgv:GridViewDataDateColumn>
<dxwgv:GridViewDataDateColumn FieldName="ShippedDate" VisibleIndex="5">
</dxwgv:GridViewDataDateColumn>
<dxwgv:GridViewDataTextColumn FieldName="ShipVia" VisibleIndex="6">
</dxwgv:GridViewDataTextColumn>
<dxwgv:GridViewDataTextColumn FieldName="Freight" VisibleIndex="7">
</dxwgv:GridViewDataTextColumn>
<dxwgv:GridViewDataTextColumn FieldName="ShipName" VisibleIndex="8">
</dxwgv:GridViewDataTextColumn>
<dxwgv:GridViewDataTextColumn FieldName="ShipAddress" VisibleIndex="9">
</dxwgv:GridViewDataTextColumn>
<dxwgv:GridViewDataTextColumn FieldName="ShipCity" VisibleIndex="10">
</dxwgv:GridViewDataTextColumn>
<dxwgv:GridViewDataTextColumn FieldName="ShipRegion" VisibleIndex="11">
</dxwgv:GridViewDataTextColumn>
<dxwgv:GridViewDataTextColumn FieldName="ShipPostalCode" VisibleIndex="12">
</dxwgv:GridViewDataTextColumn>
<dxwgv:GridViewDataTextColumn FieldName="ShipCountry" VisibleIndex="13">
</dxwgv:GridViewDataTextColumn>
</Columns>
<SettingsDetail IsDetailGrid="True" />
<SettingsDetail ShowDetailRow="True" />
</dxwgv:ASPxGridView>
</DetailRow>
</Templates>
<Columns>
<dxwgv:GridViewDataTextColumn FieldName="CustomerID" ReadOnly="True"
VisibleIndex="0">
</dxwgv:GridViewDataTextColumn>
<dxwgv:GridViewDataTextColumn FieldName="CompanyName" VisibleIndex="1">
</dxwgv:GridViewDataTextColumn>
<dxwgv:GridViewDataTextColumn FieldName="ContactName" VisibleIndex="2">
</dxwgv:GridViewDataTextColumn>
<dxwgv:GridViewDataTextColumn FieldName="ContactTitle" VisibleIndex="3">
</dxwgv:GridViewDataTextColumn>
<dxwgv:GridViewDataTextColumn FieldName="Address" VisibleIndex="4">
</dxwgv:GridViewDataTextColumn>
<dxwgv:GridViewDataTextColumn FieldName="City" VisibleIndex="5">
</dxwgv:GridViewDataTextColumn>
<dxwgv:GridViewDataTextColumn FieldName="Region" VisibleIndex="6">
</dxwgv:GridViewDataTextColumn>
<dxwgv:GridViewDataTextColumn FieldName="PostalCode" VisibleIndex="7">
</dxwgv:GridViewDataTextColumn>
<dxwgv:GridViewDataTextColumn FieldName="Country" VisibleIndex="8">
</dxwgv:GridViewDataTextColumn>
<dxwgv:GridViewDataTextColumn FieldName="Phone" VisibleIndex="9">
</dxwgv:GridViewDataTextColumn>
<dxwgv:GridViewDataTextColumn FieldName="Fax" VisibleIndex="10">
</dxwgv:GridViewDataTextColumn>
</Columns>
<SettingsDetail ShowDetailRow="True" />
</dxwgv:ASPxGridView>
<asp:SqlDataSource ID="SqlDataSource1" runat="server"
ConnectionString="<%$ ConnectionStrings:NorthwindConnectionString %>"
SelectCommand="SELECT * FROM [Customers]"></asp:SqlDataSource>
<asp:SqlDataSource ID="SqlDataSource2" runat="server"
ConnectionString="<%$ ConnectionStrings:NorthwindConnectionString %>"
SelectCommand="SELECT * FROM [Orders] WHERE ([CustomerID] = @CustomerID)">
<SelectParameters>
<asp:SessionParameter Name="CustomerID" SessionField="CustomerID"
Type="String" />
</SelectParameters>
</asp:SqlDataSource>
<asp:SqlDataSource ID="SqlDataSource3" runat="server"
ConnectionString="<%$ ConnectionStrings:NorthwindConnectionString %>"
SelectCommand="SELECT * FROM [Order Details] WHERE ([OrderID] = @OrderID)">
<SelectParameters>
<asp:SessionParameter Name="OrderID" SessionField="OrderID" Type="Int32" />
</SelectParameters>
</asp:SqlDataSource>
</div>
</form>
</body>
</html>
Happy TechEd! Have a safe trip home.