Nested Gridview binding in asp.net with collapsible feature

ASP.Net GridView binding is really easy but nested binding is a little bit tricky. We need to create two row for every single row one for normal row data and other for nested grid. There are two way to create multiple rows, one is by using the JavaScript by manipulating the html and other is directly from code behind, which we will use in our example, really it is not easy but very easy. We will hide the nested grid on load and will use JavaScript to open and close the nested grid. Here is the output which we are going to discuss in this article

alt text

Let me confirm that we are not going to discuss here paging, sorting and filtering in this article, I know you are expert in it and will do it easily. First of all we are going to write the code to get data from Northwind database Customers table and nested grid will show the orders for every customer. I will show maximum 3 records in this example, if you want you can use the paging and show whatever number of records you want to show in inner grid. So here is the code to get the customers and orders on the basis of customer’s Id

public List<Customer> GetCustomers(Int32 Page = 1, Int32 PageSize = 10)
{
    var db = new NWEntities();
    var customers = db.Customers
        //.Include("Orders")
        //.Include("Suppliers")
        .OrderBy(x => x.ContactName)
        .Skip((Page - 1) * PageSize)
        .Take(PageSize).ToList();
    return customers;
}

public List<Order> GetCustomerOrders(String customerId)
{
    var db = new NWEntities();
    var orders = db.Orders
        .Where(x => x.CustomerID == customerId)
        .Take(3).ToList();
    return orders;
}

I used Entity Framework to get data from database, I don’t think you want me to give the detail of EF. First method “GetCustomers” will return 10 customers and other method “GetCustomerOrders” will return 3 order for the provided customer Id. Here is the HTML of the page, look at it, we will discuss it every part of the code which is tricky

<asp:GridView ID="gvCustomers" runat="Server"
    DataKeyNames="CustomerID"
    Width="100%"
    GridLines="None"
    HeaderStyle-CssClass="gvHeader"
    CssClass="gvRow"
    AlternatingRowStyle-CssClass="gvAltRow"
    AutoGenerateColumns="False" 
    OnRowDataBound="gvCustomers_RowDataBound">
    <Columns>
        <asp:TemplateField>
            <ItemTemplate>
                <img src="/images/close-arrow.png" 
                    onclick="ShoeHide(this, 'tr<%# Eval("CustomerID") %>')" />
            </ItemTemplate>
        </asp:TemplateField>
        <asp:BoundField DataField="CustomerID" HeaderText="ID" />
        <asp:BoundField DataField="ContactName" HeaderText="Customer" />
        <asp:BoundField DataField="Address" HeaderText="Address" />
        <asp:BoundField DataField="City" HeaderText="City" />
        <asp:BoundField DataField="Country" HeaderText="Country" />

        <asp:TemplateField  HeaderText="Phone">
           <ItemTemplate>
             <%# Eval("Phone") %>
             <%# MyNewRow(Eval("CustomerID")) %>                     
                <asp:GridView ID="gvOrders" runat="server"
                    Width="100%"
                    GridLines="None"
                    AutoGenerateColumns="false"
                    DataKeyNames="CustomerID"
                    HeaderStyle-CssClass="gvChildHeader"
                    CssClass="gvRow" 
                    style="padding:0; margin:0"
                    AlternatingRowStyle-CssClass="gvAltRow">
                    <Columns>
                       <asp:BoundField DataField="OrderID" HeaderText="Order ID" />
                       <asp:BoundField DataField="OrderDate" HeaderText="Order Date" 
                            DataFormatString="{0:MM-dd-yy}" />
                       <asp:BoundField DataField="ShippedDate" HeaderText="Shipped On" 
                            DataFormatString="{0:MM-dd-yy}" />
                       <asp:BoundField DataField="ShipName" HeaderText="Ship To Name" />
                       <asp:BoundField DataField="ShipCity" HeaderText="City" />
                    </Columns>
                 </asp:GridView>

            </ItemTemplate>
        </asp:TemplateField>
    </Columns>
</asp:GridView>    
  1. DataKeyNames="CustomerID" : we will use it get the current customer Id in row database to get order from the customer
  2. OnRowDataBound: we need to add this event to bind our nested grid, open the page in design view and select the grid and open property window, from the top select events icon and click on OnRowDataBound and it will add the even in our grid and write the code in code behind file, we will code this event latter.
  3. The very first column is a TemplateField in which we will use our arrow image to show hide the order grid, in this column we added an image with onclick event to Show/Hide, ShowHide is a JavaScript function in which we pass two parameters, one is this, to change the arrow image and other is the table row Id which we have to show or hide.
  4. Last column is again a TemplateField in which we show the phone number and called a code behind method and passed the customer Id to create the Id of that row, this code behind method will create a new row.
  5. In the last template field we added our child grid and bind the columns which we will bind on run time in OnRowDataBound event.

There is nothing special which need more detail, here is the code behind method to create the new row

