aspBoy Home  |  Advertise

Hierarchical GridView With Clickable Rows

Author: Srikanth Reddy
Bookmark and Share

In this article I am going to demonstrate on how to make a hierarchical GridView which enables the user to open up the hierarchy with the click on a row, which enables the user to close the currently opened hierarchy when the user clicks on another row or the same row. Unlike the hierarchical grids which place a Plus or Minus sign to open and close the hierarchy, we will create this GridView without using them (i.e., the user will be able to just click the row and open up the hierarchy).

So let's get it started.

 

The features this GridView provides are:

1. Hierarchy without plus and minus signs.

2. Opens up the hierarchy on the click of a row.

3. Upon clicking another row, the currently opened hierarchy is closed and the clicked row's hierarchy is opened.

4. Upon clicking the row which has its hierarchy already opened, the hierarchy is closed.

 

Before you start reading the article, Click here to view the demo. Tested on Microsoft Internet Explorer 6, 7, Google Chrome 1.0, Mozilla Firefox 3.0.

 

Now that you have seen the demo let's create a hierarchical GridView.

 

In this article I'll make use of four tables from the Northwind database, Customers, Products, Orders and Order Details. The parent GridView will be bound to the customers table. The query which retrieves all the customers is as shown below:

 

"select CustomerID, CompanyName, Address, City, Country, Phone from Customers";

 

The child GridView displays all the orders placed by each customer. The child GridView's data binding query is as follows:

 

"select Products.Productname, Products.UnitPrice, [Order Details].Quantity, [Order Details].Discount from Products inner join [Order Details]" +

"on Products.ProductID=[Order Details].ProductID" +

" where orderid in(select orderid from orders where customerid='ALFKI')";

 

As the data binding queries are ready, let's add a GridView to the aspx page as shown below:

 

<asp:GridView ID="gvCustomers" runat="server" AutoGenerateColumns="false" Width="100%"

ShowFooter="false" Font-Names="Verdana" Font-Size="Small" OnRowDataBound="gvCustomers_RowDataBound">

</asp:GridView>

 

Pay attention to the bolded parts, AutoGenerateColumns is set to false and we also need to handle the OnRowDataBound event to populate the child GridView.

 

Now lets add the bound fields to display the customer information. After adding the bound fields the GridView looks as shown below:

 

<asp:GridView ID="gvCustomers" runat="server" AutoGenerateColumns="false" Width="100%"

ShowFooter="false" Font-Names="Verdana" Font-Size="Small" OnRowDataBound="gvCustomers_RowDataBound">

<HeaderStyle HorizontalAlign="Left" BackColor="#000000" ForeColor="white" />

<RowStyle BackColor="" ForeColor="Black" />

<Columns>

      <asp:BoundField DataField="CustomerID" HeaderText="Customer ID" />

      <asp:BoundField DataField="CompanyName" HeaderText="Company Name" /> 

      <asp:BoundField DataField="Address" HeaderText="Address" />

      <asp:BoundField DataField="City" HeaderText="City" />

      <asp:BoundField DataField="Country" HeaderText="Country" />

      <asp:BoundField DataField="Phone" HeaderText="Phone" />

</Columns>

</asp:GridView>

 

With this the parent GridView part (to which all the customers are bound) is finished, Now, we need to create a child GridView to show the orders placed by each customer. For this we need to add a TemplateField which holds the nested GridView. After adding the Template Field the entire GridView looks as shown below:

 

<asp:GridView ID="gvCustomers" runat="server" AutoGenerateColumns="false" Width="100%"

ShowFooter="false" Font-Names="Verdana" Font-Size="Small" OnRowDataBound="gvCustomers_RowDataBound">

<HeaderStyle HorizontalAlign="Left" BackColor="#000000" ForeColor="white" />

<RowStyle BackColor="" ForeColor="Black" />

