In this article I will explain with an example, how to build a nested
GridViews i.e.
GridView inside
GridView with expand collapse functionality to show and hide the child
GridView in
ASP.Net using C# and VB.Net.
Database
Here I am making use of Microsoft’s Northwind Database. You can download it from here.
HTML Markup
The
HTML Markup consists of:
GridView – For displaying parent records.
Inside the
GridView, there another
GridView which is used to display child records placed inside an
ASP.Net Panel control.
<asp:GridView ID="gvCustomers" runat="server" AutoGenerateColumns="false" CssClass="Grid"
DataKeyNames="CustomerID" OnRowDataBound="OnRowDataBound">
<Columns>
<asp:TemplateField>
<ItemTemplate>
<img alt="" style="cursor:pointer" src="images/plus.png" />
<img alt="" style="cursor:pointer;display:none" src="images/minus.png" />
<asp:Panel ID="pnlOrders" runat="server" Style="display:none">
<asp:GridView ID="gvOrders" runat="server" AutoGenerateColumns="false" CssClass="ChildGrid">
<Columns>
<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>
Generic function to populate DataTable
The following function is used throughout this article to populate
DataTable with Database records.
It accepts the SQL Query as parameter and it returns the
DataTable object containing the Database records.
C#
private static DataTable GetData(string query)
{
string constr = ConfigurationManager.ConnectionStrings["constr"].ConnectionString;
using (SqlConnection con = new SqlConnection(constr))
{
using (SqlDataAdapter sda = new SqlDataAdapter(query, con))
{
using (DataTable dt = new DataTable())
{
sda.Fill(dt);
return dt;
}
}
}
}
VB.Net
Private Shared Function GetData(query As String) As DataTable
Dim constr As String = ConfigurationManager.ConnectionStrings("constr").ConnectionString
Using con As New SqlConnection(constr)
Using sda As New SqlDataAdapter(query, con)
Using dt As New DataTable()
sda.Fill(dt)
Return dt
End Using
End Using
End Using
End Function
Binding Parent GridView
Inside the Page Load event, GetData method is called.
Inside
GetData method, the records fetched from the
Customers table are bound to the parent
ASP.Net GridView.
C#
protected void Page_Load(object sender, EventArgs e)
{
if (!this.IsPostBack)
{
gvCustomers.DataSource = GetData("SELECT TOP 10 * FROM Customers");
gvCustomers.DataBind();
}
}
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
Binding the Child GridView with the Orders for each Customer in the Parent GridView
Inside the
OnRowDataBound event handler,
CustomerId is referenced using the
DataKeys property and then, the child
GridView is referenced.
Finally, the child
GridView is populated with the records from the
Orders table fetched based on
CustomerId.
C#
protected void OnRowDataBound(object sender, GridViewRowEventArgs e)
{
if (e.Row.RowType == DataControlRowType.DataRow)
{
string customerId = gvCustomers.DataKeys[e.Row.RowIndex].Value.ToString();
GridView gvOrders = e.Row.FindControl("gvOrders")as GridView;
gvOrders.DataSource = GetData(string.Format("SELECT TOP 3 * FROM Orders WHERE CustomerId='{0}'", customerId));
gvOrders.DataBind();
}
}
VB.Net
Protected Sub OnRowDataBound(sender As Object, e As GridViewRowEventArgs)
If e.Row.RowType = DataControlRowType.DataRow Then
Dim customerId As String = gvCustomers.DataKeys(e.Row.RowIndex).Value.ToString()
Dim gvOrders As GridView = TryCast(e.Row.FindControl("gvOrders"), GridView)
gvOrders.DataSource = GetData(String.Format("SELECT TOP 3 * FROM Orders WHERE CustomerId='{0}'", customerId))
gvOrders.DataBind()
End If
End Sub
Client Side Expand Collapse functionality using jQuery
Inside the
HTML Markup, the
jQuery JS script file is inherited.
Plus Button
The plus image has been assigned with a
jQuery click event handler.
Inside this event handler, the child Grid html is referenced and appended to the Parent
GridView row and the minus image is displayed and the plus image is made hidden.
Minus Button
The minus image has been assigned with a
jQuery click event handler.
Inside this event handler, the child Grid is removed from the Parent
GridView row and the plus image is displayed and the minus image is made hidden.
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.7.1/jquery.min.js"></script>
<script type="text/javascript">
$("[src*=plus]").on("click", function () {
$(this).closest("tr").after("<tr><td></td><td class='orders' colspan = '999'>" + $(this).closest("tr").find("[id*=Orders]").html() +"</td></tr>");
$(this).closest("tr").find("[src*=minus]").show();
$(this).hide();
});
$("[src*=minus]").on("click", function () {
$(this).closest("tr").next().remove();
$(this).closest("tr").find("[src*=plus]").show();
$(this).hide();
});
</script>
Screenshot
Demo
Downloads