public String MyNewRow(object customerId)
{
    /* 
        * 1. Close current cell in our example phone </TD>
        * 2. Close Current Row </TR>
        * 3. Cretae new Row with ID and class <TR id='...' style='...'>
        * 4. Create blank cell <TD></TD>
        * 5. Create new cell to contain the grid <TD>
        * 6. Finall grid will close its own row
        ************************************************************/ 
    return String.Format(@"</td></tr><tr id ='tr{0}' style='collapsed-row'>
               <td></td><td colspan='100' style='padding:0px; margin:0px;'>", customerId);
}

I wrote complete detail to create a new row from code behind, I put colspan = '100' so it can expand to the end, is not it easy to write you html in code behind.

Now time to write the code to bind the grid on load of the page and child grid in OnRowDataBound, so here is the code bind the grid on page load, I created a method BindGrid() and called on page load

protected void Page_Load(object sender, EventArgs e)
{
    if (!IsPostBack)
        BindGrid();
}

private void BindGrid()
{
    var repository = new CustomersRepository();
    var customers = repository.GetCustomers(1, 10);
    gvCustomers.DataSource = customers;
    gvCustomers.DataBind();
}

One you clicked on OnRowDataBound event in property it created this method so let’s complete it to get the data from database for the current row and find the grid in this row and bind with searched data

protected void gvCustomers_RowDataBound(object sender, GridViewRowEventArgs e)
{
    if (e.Row.RowType == DataControlRowType.DataRow)
    {
        string customerId = gvCustomers.DataKeys[e.Row.RowIndex].Value.ToString();
        var gvOrders = (GridView)e.Row.FindControl("gvOrders");

        var repository = new CustomersRepository();
        var orders = repository.GetCustomerOrders(customerId);
        gvOrders.DataSource = orders;
        gvOrders.DataBind();        
    }
}

Final step is to add the JavaScript to show and hide the child grid and also change the arrow direction according to the current state of the row, here is our JavaScript

<script type="text/javascript">
    function ShoeHide(img, div) {
        var current = $('#' + div).css('display');
        if (current == 'none') {
            $('#' + div).show('slow');
            $(img).attr('src', '/images/open-arrow.png');
        }
        else {
            $('#' + div).hide('slow');
            $(img).attr('src', '/images/close-arrow.png');
        }
    }
</script>

Let me add the CSS as well which I used to color the grid header, row and alternate row and hide the child grid, there is nothing special just coloring and hide the child grid by using display property of CSS

.gvHeader th
{
    padding: 7px;
    background-color: #1A4C1A;
    color: #fff;
    border: 1px solid #bbb;
    font-weight:normal;
}

.gvChildHeader th
{
    padding: 4px;
    background-color: #999966;
    color: #fff;
    border: 1px solid #bbb;
    font-weight:normal;
}

.gvRow td
{
    padding: 7px;
    background-color: #ffffff;
    border: 1px solid #bbb;
}

.gvAltRow td
{
    padding: 7px;
    background-color: #f1f1f1;
    border: 1px solid #bbb;
}
.collapsed-row
{
    display:none;
    padding:0px;
    margin:0px;
}

I forgot to notify that I used jQuery to show hide the child grid if you don’t what to use the jQuery then simply change it with normal JavaScript code.

Ali Adravi Having 13+ years of experience in Microsoft Technologies (C#, ASP.Net, MVC and SQL Server). Worked with Metaoption LLC, for more than 9 years and still with the same company. Always ready to learn new technologies and tricks.
  • asp.net
  • gridview
  • c#
By Ali Adravi On 03 Dec, 14  Viewed: 11,538

Other blogs you may like

Readonly textbox postback issues and solutions

In many cases we need to use read only text box so user cannot edit the value but readonly textbox will lost its value after postback. Let’s say you have a read only text box to set date by using ajax calendar so after post back date will be lost, other case might be you are setting some value in... By Ali Adravi   On 24 Apr 2013  Viewed: 4,302

Call code behind method from JavaScript in asp.net

There are ways to call a web service method JavaScript, for more detail you can see [how to retrieve data from database using JavaScript in asp.net][1], but is there any way to call a normal method from JavaScript? And the answer is No; show how we can call a code behind method from JavaScript,... By Jonathan King   On 08 Apr 2013  Viewed: 12,506

Gridview paginated data with search and sort functionality in asp.net

Most of the times we need to use GridView control to show tabular data in our asp.net application. We simply write procedure to search the records and bind them with GridView and show 10 to 20 records per page. Have you ever thought that why you are fetching all the records and show only 10 to 20... By Ali Adravi   On 16 Feb 2013  Viewed: 8,569

ASP.Net 4.5 new feature Model Binding

A nice feature with ASP.Net is the model binding, it reduced our code and effort to bind our well know controls like GridView, DataList, Repeater etc. So let’s see how we can bind our old controls in new and easy way. Let’s see how we bind the our grid before 4.5 1. We write the code to get... By Mike .Net   On 17 Jan 2013  Viewed: 3,217

Upload multiple image in multiple size with progress bar in asp.net

In asp.net there is not control to select multiple files and upload them once with progress bar, so we will use a small third party DLL to achieve this functionality. We will use Flajaxian FileUploader, you can download it from [http://www.flajaxian.com][1] We will create three different images... By Hamden   On 12 Jul 2012  Viewed: 6,567