Sample code Download
Question:
I have a question regarding DataGrid. Can I bind a column of a DataGrid to
another DataGrid? I have a situation where I have to display a few details in
the same DataGrid on click of a HyperLink column.
Thanks in advance.
Regards,
Prathibha Sharangapani
The Answer:
Prathibha, this can certainly be done. You are describing a master/detail
DataGrid. For example, a DataGrid of customers with a column in each row that
has an embdedded DataGrid of all the orders placed by that customer. The real
challenge here is, how do we data bind a DataGrid that isn't created until
run-time. You see, we cannot set the DataGrid's
DataSource
property, or invoke the DataGrid's
DataBind()
method, because the DataGrid doesn't exist at desing-time.
Every data bound control exposes an
ItemDataBound
event that we can handle. This event fires with each item that is data bound.
In the case of a DataGrid, the
ItemDataBound
event fires for each
DataGridItem
(row) in the DataGrid. Since we can capture the
ItemDataBound
event for each row, we can capture values from the row, such as the
CustomerID
for the specified row, and dynamically create and add controls to the
Controls
collection of one of the columns (cells).
Let's Get It On
We will demonstrate building a master/detail DataGrid using the Northwind Customers
and Orders tables. The sample code download also contains an orders to
order detail master/detail DataGrid, and all of the sample code is in C# and
Visual Basic .NET.
First, lets build the Web form user interface. For this we will use a DataGrid
with
BoundColumns and
TemplateColumns. The result will look like this:

