Introduction

This article presents how the GridView control can be nested to show master/detail relationships for multilevel hierarchal data. In this article, I have implemented it for three levels, and it can be implemented for n levels quite easily, by maintaining the information about the edit index of the parent GridView controls.

This article provides a simple, easy, and clean solution for displaying data with multilevel master/detail relationships. This article also shows how the GridView can be used effectively by using its template fields, and how controls inside template fields are accessed and bound. More importantly, you will realize the power of GridView's edit mode, and learn how it can be used to do things other than editing; evading from its default usage!!

Background

I happened to work on an HRM system of a company where I had to display details of employees' log events. This was a three level master detail relationship. At the first level, I had to show all the employees of the company (in a ParentGridView). When clicked at any employee, at the second level, I had to show the days in a month when he/she was present (in a ChildGridView), and on the third level, when clicked on a particular day, the corresponding log events of that day (login or log out) had to be displayed (in a GrandChildGridView). I implemented this multilevel master detail relationship by handling the OnRowEditing event of a GridView just to get the new edit index of the parent GridView and save it in the session for the child GridViews.

Using the code

In this particular demo version, I have used Northwind’s Pubs database. The tables Publishers, Titles, and Roysched have a three level master detail hierarchical relationship: each publisher has published multiple titles, and each title has multiple royalty schedules. There is a parent GridView named ParentGridView showing the Publishers table, a child GridView named ChildGridView to display the corresponding records of the Titles table, and a grand child GridView named GrandChildGridView to show the corresponding royalty schedule of a particular title. All parent GridViews need to be templated to show the child one in edit mode. The SelectCommand for the ParentGridView shows that it will have three template fields, pub_id, pub_name, and city, yet there will be a fourth template field that will be used to show the child GridView.

SelectCommand="SELECT [pub_id], pub_name], [city] FROM [publishers]";

I have removed the formatting code and explained the implementation of all three GridViews separately for the sake of clarity. I will show where a child GridView should be nested by writing its name with a star just to increase readability and understanding.

Parent GridView

The parent GridView is templated for all columns selected in the SelectCommand. As you can see, in the code, there is one more template field named ‘View’ which is the only part where the magic lies! In its IetmTemplate, there is a button with the Text, ‘+’, and CommandName, ‘Edit’. This plus sign button, when clicked, lets the GridView to enter edit mode, and fires the OnRowEditing event which is handled. What is to be shown in the edit mode is implemented in the EditItemTemplate. As shown in the code, it has a button with CommandName 'Cancel' and Text ‘-’ (to collapse the GridView by just running the Cancel command, which resets the edit index to -1 and the ParentGridView comes out of editing mode; obviously, the ChildGridView shown in edit mode will no longer be visible, which gives the notion of collapsibility). Next to the Cancel ‘-' button is a ChildGridView.

The event handling code for OnRowEditing is quite self-explanatory, yet it's is important to know that the OnRowEditing event does not have any code for editing. Instead, it sets the the ParentGridView’s edit index to that of the current row and binds it. It saves the index and pub_id in the session, because later, we’ll need both of them: pub_id to get the corresponding tuples of Titles against it, and ParentGridViewIndex is needed to find the row containing the ChildGridView whose row has been selected.

Child GridView

Let's take a look at the SelectCommand of the DataSource for the ChildGridView. Its WHERE clause needs pub_id, which we have already stored in Session[“Pub_ID”].

<SelectCommand="SELECT [title_id],[title], [type],[price]
FROM [titles] WHERE ([pub_id] = ?) ORDER BY [price]"><SelectParameters><asp:SessionParameterName="PubID"SessionField="PubID"Type="Int32"/></SelectParameters>

The GrandChildView is templated in the same way as ParentGridView. It also has a template field named ‘View’ to show its own child grid (GrandChildGridView) with the same buttons, and the same commands on the same positions. It also implements its OnRowEditing, which is a little different than that of its parent. Here, the child grid view is not directly accessible as it is nested in the parent one. To set its edit index with new edit index, first we need to find it from the particular row of ParentGridView. The row of ParentGridView containing the ChildGridView is found using ParentGridViewIndex from the session variable.

Grand Child GridView

The WHERE clause of GrandChildDataSource’s SelectCommand needs the value of title_id from its immediate parent table that was stored in the session when the particular title was selected. It is now being used to show all the rows of the roysched table against it in the GrandChildGridView.

<SelectCommand="select [lorange],[hirange],roysched.royalty
from [roysched], [titles] where
titles.title_id=roysched.title_id
and (roysched.title_id=?)"><SelectParameters><asp:SessionParameterName="title_id"SessionField="TitleID"Type="String"/></SelectParameters>

There is no need to use GrandChildGridView with template fields as we don't need any further child relationship with it. Otherwise, we'll have to implement it using template fields.

Points of Interest

It is important to know that I have used the GridView's Edit mode for displaying the child GridView. No editing is done in the Edit mode, yet simultaneously, it can be done if desired. The OnRowEditing event does not contain any code for editing, rather it does what is needed; the edit index and the primary key value stored in the session will be used in the OnRowEditing event of the child GridViews for searching the particular row of the parent GridView and finding the child GridView from it, and later will be used in the SelectCommand of the child GridView to get all the child rows against that particular parent ID. The Cancel command already does what we want; it cancels the editing, and the parent GridView comes out of the Edit mode hiding its child GridView.

If any parent GridView is collapsed in the hierarchy, its entire children will automatically collapse.

More importantly, it can be implemented for any number of levels by simply keeping the track of primary keys and edit indexes of all parent GridViews of a child GridView. Every child GridView knows its own edit index whereas it needs to know the edit index of all its parent GridViews, which can be provided by storing the edit index of each parent GridView in the session and using them when needed to find a particular row containing the child GridView. Lastly, always place a SqlDataSource control besides a GridView otherwise a child GridView will no longer be able to access it.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

Hi Mahr,
I would like to thank you for writing a very detailed article. I used your source code and extended it to serach for records between a given daterange and then view master detail records. I am only using the grid upto 2 levels. A couple changes in my application: I am using ajax calendar control for the dates. Also, I am handling the datasource binding for the parent grid in the code behind. I had to change the buttons in the grid to link buttons as they were throwing an error on invalid postback while trying to go to the detailed level which could have resolved using EnableEventValidation="false" but I didn't want to do that. I had to add a event handler for RowCancelingEdit and it works great.

Hi,
My requirement is i have multiple level of hierarchy
columns to be displayed in grid are Col_Name,Dyn1,Dyn2,.....Dyn n
Dyn1,Dyn2,....Dyn n are dynamic columns
in 1 datatable i will have the entire hierarchy
and pls find the data displayed in terms of levels
i need to bind this to grid is it possible?
if so pls send me the sample code
First level Hierarchy

I am running visual studio 2005 Professional editon.
I am adding column to gridview at runtime.
i am used PageIndex. The problem is when i am changing oone page another page column get adding. on first changing one column, on second changing one more gets added so i get two column with same name,

Nice article,
now i am working in a project where i want calculate the attendance by daily report, monthly report and individual report.

if i am adding the attendance for staff on daily it takes new row so the repeat of id is there, and when i am storing a month detail in row its becamw default to calculate the overall present days. is there any to calculate the present days in a single row