Nested GridView controls in ASP.NET: best practices
ASP.NET GridView control hosting another GridView in data-centric web applications
General solution
NestingGridView
controls in ASP.NET were described in that almost “canonical” Microsoft walkthrough [1]. In general, it requires creating of two GridView
controls: the first one serving as a container or master GridView1
with its own DataSource1
(SqlDataSource
, AccessDataSource
, etc.) and second "nested" GridView2
with its underlying DataSource2
residing within a TemplateField
in master GridView1
.
Code snippet [1] inside the GridView1 RowDataBound
event procedure (GridView1_RowDataBound
) serves the task of passing a "foreign key" value from GridView1
to DataSource2
. In the particular example provided in [1], the “foreign key” stored in first cell of GridView1 (e.Row.Cells[0].Text)
is passed to DefaultValue
property of only one SelectParameter
(SelectParameters[0]
) of SqlDataSource2:
Listing 1. Original solution on MSDN w/hardcoded Cells index
protected void GridView1_RowDataBound(object sender, GridViewRowEventArgs e){
if (e.Row.RowType == DataControlRowType.DataRow)
{
SqlDataSource s = (SqlDataSource)e.Row.FindControl("SqlDataSource2");
s.SelectParameters[0].DefaultValue = e.Row.Cells[0].Text;
}
}
This should work in all practical versions of ASP.NET (2.0/3.5/4.0). However, the potential drawback of this solution rises from the fact that the foreign key value (for example, ID) could be omitted from visible fields set of GridView1
control. Also, even in case this foreign key field is presented in GridView1
, then there is potential inconvenience of hard-coded Cell index of 0
; generally, it can be not the first one in the field set and, also, its index could change dynamically during the data table editing operations or as a result of dynamically re-arranging table columns order.
Better solution
Better solution shown in the following Listing 2 effectively resolves all of those potential issues described above by using namedDataKeys
in GridView1
control to be used as a "foreign keys" for second DataSource
. In this particular example, DataKey
“Composer
” is added to DataKeys
in GridView1 control (requires the line DataKeyNames="Composer"
) to be passed to corresponding select parameter s.SelectParameters[0].DefaultValue
of second DataSource
named “dsMusic
”.
Listing 2. Improved version using DataKeys
protected void GridView1_RowDataBound(object sender, GridViewRowEventArgs e) {
if (e.Row.RowType == DataControlRowType.DataRow)
{
// find DataSource used for nested GridView (could be SqlDataSource)
AccessDataSource s = (AccessDataSource)e.Row.FindControl("dsMusic");
// assign parameter’s default value from the DataKeys in GridView1
s.SelectParameters[0].DefaultValue = Convert.ToString(((GridView)sender).DataKeys[e.Row.RowIndex].Values["Composer"]);
}
}
Note: Listing 2 demonstrates the use of ASP.NET AccessDataSource
, but the concept/usage is pretty much identical to SqlDataSource
.
Sample Demo
Online Music Library with attached YouTube player [2-4] is implemented using the nestedGridView
techniques described above: click the image below to see the working demo:

Performance Improvement
Significant performance improvement could be achieved by enabling caching for nestedDataSource
s and using FilterParameters
instead of SelectParameters
. To do so:
- Set the nested
DataSource
attributes;FilterExpression="Composer ='{0}'" EnableCaching="True"
-
Modify the event procedure in Listing 2 by replacing the line assigning
SelectParameters
with the following one:// assign filter parameter default value from the datakey s.FilterParameters[0].DefaultValue = Convert.ToString(((GridView)sender).DataKeys[e.Row.RowIndex].Values["Composer"]);