[Run Sample]
From the screenshot you can see that we have three columns, but actually there
is acolumn that is hidden as the first column.
The first column is a
BoundColumn
with its
Visible
property set to
False
(to hide it). This column is bound to the
CustomerID
field in the data source. We will use this later to bind the orders DataGrid.
The second column is a
HyperLinkColumn, also bound to the
CustomerID
field, and set to link to the OrderDetailDataGrid.aspx page (see the
code sample download). The third and fourth columns are
TemplateColumns used to create a custom layout.
Here is the Web Form code:
<%@ Page language="c#"
Inherits="MasterDetail.CustomerOrderDataGrid"
EnableViewState="False" %>
<HTML>
<body style="font: x-small Verdana, Arial, sans-serif;">
<!-- Begin Web Form -->
<form id="CustomerOrderDataGrid"
method="post" runat="server">
<p><a href="/DayOfDotNet/">Parent
Directory</a></p>
<!-- Begin
DataGrid -->
<asp:DataGrid
id="CustomerDataGrid" runat="server"
AutoGenerateColumns="False"
CellPadding="2"
CellSpacing="0"
Font-Names="Verdana,
Arial, sans-serif"
BorderColor="Black"
BorderWidth="1"
GridLines="Horizontal"
OnItemDataBound="CustomerDataGrid_OnItemDataBound">
<HeaderStyle
Font-Bold="True"
Font-Size="small"
Font-Name="Arial"
BackColor="Maroon"
ForeColor="White" />
<ItemStyle
Font-Size="x-small" />
<AlternatingItemStyle
BackColor="Tan" />
<Columns>
<asp:BoundColumn
DataField="CustomerID"
Visible="False" />
<asp:HyperLinkColumn
DataTextField="CustomerID"
DataNavigateUrlField="CustomerID"
DataNavigateUrlFormatString="OrderDetailDataGrid.aspx?customerid={0}"
HeaderText="ID"
ItemStyle-VerticalAlign="Top"
/>
<asp:TemplateColumn
ItemStyle-VerticalAlign="Top" HeaderText="Customer">
<ItemTemplate>
<b><%#
DataBinder.Eval(Container.DataItem, "CompanyName") %></b><br>
<%#
DataBinder.Eval(Container.DataItem, "Address" ) %><br>
<%#
DataBinder.Eval(Container.DataItem, "City" ) %>,
<%#
DataBinder.Eval(Container.DataItem, "Region") %>
<%#
DataBinder.Eval(Container.DataItem, "PostalCode" ) %><br>
<br>
<%#
DataBinder.Eval(Container.DataItem, "ContactName" ) %><br>
<%#
DataBinder.Eval(Container.DataItem, "ContactTitle" ) %><br>
<%#
DataBinder.Eval(Container.DataItem, "Phone" ) %>
</ItemTemplate>
</asp:TemplateColumn>
<asp:TemplateColumn
ItemStyle-VerticalAlign="Top"
HeaderText="Orders">
<%--
Embedded DataGrid will go here --%>
</asp:TemplateColumn>
</Columns>
</asp:DataGrid>
<!-- End DataGrid -->
</form>
<!-- End Web Form -->
</body>
</HTML> |
You can see, in the forth column I have added a server-side comment indicating
where the embedded DataGrid will be placed.
Break It Down
In the code behind class for the Web Form we will do a couple of things: bind
the
CustomerDataGrid
server control to the Customers table from the Northwind database;
handle the
CustomerDataGrid's
ItemDataBound
event, and dynamically create an
OrdersDataGrid
and load it into the fourth column
Controls
collection.
Lets start by retreiving the data for both the customers and orders in the
Page_Load()
event handler.
using System;
using System.Data;
using System.Data.SqlClient;
using System.Drawing;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.HtmlControls;
using System.Configuration;
namespace MasterDetail
{
public class CustomerOrderDataGrid :
System.Web.UI.Page
{
protected DataGrid
CustomerDataGrid;
private DataSet ds =
new DataSet();
private void Page_Load(object
sender, System.EventArgs e)
{
string sqlStmt
= "SELECT * FROM Customers; SELECT * FROM Orders";
string conString
= "server=localhost;database=Northwind;uid=sa;pwd=;";
SqlDataAdapter sda = new
SqlDataAdapter(sqlStmt, conString);
sda.Fill(ds);
ds.Tables[0].TableName =
"Customers";
ds.Tables[1].TableName =
"Orders";
CustomerDataGrid.DataSource =
ds.Tables["Customers"];
CustomerDataGrid.DataBind();
}
}
}
|
In the SQL statement we select two result sets. When we invoke the data
adapter's
Fill()
method this will create two
DataTables, one for each result set. For
organizational (and anal retentive) purposes, we set the
TableName
property of each of the
DataTables, and bind the
CustomerDataGrid.
Notice, we declared the DataSet (ds) at the
class-level. This will give us access to the DataSet from the
OnItemDataBound
event handler. In the
OnItemDataBound
event handler we need to dynamically construct a DataGrid, and bind it to only
records in the Orders
DataTable
that have the same
CustomerID
value as the
CustomerID
for the current row (the value in the first column, which is hidden for
display).
Lets take a look at the
OnItemDataBound()
event handler:
//Use the OnItemDataBound event handler to
dynamically add an embedded DataGrid
protected void CustomerDataGrid_OnItemDataBound(object
sender, DataGridItemEventArgs e)
{
//When each row is
created in the DataGrid, eval the ItemType
if(e.Item.ItemType
== ListItemType.Item ||
e.Item.ItemType ==
ListItemType.AlternatingItem)
{
//If the
ItemType is Item or AlternatingItem,
//Create a new DataGrid object
named OrdersDataGrid
DataGrid OrdersDataGrid =
new DataGrid();
//Format the
DataGrid to look cool.
OrdersDataGrid.BorderWidth =
(Unit)1;
OrdersDataGrid.CellPadding = 4;
OrdersDataGrid.CellSpacing = 0;
OrdersDataGrid.GridLines =
GridLines.Horizontal;
OrdersDataGrid.BorderColor =
Color.FromName("Black");
OrdersDataGrid.ItemStyle.Font.Name
= "Verdana";
OrdersDataGrid.ItemStyle.Font.Size
= FontUnit.XSmall;
OrdersDataGrid.AlternatingItemStyle.BackColor
= Color.FromName("LightGray");
OrdersDataGrid.ShowHeader =
true;
OrdersDataGrid.HeaderStyle.BackColor
= Color.FromName("Black");
OrdersDataGrid.HeaderStyle.ForeColor
= Color.FromName("White");
OrdersDataGrid.HeaderStyle.Font.Bold
= true;
OrdersDataGrid.HeaderStyle.Font.Size
= FontUnit.XSmall;
//Do not
autogenerate columns.
OrdersDataGrid.AutoGenerateColumns
= false;
//****Add a
series of BoundColumns****//
//***Order
ID***//
BoundColumn bc = new
BoundColumn();
//Set the
BoundColumn Values
bc.HeaderText = "Order
ID";
bc.DataField =
"OrderID";
bc.ItemStyle.Wrap = false;
//Add the
BoundColumn to the OrdersDataGrid.
OrdersDataGrid.Columns.Add(bc);
//***Order
Date***//
bc = new BoundColumn();
bc.HeaderText = "Order
Date";
bc.DataField =
"OrderDate";
bc.DataFormatString="{0:d}";
bc.ItemStyle.Wrap = false;
OrdersDataGrid.Columns.Add(bc);
//***Required
Date***//
bc = new BoundColumn();
bc.HeaderText = "Required
Date";
bc.DataField =
"RequiredDate";
bc.DataFormatString="{0:d}";
bc.ItemStyle.Wrap = false;
OrdersDataGrid.Columns.Add(bc);
//***Shipped
Date***//
bc = new BoundColumn();
bc.HeaderText = "Shipped
Date";
bc.DataField =
"ShippedDate";
bc.DataFormatString="{0:d}";
bc.ItemStyle.Wrap = false;
OrdersDataGrid.Columns.Add(bc);
//****End
BoundColumns****//
//Get the
Authors DataView and filter it for the current ISBN
DataView _orders =
ds.Tables["Orders"].DefaultView;
_orders.RowFilter =
"CustomerID='" + e.Item.Cells[0].Text
+ "'";
//Bind the
DataGrid.
OrdersDataGrid.DataSource =
_orders;
OrdersDataGrid.DataBind();
//Add the
OrdersDataGrid to the BooksDataGrid.
e.Item.Cells[3].Controls.Add(OrdersDataGrid);
}
}
|
In this event handler, we construct a DataGrid in code, setting all of the
properties we need to. Next we construct and add any columns we need. In this
example, all of the columns are
BoundColumn
objects, but they could just as easily be
HyperLinkColumns,
ButtonColumns, or any of the other column data types.
Once we define all of the columns and add them to the DataGrid, we bind the
DataGrid to a filtered
DataView. The
DataView
is the Orders
DataTable, with a
RowFilter
set to the current
CustomerID
value. This will filter out any rows that don't have a matching
CustomerID. Once we filter the
DataView, we set the
OrdersDataGrid's
DataSource
property, and data bind it. Now we have a populated DataGrid that can be added
to the
CustomerDataGrid's fourth column, in the
Controls
collection.
Summary
The DataGrid is probably the single most powerful server control in the ASP.NET
tool box. Its flexibility enables granular control over the rendered layout,
and the ability to handler the events of the DataGrid enable easy manipulation
of the output, such as embedding other data bound controls.