In this article I will explain with an example, how to implement Nested Grid (GridView) with Expand Collapse in ASP.Net MVC Razor.
The Nested Grid (GridView) will be implemented with the help of WebGrid in ASP.Net MVC Razor.
The Nested WebGrid will be a combination of Parent – Child Table where Child WebGrid will be placed inside the Parent WebGrid and the Child WebGrid will be shown and hidden using Expand-Collapse feature in ASP.Net MVC Razor.
Here I am making use of Microsoft’s Northwind Database. You can download it from here.
Entity Framework Model
Once the Entity Framework is configured and connected to the database table, the Model will look as shown below.
Note: For beginners in ASP.Net MVC and Entity Framework, please refer my article ASP.Net MVC: Simple Entity Framework Tutorial with example. It covers all the information needed for connecting and configuring Entity Framework.
Implement Nested Grid (GridView) in ASP.Net MVC
The following Model class consists of three properties.
public class CustomerModel
    public string ContactName { get; set; }
    public string City { get; set; }
    public List<Order> Orders { get; set; }
The Controller consists of the following Action method.
Action method for handling GET operation of Index View
The Entity Framework is now configured and hence now we can create a Controller and write code to fetch the records from the Customers Table of the Northwind Database.
Inside the Index Action method, the Customer records are fetched using Entity Framework and a loop is executed over the fetched records.
Inside the loop, the values of ContactName, City and the List of Orders for each Customer is assigned to an object of CustomerModel class and the object is added to a List of CustomerModel class.
Finally, the List of CustomerModel class are returned to the View.
public class HomeController : Controller
    // GET: Home
    public ActionResult Index()
        using (NorthwindEntities entites = new NorthwindEntities())
            List<CustomerModel> model = new List<CustomerModel>();
            foreach (Customer customer in entites.Customers.Take(10))
                model.Add(new CustomerModel
                    ContactName = customer.ContactName,
                    City = customer.City,
                    Orders = entites.Orders.Where(o => o.CustomerID == customer.CustomerID).Take(5).ToList()
            return View(model);
Inside the View, the CustomerModel class is declared as IEnumerable which specifies that the Model will be available as a Collection.
Populating the Nested WebGrid
For displaying the Parent Table records, the WebGrid is rendered using GetHtml function which renders the WebGrid using Model.
The WebGrid is initialized with the Model i.e. IEnumerable collection of CustomerModel class objects as source.
Note: For more details on using WebGrid, please refer WebGrid Step By Step Tutorial with example in ASP.Net MVC.
The first Cell of the WebGrid contains an IMG element and a hidden HTML DIV. The Child WebGrid will be copied to this HTML DIV using jQuery.
The Child WebGrid will be populated using IEnumerable collection of Orders of each CustomerModel object.
Showing Hiding Child WebGrid with Expand-Collapse feature
Inside the jQuery document ready event handler, each Child WebGrid will be removed from last column and will be copied to the hidden HTML DIV in the first column.
Then each HTML IMG element with Plus image has been assigned a jQuery Click event handler. When the IMG element is clicked, the contents of Child WebGrid are copied from the HTML DIV and are appended to the Parent WebGrid in a new Row.
And the Plus image is replaced with Minus image.
In similar way, each HTML IMG element with Minus image has been assigned a jQuery Click event handler. When the IMG element is clicked, the new HTML Table Row which was appended with the Child WebGrid is removed.
And the Minus image is replaced with Plus image.
@using Nested_WebGrid_MVC.Models
@model IEnumerable<CustomerModel>
    Layout = null;
    WebGrid webGrid = new WebGrid(source: Model, canSort: false, canPage: false);
<!DOCTYPE html>
    <meta name="viewport" content="width=device-width"/>
    <style type="text/css">
        body { font-family: Arial; font-size: 10pt; }
        .Grid { border: 1px solid #ccc; border-collapse: collapse; background-color: #fff; }
        .Grid th { background-color: #B8DBFD; color: #333; font-weight: bold; }
        .Grid th, .Grid td { padding: 5px; border: 1px solid #ccc; }
        .Grid img { cursor:pointer; }
        .ChildGrid { width: 100%; }
        .ChildGrid th { background-color: #6C6C6C; color: #fff; font-weight: bold; }
        htmlAttributes: new { @id = "WebGrid", @class = "Grid" },
        columns: webGrid.Columns(
                 webGrid.Column(null, null, format: @<text><img src="~/Images/plus.png"/><div style="display:none"></div></text>),
                         webGrid.Column("ContactName", "Contact Name"),
                         webGrid.Column("City", "City"),
                         webGrid.Column(format: (item) =>
                             WebGrid childGrid = new WebGrid(source: item.Orders, canSort: false, canPage: false);
                             return childGrid.GetHtml(
                             htmlAttributes: new { @class = "ChildGrid" },
                             columns: childGrid.Columns(
                                     childGrid.Column("OrderId", "OrderId"),
                                     childGrid.Column("OrderDate", "OrderDate")
    <script type="text/javascript" src=""></script>
    <script type="text/javascript">
        $(function () {
            //Loop through all Child Grids.
            $("#WebGrid .ChildGrid").each(function () {
                //Copy the Child Grid to DIV.
                var childGrid = $(this).clone();
                //Remove the Last Column from the Row.
            //Remove Last Column from Header Row.
            $("#WebGrid TH:last-child").eq(0).remove();
        //Assign Click event to Plus Image.
        $("body").on("click", "img[src*='plus.png']", function () {
            $(this).closest("tr").after("<tr><td></td><td colspan = '999'>" + $(this).next().html() + "</td></tr>");
            $(this).attr("src", "/images/minus.png");
        //Assign Click event to Minus Image.
        $("body").on("click", "img[src*='minus.png']", function () {
            $(this).attr("src", "/images/plus.png");
Implement Nested Grid (GridView) in ASP.Net MVC