
Introduction
I had to implement a Master-Detail GridView in my application. Searching the net, I did find a lot of solutions, but couldn’t find one where the child table was positioned properly. Well, there might be other solutions that give the same result, but I thought sharing this might help others.
I’ve extended the SearchGridView (the last article I posted) and created a NestableGridView class. I overrode the RenderContents function to render the child table in a separate row.
I’ve extended the SearchGridView control as a NestableGridView.
public class NestableGridView : SearchGridView
I’ve overrode the RenderContents function to identify the cell where the child table is and to render it in a separate row.
protected override void RenderContents(HtmlTextWriter writer)
{
if (DesignMode || Rows.Count == 0)
{
base.RenderContents(writer);
return;
}
base.RenderBeginTag(writer);
if(HeaderRow != null)
HeaderRow.RenderControl(writer);
foreach (GridViewRow row in Rows)
{
Control childCtl = row.FindControl(ChildTableID);
if (childCtl != null && childCtl.Visible)
{
row.RenderBeginTag(writer);
TableCell childCell = null;
foreach (TableCell cell in row.Cells)
{
if (cell.Controls.IndexOf(childCtl) == -1)
cell.RenderControl(writer);
else
{
writer.Write("<"+"td"+"><"+"/td"+">");
childCell = cell;
}
}
if(childCell != null)
{
writer.Write("<"+"/tr"+"><"+"tr"+">");
childCell.ColumnSpan = row.Cells.Count;
childCell.Attributes.Add("align",ChildTableAlign.ToString());
childCell.RenderControl(writer);
}
row.RenderEndTag(writer);
}
else
{
row.RenderControl(writer);
}
}
if (FooterRow != null)
FooterRow.RenderControl(writer);
if (PagerRow != null)
PagerRow.RenderControl(writer);
base.RenderEndTag(writer);
}
The SelectedIndexChanging event handles the toggling visibility of the child table.
protected override void OnSelectedIndexChanging(GridViewSelectEventArgs e)
{
base.OnSelectedIndexChanging(e);
if (DesignMode)
return;
if (Rows[e.NewSelectedIndex] != null &&
Rows[e.NewSelectedIndex].FindControl(ChildTableID) != null)
{
Control childCtl = Rows[e.NewSelectedIndex].FindControl(ChildTableID);
if (childCtl != null)
childCtl.Visible = !childCtl.Visible;
}
}
Now, let me illustrate the above control with the help of an example. For this purpose, I have used the Categories and Products table from the NorthWind database.
- Step 1: Create a datasource
dsMaster with select query "SELECT * From Categories".
- Step 2 : Create an instance of
NestableGridView and customize the SearchFilters property to have the list of columns on which the search can be performed.
- Step 3: Create a template column with a
SearchGridView with the datasource dsChild and a hidden field _hfChildSearch to hold the search string of the child table. Set the Visibility of the Panel to false to hide all child tables, or true to show all tables initially.
- Step 4: Set the child table ID of the master grid as
pnlChild (the ID of the child table panel).
- Step 5: Implement the
SearchGrid event on the master and child tables to filter and search records.
- Step 6: Add empty data templates to the master and child tables to insert new records.
- Step 7: Implement the
AddRow event on the master and child tables to add new records to the tables.
NestableGridView can be used not only to display child tables but any control inside the master row, like pictures etc. Also, the alignment of the child row can be set using the ChildTableAlign attribute.