In this article I will explain how to implement Collapsible Nested GridViews i.e. GridView inside GridView with Expand and Collapse with paging in Child GridView.
Note: In past I have already explained ASP.Net Nested GridViews – GridView inside GridView with Expand and Collapse feature. This article makes use of jQuery to implement Collapsible Nested GridViews without paging.
I’ll make use of Customers and Orders Table of Microsoft’s Northwind Database which you can easily download using the link provided below.
HTML Markup
The HTML Markup contains a simple ASP.Net GridView with a child ASP.Net GridView in the ItemTemplate of TemplateField of ASP.Net GridView.
Note: I am storing Customer ID in the DataKeys property of the GridView as that will be required to populate the Child or Nested GridView of Orders.
<asp:GridView ID="gvCustomers" runat="server" AutoGenerateColumns="false" CssClass="Grid"
                <asp:ImageButton ID="imgShow" runat="server" OnClick="Show_Hide_ChildGrid" ImageUrl="~/images/plus.png"
                    CommandArgument="Show" />
                <asp:Panel ID="pnlOrders" runat="server" Visible="false" Style="position: relative">
                    <asp:GridView ID="gvOrders" runat="server" AutoGenerateColumns="false" PageSize="5"
                        AllowPaging="true" OnPageIndexChanging="OnChildGrid_PageIndexChanging" CssClass="ChildGrid">
                            <asp:BoundField ItemStyle-Width="150px" DataField="OrderId" HeaderText="Order Id" />
                            <asp:BoundField ItemStyle-Width="150px" DataField="OrderDate" HeaderText="Date" />
        <asp:BoundField ItemStyle-Width="150px" DataField="ContactName" HeaderText="Contact Name" />
        <asp:BoundField ItemStyle-Width="150px" DataField="City" HeaderText="City" />
You will need to import the following namespaces.
using System.Data;
using System.Data.SqlClient;
using System.Configuration;
Imports System.Data
Imports System.Data.SqlClient
Imports System.Configuration
Binding the Customers records to the Parent GridView
Below is the code to bind the parent ASP.Net GridView with the records of Customers table from the Northwind Database.
protected void Page_Load(object sender, EventArgs e)
    if (!IsPostBack)
        gvCustomers.DataSource = GetData("select top 10 * from Customers");
private static DataTable GetData(string query)
    string constr = ConfigurationManager.ConnectionStrings["constr"].ConnectionString;
    using (SqlConnection con = new SqlConnection(constr))
        using (SqlCommand cmd = new SqlCommand())
            cmd.CommandText = query;
            using (SqlDataAdapter sda = new SqlDataAdapter())
                cmd.Connection = con;
                sda.SelectCommand = cmd;
                using (DataSet ds = new DataSet())
                    DataTable dt = new DataTable();
                    return dt;
Protected Sub Page_Load(sender As Object, e As EventArgs) Handles Me.Load
    If Not IsPostBack Then
        gvCustomers.DataSource = GetData("select top 10 * from Customers")
    End If
End Sub
Private Shared Function GetData(query As String) As DataTable
    Dim constr As String = ConfigurationManager.ConnectionStrings("constr").ConnectionString
    Using con As New SqlConnection(constr)
        Using cmd As New SqlCommand()
            cmd.CommandText = query
            Using sda As New SqlDataAdapter()
                cmd.Connection = con
                sda.SelectCommand = cmd
                Using ds As New DataSet()
                    Dim dt As New DataTable()
                    Return dt
                End Using
            End Using
        End Using
    End Using
End Function
Displaying the Child GridView with the Orders for each Customer in the Parent GridView
On the Click event of the Expand ImageButton of the Parent GridView, I am first searching the Child GridView in the corresponding GridView Row and then populating it with the records from the Orders table of the Northwind Database based on CustomerId stored in the DataKey property for the row to which the ImageButton belongs.
I have made use of the CommandArgument property of the ImageButton to determine whether the operation is of Expand or Collapse.
Also I have implemented Paging for the Child GridView using the OnChildGrid_PageIndexChanging event.
Note: GetData is a generic function and the same function discussed above is used here.
protected void Show_Hide_ChildGrid(object sender, EventArgs e)
    ImageButton imgShowHide = (sender as ImageButton);
    GridViewRow row = (imgShowHide.NamingContainer as GridViewRow);
    if (imgShowHide.CommandArgument == "Show")
        row.FindControl("pnlOrders").Visible = true;
        imgShowHide.CommandArgument = "Hide";
        imgShowHide.ImageUrl = "~/images/minus.png";
        string customerId = gvCustomers.DataKeys[row.RowIndex].Value.ToString();
        GridView gvOrders = row.FindControl("gvOrders") as GridView; gvOrders.ToolTip = customerId;
        gvOrders.DataSource = GetData(string.Format("select * from Orders where CustomerId='{0}'", customerId));
        row.FindControl("pnlOrders").Visible = false;
        imgShowHide.CommandArgument = "Show";
        imgShowHide.ImageUrl = "~/images/plus.png";
private void BindOrders(string customerId, GridView gvOrders)
    gvOrders.ToolTip = customerId;
    gvOrders.DataSource = GetData(string.Format("select * from Orders where CustomerId='{0}'", customerId));
protected void OnChildGrid_PageIndexChanging(object sender, GridViewPageEventArgs e)
    GridView gvOrders = (sender as GridView);
    gvOrders.PageIndex = e.NewPageIndex;
    BindOrders(gvOrders.ToolTip, gvOrders);
Protected Sub Show_Hide_ChildGrid(sender As Object, e As EventArgs)
    Dim imgShowHide As ImageButton = TryCast(sender, ImageButton)
    Dim row As GridViewRow = TryCast(imgShowHide.NamingContainer, GridViewRow)
    If imgShowHide.CommandArgument = "Show" Then
        row.FindControl("pnlOrders").Visible = True
        imgShowHide.CommandArgument = "Hide"
        imgShowHide.ImageUrl = "~/images/minus.png"
        Dim customerId As String = gvCustomers.DataKeys(row.RowIndex).Value.ToString()
        Dim gvOrders As GridView = TryCast(row.FindControl("gvOrders"), GridView)
        BindOrders(customerId, gvOrders)
        row.FindControl("pnlOrders").Visible = False
        imgShowHide.CommandArgument = "Show"
        imgShowHide.ImageUrl = "~/images/plus.png"
    End If
End Sub
Private Sub BindOrders(customerId As String, gvOrders As GridView)
    gvOrders.ToolTip = customerId
    gvOrders.DataSource = GetData(String.Format("select * from Orders where CustomerId='{0}'", customerId))
End Sub
Protected Sub OnChildGrid_PageIndexChanging(sender As Object, e As GridViewPageEventArgs)
    Dim gvOrders As GridView = TryCast(sender, GridView)
    gvOrders.PageIndex = e.NewPageIndex
    BindOrders(gvOrders.ToolTip, gvOrders)
End Sub
Client Side Scripting
Finally some client side scripting using jQuery is required in order to create insert an HTML Row for displaying the Nested or Child GridView.
<script type="text/javascript" src=""></script>
<script type="text/javascript">
    $(function () {
        $("[src*=minus]").each(function () {
            $(this).closest("tr").after("<tr><td></td><td colspan = '999'>" + $(this).next().html() + "</td></tr>");
Collapsible Nested GridView with Paging using ASP.Net