
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.