<Columns>

      <asp:BoundField DataField="CustomerID" HeaderText="Customer ID" />

      <asp:BoundField DataField="CompanyName" HeaderText="Company Name" />

      <asp:BoundField DataField="Address" HeaderText="Address" />

      <asp:BoundField DataField="City" HeaderText="City" />

      <asp:BoundField DataField="Country" HeaderText="Country" />

      <asp:BoundField DataField="Phone" HeaderText="Phone" />

       <asp:TemplateField>

       <ItemTemplate>

       </td></tr>

      <tr>

            <td colspan="6">

            <div id="<%# Eval("CustomerID") %>" style="display: none; position: relative">

         <center style="color: #EF3A44">

         <aspLabel ID="lblOrders" runat="server" Text="" Width="100%"></asp:Label></center>

         <asp:GridView ID="gvOrders" runat="server" AutoGenerateColumns="false" Width="100%"

         Font-Names="Verdana" Font-Size="small" EmptyDataText="No orders placed.">

         <HeaderStyle HorizontalAlign="Left" BackColor="#F79196" ForeColor="black" />

         <RowStyle BackColor="white" ForeColor="black" />

         <AlternatingRowStyle BackColor="#FDEAEB" ForeColor="black" />

         <Columns>

                 <asp:BoundField DataField="ProductName" HeaderText="Product" />

                 <asp:BoundField DataField="UnitPrice" HeaderText="Unit Price" />

                 <asp:BoundField DataField="Quantity" HeaderText="Quantity" />

                 <asp:BoundField DataField="Discount" HeaderText="Discount" />

        </Columns>

        </asp:GridView>

          </div>

          </td>

     </tr>

     </ItemTemplate>

     </asp:TemplateField>

</Columns>

</asp:GridView>

 

The bolded part is where the TemplateField starts. Notice that we have used </td></tr> and then opened a new row using <tr>. This is required because if you do not use </td></tr> then the nested GridView appears as the column of the parent GridView (which is not desired). As we have closed a table row and opened a new one the nested GridView displays in a new Row.

 

Now the following line of code is where we show and hide the hierarchy:

 

<div id="<%# Eval("CustomerID") %>" style="display: none; position: relative">

 

Within this div tag we have created the child GridView 'gvOrders'. The div tag's id is set to the id of the customer, a unique id which guarantees a distinct id to each div. Initially, we set its style display property to none, which means initially the parent GridView with list of customers will only be visible. The nested GridView will only be visible when the user clicks on a row. We will register the onclick event on each row in the code behind shortly.

Now that we have completed the aspx part, lets bind the data in the code behind part. The entire code behind part is as shown below:

SqlConnection connection = new SqlConnection(System.Configuration.ConfigurationManager.AppSettings["conString"]);

protected void Page_Load(object sender, EventArgs e)

{

        if (!IsPostBack)

        {

        //Call GetCustomersAndOrders() method to populate the Parent GridView.

        GetCustomersAndOrders();

        }

}

protected void GetCustomersAndOrders()

{

        string strCustomers = "select CustomerID, CompanyName, Address, City, Country, Phone from Customers";

        SqlCommand cmdCustomers = new SqlCommand(strCustomers, connection);

        SqlDataAdapter adpCustomers = new SqlDataAdapter(cmdCustomers);

        DataTable dtCustomers = new DataTable("Customers");

        adpCustomers.Fill(dtCustomers);

        gvCustomers.DataSource = dtCustomers;

        gvCustomers.DataBind();

        gvCustomers.GridLines = GridLines.None;

}

protected void gvCustomers_RowDataBound(object sender, GridViewRowEventArgs e)

{

        if (e.Row.RowType == DataControlRowType.DataRow)

        {

                e.Row.Attributes.Add("onmouseover", "this.style.backgroundColor='#4A4A4A'; this.style.cursor='pointer'; this.style.color='white'");

                e.Row.Attributes.Add("onmouseout", "this.style.backgroundColor='#FFFFFF'; this.style.color='black'");

                e.Row.ToolTip = "Click to view the orders placed by " + e.Row.Cells[1].Text;

                string strOrders = "select Products.Productname, Products.UnitPrice, [Order Details].Quantity, [Order Details].Discount from Products inner join [Order Details]" +

"on Products.ProductID=[Order Details].ProductID" +

" where orderid in(select orderid from orders where customerid='" + e.Row.Cells[0].Text + "')";

                GridView gvOrders = (GridView)e.Row.FindControl("gvOrders");

                Label lblOrders = (Label)e.Row.FindControl("lblOrders");

               if (gvOrders != null)

               {

                          SqlCommand cmdOrders = new SqlCommand(strOrders, connection);

                          SqlDataAdapter adpOrders = new SqlDataAdapter(cmdOrders);

                          DataTable dtOrders = new DataTable("Orders");

                          adpOrders.Fill(dtOrders);

                          gvOrders.DataSource = dtOrders;

                          gvOrders.DataBind();

                          gvOrders.GridLines = GridLines.None;

               }

               if (lblOrders != null)

               {

                         lblOrders.Text = "Orders placed by " + e.Row.Cells[1].Text;

               }

               e.Row.Attributes.Add("onclick", "javascript:CollapseExpand('" + e.Row.Cells[0].Text + "')");

        }

}

 

