This article illustrates how to implement Three (3) Levels of GridView.
Database
This article uses Microsoft’s Northwind Database. You can download it from here.
HTML Markup
The HTML Markup consists of:
GridView – For displaying parent records.
Inside a
GridView there another
GridView which is used to display child records.
Note: The CustomerID and OrderID fields are stored in the DataKeys property of the GridView as that will be required to populate the Child or Nested GridView of Orders as well as Products.
The following HTML Markup consists of:
Customers GridView: The
Customers GridView is the Main
GridView and it consists of a ImageButton and Panel inside a
TemplateField Column.
The Panel consists of another
GridView i.e.
Orders GridView which in turn consists of
Products GridView.
The
Customers and
Orders GridView consists of
ImageButton to hide and show the respective Child GridViews.
<asp:GridView ID="gvCustomers" runat="server" AutoGenerateColumns="false" CssClass="Grid"
DataKeyNames="CustomerID">
<Columns>
<asp:TemplateField>
<ItemTemplate>
<asp:ImageButton ID="imgOrdersShow" runat="server" OnClick="OnShowHideOrdersGrid"
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="OnOrdersGrid_PageIndexChanging" CssClass="ChildGrid"
DataKeyNames="OrderId">
<Columns>
<asp:TemplateField>
<ItemTemplate>
<asp:ImageButton ID="imgProductsShow" runat="server" OnClick="OnShowHideProductsGrid"
ImageUrl="~/images/plus.png" CommandArgument="Show" />
<asp:Panel ID="pnlProducts" runat="server" Visible="false" Style="position:relative">
<asp:GridView ID="gvProducts" runat="server" AutoGenerateColumns="false" PageSize="2"
AllowPaging="true" OnPageIndexChanging="OnProductsGrid_PageIndexChanging" CssClass="Nested_ChildGrid">
<Columns>
<asp:BoundField ItemStyle-Width="150px" DataField="ProductId" HeaderText="Product Id" />
<asp:BoundField ItemStyle-Width="150px" DataField="ProductName" HeaderText="Product Name" />
</Columns>
</asp:GridView>
</asp:Panel>
</ItemTemplate>
</asp:TemplateField>
<asp:BoundField ItemStyle-Width="150px" DataField="OrderId" HeaderText="Order Id" />
<asp:BoundField ItemStyle-Width="150px" DataField="OrderDate" HeaderText="Date" />
</Columns>
</asp:GridView>
</asp:Panel>
</ItemTemplate>
</asp:TemplateField>
<asp:BoundField ItemStyle-Width="150px" DataField="ContactName" HeaderText="Contact Name" />
<asp:BoundField ItemStyle-Width="150px" DataField="City" HeaderText="City" />
</Columns>
</asp:GridView>
Namespaces
You will need to import the following namespaces.
C#
using System.Data;
using System.Data.SqlClient;
using System.Configuration;
VB.Net
Imports System.Data
Imports System.Data.SqlClient
Imports System.Configuration
Binding the Customers records to the Level 1 GridView
Inside the
Page_Load event,
GetData method is called and the records of
Customers table are fetched and displayed in
GridView.
Note: GetData is a generic function and the same function discussed above is used here.
C#
protected void Page_Load(object sender, EventArgs e)
{
if (!this.IsPostBack)
{
gvCustomers.DataSource = GetData("select top 10 * from Customers");
gvCustomers.DataBind();
}
}
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();
sda.Fill(dt);
return dt;
}
}
}
}
}
VB.Net
Protected Sub Page_Load(sender As Object, e As EventArgs) Handles Me.Load
If Not Me.IsPostBack Then
gvCustomers.DataSource = GetData("select top 10 * from Customers")
gvCustomers.DataBind()
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()
sda.Fill(dt)
Return dt
End Using
End Using
End Using
End Using
End Function
Displaying the Child (Level 2) GridView with the Orders for each Customer in the Parent (Level 1) GridView
On the
Click event of the
Expand ImageButton of the Parent
GridView, first the Child
GridView in the corresponding
GridView Row is referenced and then populated with the records from the
Orders table of the Northwind Database based on
CustomerId stored in the
DataKey property for the row.
The CommandArgument property of the ImageButton is used to determine whether the operation is of Expand or Collapse.
The Paging for the Child
GridView is done using the
OnOrdersGrid_PageIndexChanging event.
C#
protected void OnShowHideOrdersGrid(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;
this.BindOrders(customerId,gvOrders);
}
else
{
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));
gvOrders.DataBind();
}
protected void OnOrdersGrid_PageIndexChanging(object sender, GridViewPageEventArgs e)
{
GridView gvOrders = (sender as GridView);
gvOrders.PageIndex = e.NewPageIndex;
this.BindOrders(gvOrders.ToolTip, gvOrders);
}
VB.Net
Protected Sub OnShowHideOrdersGrid(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)
Me.BindOrders(customerId,gvOrders)
Else
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))
gvOrders.DataBind()
End Sub
Protected Sub OnOrdersGrid_PageIndexChanging(sender As Object, e As GridViewPageEventArgs)
Dim gvOrders As GridView = TryCast(sender, GridView)
gvOrders.PageIndex = e.NewPageIndex
Me.BindOrders(gvOrders.ToolTip, gvOrders)
End Sub
Displaying the Child (Level 3) GridView with the Products for each Order placed by a Customer in the Products Grid (Level 3) GridView
On the
Click event of the
Expand ImageButton of the Parent
GridView, first the Child
GridView in the corresponding
GridView Row is referenced and then populated with the records from the
Products table of the Northwind Database based on
OrderID stored in the
DataKey property for the row.
The CommandArgument property of the ImageButton is used to determine whether the operation is of Expand or Collapse.
The Paging for the Child
GridView is done using the
OnProductsGrid_PageIndexChanging event.
C#
protected void OnShowHideProductsGrid(object sender, EventArgs e)
{
ImageButton imgShowHide = (sender as ImageButton);
GridViewRow row = (imgShowHide.NamingContainer as GridViewRow);
if (imgShowHide.CommandArgument == "Show")
{
row.FindControl("pnlProducts").Visible = true;
imgShowHide.CommandArgument = "Hide";
imgShowHide.ImageUrl = "~/images/minus.png";
int orderId = Convert.ToInt32((row.NamingContainer as GridView).DataKeys[row.RowIndex].Value);
GridView gvProducts = row.FindControl("gvProducts") as GridView;
this.BindProducts(orderId, gvProducts);
}
else
{
row.FindControl("pnlProducts").Visible = false;
imgShowHide.CommandArgument = "Show";
imgShowHide.ImageUrl = "~/images/plus.png";
}
}
private void BindProducts(int orderId, GridView gvProducts)
{
gvProducts.ToolTip = orderId.ToString();
gvProducts.DataSource = GetData(string.Format("SELECT ProductId, ProductName FROM Products WHEREProductId IN (SELECT ProductId FROM [Order Details] WHEREOrderId = '{0}')", orderId));
gvProducts.DataBind();
}
protected void OnProductsGrid_PageIndexChanging(object sender, GridViewPageEventArgs e)
{
GridView gvProducts = (sender as GridView);
gvProducts.PageIndex = e.NewPageIndex;
this.BindProducts(int.Parse(gvProducts.ToolTip), gvProducts);
}
VB.Net
Protected Sub OnShowHideProductsGrid(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("pnlProducts").Visible = True
imgShowHide.CommandArgument = "Hide"
imgShowHide.ImageUrl = "~/images/minus.png"
Dim orderId As Integer = Convert.ToInt32(TryCast(row.NamingContainer, GridView).DataKeys(row.RowIndex).Value)
Dim gvProducts As GridView = TryCast(row.FindControl("gvProducts"), GridView)
Me.BindProducts(orderId,gvProducts)
Else
row.FindControl("pnlProducts").Visible = False
imgShowHide.CommandArgument = "Show"
imgShowHide.ImageUrl = "~/images/plus.png"
End If
End Sub
Private Sub BindProducts(orderId As Integer, gvProducts As GridView)
gvProducts.ToolTip = orderId.ToString()
gvProducts.DataSource = GetData(String.Format("SELECT ProductId, ProductName FROM Products WHEREProductId IN (SELECT ProductId FROM [Order Details] WHEREOrderId = '{0}')", orderId))
gvProducts.DataBind()
End Sub
Protected Sub OnProductsGrid_PageIndexChanging(sender As Object, e As GridViewPageEventArgs)
Dim gvProducts As GridView = TryCast(sender, GridView)
gvProducts.PageIndex = e.NewPageIndex
Me.BindProducts(Integer.Parse(gvProducts.ToolTip), gvProducts)
End Sub
Client Side
Inside the HTML Markup, the following
jQuery JS file is inherited.
1. jquery.min.js
Inside the
jQuery document ready event handler, a loop is executed for all the
Orders and
Products ImageButton and the corresponding Child GridViews appended next to the Button.
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/3.7.1/jquery.min.js"></script>
<script type="text/javascript">
$(function () {
$("[id*= imgOrdersShow]").each(function () {
if ($(this)[0].src.indexOf("minus") != -1) {
$(this).closest("tr").after("<tr><td></td><td colspan = '999'>" + $(this).next().html() + "");
$(this).next().remove();
}
});
$("[id*= imgProductsShow]").each(function () {
if ($(this)[0].src.indexOf("minus") != -1) {
$(this).closest("tr").after("<tr><td></td><td colspan = '999'>" + $(this).next().html() + "");
$(this).next().remove();
}
});
});
</script>
CSS Style for displaying GridViews
The GridView’s have been assigned with the following CSS.
<style type="text/css">
body {font-family: Arial; font-size: 10pt; }
.Grid th {background-color: #6C6C6C; color: White; font-size: 10pt; line-height: 200%; }
.Grid td {background-color: #eee; color: black; font-size :10pt; line-height: 200%; }
.ChildGrid th {background-color: #6C6C6C; color: #fff; font-size: 10pt; line-height: 200%; }
.ChildGrid td {background-color: #fff; color: black; font-size: 10pt; line-height: 200%; }
</style>
Screenshot
Demo
Downloads