Introduction
Most of the examples that show a Master/Detail DataGrid
do it using nested ListControls
. This article shows how to accomplish the same using only a DataGrid
-derived class.
Solution
The DataGrid
shows exactly the number of rows that its DataSource
has. Suppose you have the following need: show orders grouped by customers, but before a order from a new customer you show the customer details.
How can you do it?
Unfortunately there's no available/easy solution for it. You could create your own DataSource
, manually adding the customer's details, and bind it to the grid. The solution I'll show is fairly easy to implement. All you have to do is to use the supplied DataGrid
extended class and listen for the DataGridItemRender
event. There you decide if you want to insert new rows into the DataGrid
.Here's a code snippet of the provided sample project that adds customer information everytime the customerID of the order row changes:
private void grid_DataGridItemRender(object sender,
CodeProject.MasterDetailGrid.DataGridItemRenderEventArgs e)
{
DsCustomersOrders.OrdersRow order =
this.dsCustomersOrders.Orders[e.ItemIndex];
bool insertCustomerInfo = true;
if (e.ItemIndex > 0)
{
DsCustomersOrders.OrdersRow previousOrder =
this.dsCustomersOrders.Orders[e.ItemIndex - 1];
insertCustomerInfo = previousOrder.CustomerID != order.CustomerID;
}
if (insertCustomerInfo)
{
DsCustomersOrders.CustomersRow customer = order.CustomersRow;
TableCell cellCustomerName = new TableCell();
cellCustomerName.ColumnSpan = e.Grid.Columns.Count;
cellCustomerName.Font.Bold = true;
cellCustomerName.Text = customer.CompanyName;
cellCustomerName.HorizontalAlign = HorizontalAlign.Left;
TableRow row1 = new TableRow();
row1.BackColor = Color.Cyan;
row1.Cells.Add(cellCustomerName);
TableCell cellCustomerCountry = new TableCell();
cellCustomerCountry.ColumnSpan = e.Grid.Columns.Count;
cellCustomerCountry.Text = customer.Country;
cellCustomerCountry.HorizontalAlign = HorizontalAlign.Right;
TableRow row2 = new TableRow();
row2.BackColor = row1.BackColor;
row2.Cells.Add(cellCustomerCountry);
e.NewRows = new TableRow[] { row1, row2 };
}
}
The way the MasterDetailGrid
handles this event is fairly simple. Before the html is rendered the event is raised for each DataGridItem
that the grid has. If new rows need to be added they are inserted in the grid's table. The rows collection which can be acessed using its Control
property at index 0. Below is the code to accomplish it in the extended grid class:
protected override void RenderContents(HtmlTextWriter output)
{
if (HasControls())
{
Table table = this.Controls[0] as Table;
for (int i=0; i < table.Controls.Count; ++i)
{
DataGridItem item = (DataGridItem)table.Controls[i];
if (item.ItemType == ListItemType.AlternatingItem
|| item.ItemType == ListItemType.Item)
{
DataGridItemRenderEventArgs args =
new DataGridItemRenderEventArgs(this, item.ItemIndex,
item.DataItem);
OnDataGridItemRender(args);
if (args.NewRows != null)
{
foreach (TableRow row in args.NewRows)
{
table.Rows.AddAt(i, row);
++i;
}
}
}
}
}
base.RenderContents(output);
}
You can have customize the html ouput even more using this approach. Currently it only handles new rows.