The GetCustomersAndOrders method binds the data to the parent GridView with list of all customers. In the RowDataBound event (which fires for each row) of the parent GridView('gvCustomers'), we are adding the onmouseover and onmouseout events to change the pointer and row's background and foreground colors.

 

e.Row.Attributes.Add("onmouseover", "this.style.backgroundColor='#4A4A4A'; this.style.cursor='pointer'; this.style.color='white'");

e.Row.Attributes.Add("onmouseout", "this.style.backgroundColor='#FFFFFF'; this.style.color='black'");

 

Similarly, we add the tool tip for each row in the following line:

 

e.Row.ToolTip = "Click to view the orders placed by " + e.Row.Cells[1].Text;

 

Finally we find the GridView and the label control in the following lines:

 

GridView gvOrders = (GridView)e.Row.FindControl("gvOrders");

Label lblOrders = (Label)e.Row.FindControl("lblOrders");

 

Atlast we add the onclick client side event to each row as shown below:

 

e.Row.Attributes.Add("onclick", "javascript:CollapseExpand('" + e.Row.Cells[0].Text + "')");

 

The CollapseExpand(object) method is the client side method which gets called on each click on a GridView's row. The method is as shown below:

 

<script language="JavaScript" type="text/javascript">

var currentlyOpenedDiv="";

function CollapseExpand(object)

{

        var div=document.getElementById(object);

        if(currentlyOpenedDiv!="" && currentlyOpenedDiv!=div)

        {

                currentlyOpenedDiv.style.display="none";

        }

        if(div.style.display=="none")

        {

                div.style.display="inline";

                currentlyOpenedDiv=div;

        }

        else

        {

                div.style.display="none";

        }

}

</script>

  With this we complete the creation of a hierarchical GridView. Please post your valuable comments.
Bookmark and Share
Comments
Name: Sercan
Posted On: 7/20/2010 7:51:50 AM
Subject: Niiice
Comments:
excellenttttttttt
Name: zeeshan ahmad
Posted On: 5/21/2010 12:32:05 AM
Subject: Error on page
Comments:
Please tell me when i click on GridView Colum then message display Error on page. i add GridView at run time and child GridView also add at run time.
Name: Mick
Posted On: 5/10/2010 7:39:12 AM
Subject: Lazy Loading
Comments:
Hi thesamurai, do you have an example of the way you managed to achieve this? I take it your site is no longer available? Thanks Mick
Name: thesamurai
Posted On: 5/3/2010 3:22:25 AM
Subject: Lazy Loading
Comments:
add a select button to the inner grid and pass just the databinding logic to it. switch the javascript ExpandCollapse registration order and execute it after the code behind, with something like ScriptManager.RegisterStartupScript as the last line on the master grid selectindexchanged. that should give you some sort of lazy loading. You might want to check the Rows.Count to avoid multiple bindings too.
Name: Mick
Posted On: 3/10/2010 3:56:29 AM
Subject: Lazy Loading
Comments:
I tried to go to the link thesamurai left but it doesn't appear to be working. Does anybody else have an example of this working with lazyloading Thanks Mick
Name: flodelros
Posted On: 12/13/2009 9:53:36 AM
Subject: Nice One
Comments:
Nice Share, dude.....Good One..... Keep Posting with simple yet innovative Logic
Name: thesamurai
Posted On: 10/22/2009 12:42:06 AM
Subject: Upgraded...
Comments:
I implemented some of the featured asked here like the ajax lazy loading. find it here http://thesamuraiblogger.net/blog/?p=12
Name: Andrea
Posted On: 10/19/2009 3:16:01 AM
Subject: Expand with a button
Comments:
Hello, nice article and really useful. But: What can I do for to exapand the gridview-row with a simple button and not with all row? I've tryed to add a button and add the attributes on click from the onRowDataBound events, but when i press the button it show me the "hidden" elements, but then it returns hidden for the postback. Any suggests? Thanks
Name: Thapasya
Posted On: 8/28/2009 11:57:37 PM
Subject: Great Work
Comments:
Hi Srikanth, you have done a great job. Your code is very useful to me. Thank you
Name: Jack
Posted On: 8/21/2009 10:09:47 AM
Subject: VB.net version
Comments:
Has anyone worked on this using VB instead of C#?
Name: Haris
Posted On: 8/21/2009 12:15:07 AM
Subject: Thanks for this Highly valueable code snippets
Comments:
This is what i am looking for.. thanks
Name: Steven SZE
Posted On: 8/14/2009 9:16:37 AM
Subject: Very nice article. Good work! Srikanth Reddy. Byt the way, performance issue can be improved by addi
Comments:
Code Improvements 1>problem: get a handler error when you add a sorting function to the gridview. solution: create your own gridView_Sorting() function that can handle the sorting, then you rebind it back to the gridview. 2>problem: performance goes down when the records go up to 100 in one page. solution: add a paging function to the 1st level gridview that limits the max. records to 50 per page so the performace will go up. Regards, ---------------- The owner of wusit.com whatratingis.com skylise.com
Name: Steven SZE
Posted On: 8/14/2009 9:16:28 AM
Subject: Very nice article. Good work! Srikanth Reddy. Byt the way, performance issue can be improved by addi
Comments:
Code Improvements 1>problem: get a handler error when you add a sorting function to the gridview. solution: create your own gridView_Sorting() function that can handle the sorting, then you rebind it back to the gridview. 2>problem: performance goes down when the records go up to 100 in one page. solution: add a paging function to the 1st level gridview that limits the max. records to 50 per page so the performace will go up. Regards, ---------------- The owner of wusit.com whatratingis.com skylise.com
Name: Jaimie
Posted On: 7/29/2009 2:30:40 AM
Subject: Nice Article
Comments:
The said function is working fine.But there is a select in my gridview. If i click on that link the nested grid is displaying .Sort out this for me thanks Jaimie.
Name: anukit
Posted On: 7/20/2009 5:26:35 AM
Subject: Paging
Comments:
Interesting article.Based on your input I have gone further and implememted the same over a +/- button click.Everything is fine but am not getting any ideas to implemet paging in my child grid.Any ideas..? BTW my PM's just waiting to knock me down..recession has it's own charm huh!:)
Name: SportyPants
Posted On: 5/26/2009 1:57:48 PM
Subject:
Comments:
Great Article!! Pretty Elegant approach.
Name: Jan Pieters
Posted On: 5/13/2009 5:51:07 AM
Subject: Never mind my previous post
Comments:
I forgot the display stuff in the detail stuff and because of that the javascript behaved not ok.
Name: Jan Pieters
Posted On: 5/13/2009 5:36:54 AM
Subject: Rows appear not collapsed when page is loaded initially
Comments:
How to avoid opening of the details initially, just like the example shows?
Name: jay
Posted On: 5/10/2009 8:09:48 PM
Subject: Lazy loading?
Comments:
Anybody have a sample code for the same with lazy loading? Thanks
Name: Sajeeth AW
Posted On: 5/9/2009 5:17:56 AM
Subject:
Comments:
very nice and useful article, good try!
Name: The Sun
Posted On: 5/8/2009 7:14:10 PM
Subject: Nice
Comments:
It is good article for every body
Name: Michael
Posted On: 5/8/2009 11:51:40 AM
Subject: Re: performance?
Comments:
In a real world situation you wouldn't copy/paste this code and be done with it. You would not want to populate all of the order information for each customer on page load because as you allude to, it will bog down with a realistic amount of customer and order information.. What you can do however is use this article as a framework for developing a solution that retrieves the order information when you click the row and lazy load it with AJAX to keep the presentation smooth.
Name: Adeel Ehsan
Posted On: 5/8/2009 9:08:38 AM
Subject: Nice Job
Comments:
Hey there, nice work done. Just a suggestion, since we are only showing data here, so we can also use parent child Repeaters to do this. Repeater is very fast and less resource consuming.
Name: aspBoy
Posted On: 5/8/2009 7:54:29 AM
Subject: Thank You
Comments:
Thank you very much everyone for your valuable comments and your time Happy Coding
Name: aspBoy
Posted On: 5/8/2009 7:50:38 AM
Subject: RE: Use OnDemand Loading
Comments:
Hi Mohammed Azam nice to see ur comment. Yes adding all the child rows while the parent is loaded adds a performance overhead. The main intention was to focus on creating a hierarchical GridView. I'll also post the on demand version soon. Once again thanks for ur comments.
Name: Kiran
Posted On: 5/8/2009 1:17:24 AM
Subject: Good
Comments:
hi this is really awesome man really gr8
Name: Yasir
Posted On: 5/8/2009 12:57:58 AM
Subject: Nice Article
Comments:
thank you very much for explainging in such a nice way that handling gridview is no problem anymore.
Name: Nanda
Posted On: 5/7/2009 6:10:04 PM
Subject: Excellant!!
Comments:
Very well handled!!
Name: swamy
Posted On: 5/7/2009 3:08:32 AM
Subject: Cool and neat
Comments:
cool article neatly explained
Name: Vinod Marathe
Posted On: 5/6/2009 11:12:36 PM
Subject: Very Nice Artical.
Comments:
This is very good artical.I am going to use it my current project. Thanks,
Name: Bala
Posted On: 5/6/2009 10:13:15 PM
Subject: Nice one
Comments:
iam thinking on performance, post the same with jquery, by doing on demand request.
Name: ChrisDev
Posted On: 5/5/2009 5:31:11 AM
Subject: Nice
Comments:
Pretty neat!
Name: Fergara
Posted On: 5/5/2009 4:58:06 AM
Subject: Excelent
Comments:
Very explanatory.
Name: Hari
Posted On: 5/5/2009 4:41:09 AM
Subject: nice artical
Comments:
Good article
Name: Doyle
Posted On: 5/5/2009 4:04:30 AM
Subject: Thanks Great
Comments:
it helps me a lot thanks!
Name: Doyle A Nuera
Posted On: 5/5/2009 4:03:23 AM
Subject: Thanks a lot to this article
Comments:
same here im trying to find out if it is possible then here it was
Name: Raju
Posted On: 5/5/2009 2:30:29 AM
Subject:
Comments:
Since last 2 months i am finding like this article. Thanks for posting. Keep it posting on GridView. Thanks.
Name: Srujan
Posted On: 5/5/2009 1:52:59 AM
Subject:
Comments:
Its very nice article, but the only problem is performance if there are hundreads of rows
Name: Hey
Posted On: 5/5/2009 1:29:27 AM
Subject: Performance
Comments:
Take a look at your viewstate.. and use lazy loading..
Name: Nishana
Posted On: 5/5/2009 12:16:03 AM
Subject: Nice Article
Comments:
Keep up the good work.
Name: Prashanth
Posted On: 5/4/2009 10:28:00 PM
Subject: Easy to understand but slow
Comments:
This concept is good but this is loading all the records on one event which may make very slow to load the page. It is better to use jquery and load the child details on demand. www.AspnetNova.blogspot.com
Name: kumar
Posted On: 5/4/2009 9:08:02 PM
Subject: gud
Comments:
gud article i am ging to use it my current project http://planetsourcecode.in
Name: Mohammad Azam
Posted On: 5/4/2009 7:49:18 PM
Subject: RE: Use OnDemand Loading
Comments:
Hi, You should not load all the child rows when the parent is loaded. Only load the child when the row is actually clicked. This will increase performance since now you are not loading all the rows at once!
Name: CareySon
Posted On: 5/4/2009 4:27:27 PM
Subject: hi
Comments:
i think its easy to achieve this with jquery.....
Name: Jay
Posted On: 5/4/2009 3:36:27 PM
Subject: performance?
Comments:
Since you are binding every row together, how the performance will be if we have more than 20 rows?
Name: chandradev
Posted On: 4/27/2009 1:39:42 AM
Subject: nice artical
Comments:
It is really nice artical.
Add Comments
Name:
EMail:
Subject:
Comments:
Type the two words with a space in between:
(If the words doesn't appear(due to low connectivity) or if you are unable to recognize them, click on Get a new challenge button adjacent to the text box.)
Follow srikanthreddy_l on Twitter
© 2009 www.aspboy.com  All Rights Reserved.