You are on page 1of 9

HierarGrid: A Hierarchical DataGrid that Displays Master-Detail Relationships: ASP Alli...

Page 1 of 9

ASPAlliance: Articles, reviews, and samples for .NET


Developers
URL:
http://aspalliance.com/articleViewer.aspx?aId=21&pId=-1

HierarGrid: A Hierarchical DataGrid that


Displays Master-Detail Relationships
by Denis Bauer page
Feedback

Average Rating:
Views (Total / Last 10 Days): 206744/ 767

Using the HierarGrid - Part I


1 What is the HierarGrid

The ASP.NET DataGrid is a popular control for displaying tabular data, with editing,
paging and sorting capabilities. However, this is only suitable for displaying single
DataTables - there is no support for master-detail views using parent-child relations.

I have created a custom control called HierarGrid (that derives from the standard
ASP.NET DataGrid) and a custom DataGridColumn called HierarColumn.

The HierarGrid takes a DataSet containing relations between tables as its DataSource.

While iterating over the parent table, it checks the related tables for child rows, and if
one is found it dynamically loads a template for the child row(s).

The template is rendered invisibly into the custom HierarColumn and when the user
clicks the plus icon, the template's content is copied via JavaScript into a newly created
TableRow.

Sample illustration (Fig. 1):

http://aspalliance.com/articleViewer.aspx?aId=21&pId=-1 11/26/2009
HierarGrid: A Hierarchical DataGrid that Displays Master-Detail Relationships: ASP Alli... Page 2 of 9

2 Where can I download the HierarGrid

I have published the HierarGrid, including the source code, on my homepage


http://www.denisbauer.com/ASPNETControls.aspx

Please also note the demo that I created, which can be downloaded from the same
location. The following explanations are mainly based on, and refer to this demo.

The HierarGrid can be used and distributed at no charge in non-commercial and


commercial projects. However I am happy to hear about any feedback, suggestions,
improvements, criticisms, etc.

3 How to use the HierarGrid

3.1 Loading the data into a DataSet

The .NET Framework offers multiple ways to load data into a DataSet. One possible
way is to use a SQLCommand to run a SQL query against a table in MS SQL Server. In
my example, I used the "Pubs" database, a sample Database of Microsoft SQL Server. I
created three SELECT statements that returned a list of publications, the associated
authors and the sales in separate tables. The resulting DataSet can be written to a XML
file using the method ds.WriteXml. The XML file is contained in the sample application
as Testdata.XML, which can be downloaded from the link above.

To load the sample data yourself add two Views and a stored procedure into the "Pubs"
database with the following three SQL statements:

CREATE VIEW dbo.VwAuthors


AS
SELECT dbo.authors.*, dbo.titleauthor.title_id
FROM dbo.titleauthor INNER JOIN
dbo.authors ON dbo.titleauthor.au_id = dbo.authors.au_id
ORDER BY dbo.titleauthor.title_id

CREATE VIEW dbo.VwSales


AS
SELECT dbo.sales.title_id, dbo.stores.stor_name,
dbo.stores.stor_address, dbo.stores.city, dbo.stores.state,
SUM(dbo.sales.qty) AS qty, dbo.stores.zip
FROM dbo.sales INNER JOIN
dbo.stores ON dbo.sales.stor_id = dbo.stores.stor_id

GROUP BY dbo.sales.title_id, dbo.stores.stor_name,


dbo stores stor address dbo stores cit dbo stores state

http://aspalliance.com/articleViewer.aspx?aId=21&pId=-1 11/26/2009
HierarGrid: A Hierarchical DataGrid that Displays Master-Detail Relationships: ASP Alli... Page 3 of 9

dbo.stores.stor_address, dbo.stores.city, dbo.stores.state,


dbo.stores.zip
ORDER BY dbo.sales.title_id

CREATE PROCEDURE dbo.TestData AS


SELECT title_id as Title_ID, title as Title, type as Type,
price as Price, notes as Notes,
CAST(pubdate as varchar) as Publication
FROM Titles

SELECT *
FROM VwAuthors
WHERE exists (Select * from Titles where au_id = VwAuthors.au_id)
ORDER BY title_id

SELECT *
FROM VwSales
WHERE exists (Select * from Titles where title_id = VwSales.title_id)

To load the data into a DataSet create a new WebApplication and add the following
code into the Page_Load event:

[VB]
Dim sqlConnection As SqlConnection = New SqlConnection()
Dim sqlCommand As SqlCommand = New SqlCommand()

sqlConnection.ConnectionString =
"data source=(local);initial catalog=pubs;integrated security=SSPI;"
sqlCommand.CommandText = "[TestData]"
sqlCommand.CommandType = System.Data.CommandType.StoredProcedure
sqlCommand.Connection = sqlConnection
Dim ds As DataSet = New DataSet()

sqlConnection.Open()
Dim adap As SqlDataAdapter = New SqlDataAdapter(sqlCommand)

adap.Fill(ds)
sqlConnection.Close()

ds.Tables(0).TableName = "Titles"
ds.Tables(1).TableName = "Authors"
ds.Tables(2).TableName = "Sales"

[C#]
SqlConnection sqlConnection = new SqlConnection();
SqlCommand sqlCommand = new SqlCommand();

sqlConnection.ConnectionString =
"data source=(local);initial catalog=pubs;integrated security=SSPI;";
sqlCommand.CommandText = "[TestData]";
sqlCommand.CommandType = System.Data.CommandType.StoredProcedure;
sqlCommand.Connection = sqlConnection;
DataSet ds = new DataSet();

sqlConnection.Open();
SqlDataAdapter adap = new SqlDataAdapter(sqlCommand);

adap.Fill(ds);
sqlConnection.Close();

ds.Tables[0].TableName = "Titles";
ds.Tables[1].TableName = "Authors";
ds.Tables[2].TableName = "Sales";

3.2 Binding the Data to a standard DataGrid

Alternatively to the steps mentioned in the section above, you can download the

http://aspalliance.com/articleViewer.aspx?aId=21&pId=-1 11/26/2009
HierarGrid: A Hierarchical DataGrid that Displays Master-Detail Relationships: ASP Alli... Page 4 of 9

sample project and use the Testdata.XML file. This file can be loaded into a DataSet
and bound to a standard DataGrid with only a few lines of code.

If you had not already done in step 3.1 create a new WebApplication and drop a new
DataGrid from the toolbar onto the webform and call it DG1. Otherwise skip the next
few lines and continue with binding your retrieved DataSet to the DataGrid.

The following code shall be placed in the Page_Load event.

The first step is to load the data from the sample file into a DataSet or to use the
DataSet generated with the code above:

[VB]
Dim ds as DataSet = new DataSet()
ds.ReadXml(AppDomain.CurrentDomain.BaseDirectory + "\\Testdata.xml")

[C#]
DataSet ds = new DataSet();
ds.ReadXml(AppDomain.CurrentDomain.BaseDirectory + @"\Testdata.xml");

Then the DataSet can be bound to a standard DataGrid that has been dropped on a
webform:

[VB]
DG1.DataSource = ds
DG1.DataMember = "Titles"
DG1.DataBind()

[C#]
DG1.DataSource = ds;
DG1.DataMember = "Titles";
DG1.DataBind();

The procedure so far is the normal way to bind relational data from a DataSet to an
ASP.NET DataGrid and can be looked up in the .NET Framework documentation and
Quickstart tutorials. Following are the changes described the HierarGrid requires to
display parent-child relations.

3.3 Switching to the HierarGrid

One goal when developing the HierarGrid was to enable developers to switch
seamlessly between the standard DataGrid view without parent-child relations and the
detailed view with the HierarGrid.

First, reference the HierarGrid.dll in your VS.NET solution by right-clicking on


References, choosing Add and selecting the DLL.

Second, add the following declaration to the top of the ASPX file:

<%@ Register TagPrefix="DBWC" Namespace="DBauer.Web.UI.WebControls"


Assembly="DBauer.Web.UI.WebControls.HierarGrid" %>

Now, you just have to change the declaration in the ASPX file from

<asp:DataGrid id="DG1" runat="server">

to

http://aspalliance.com/articleViewer.aspx?aId=21&pId=-1 11/26/2009
HierarGrid: A Hierarchical DataGrid that Displays Master-Detail Relationships: ASP Alli... Page 5 of 9

<dbwc:HierarGrid id="HG1" runat="server">

Furthermore please keep in mind to change the code-behind file as well:

[VB]
Protected WithEvents HG1 As DBauer.Web.UI.WebControls.HierarGrid

[C#]
protected DBauer.Web.UI.WebControls.HierarGrid HG1;

Restarting your application should show that nothing has changed so far. The
HierarGrid behaves just like the normal DataGrid does.
Using the HierarGrid - Part II
3.4 Defining relations in the DataSet

DataSets offer the ability of adding relations between the different tables contained
within it. The HierarGrid uses these relations to get from the parent table to its child
data. Note that you have to define the relations prior to binding the DataSet to the
HierarGrid.

The relations can be defined be specifying the key columns in both tables:

[VB]
Dim dc1 As DataColumn
Dim dc2 As DataColumn
'Relation Title => Author on Title_ID column
dc1 = ds.Tables(0).Columns("title_id")
dc2 = ds.Tables(1).Columns("title_id")
Dim dr As DataRelation = New DataRelation("Title_Author", dc1, dc2, False)
ds.Relations.Add(dr)

'Relation Title => Sales


dc1 = ds.Tables(0).Columns("title_id")
dc2 = ds.Tables(2).Columns("title_id")
dr = New DataRelation("Title_Sales", dc1, dc2, False)
ds.Relations.Add(dr)

[C#]
DataColumn dc1;
DataColumn dc2;
//Relation Title => Author on Title_ID column
dc1 = ds.Tables[0].Columns["title_id"];
dc2 = ds.Tables[1].Columns["title_id"];
DataRelation dr = new DataRelation("Title_Author", dc1, dc2, false);
ds.Relations.Add(dr);

//Relation Title => Sales


dc1 = ds.Tables[0].Columns["title_id"];
dc2 = ds.Tables[2].Columns["title_id"];
dr = new DataRelation("Title_Sales", dc1, dc2, false);
ds.Relations.Add(dr);

3.5 Adding the HierarColumn

The HierarGrid automatically adds a new column called HierarColumn to the grid at
runtime if none is specified at design time. This column contains the image of the
plus/minus icon and the content of the child templates invisibly.

This row can be created at design time similarly to how we define other BoundColumns

or TemplateColumns without any further properties.

http://aspalliance.com/articleViewer.aspx?aId=21&pId=-1 11/26/2009
HierarGrid: A Hierarchical DataGrid that Displays Master-Detail Relationships: ASP Alli... Page 6 of 9

3.6 Creating a child template

Now comes the part of visually designing a template that is loaded to display the child
row information. Please create a new UserControl by right-clicking the project and
selection Add | Add new Item | Web User Control and give it the same name as the
first child table (in my example it is called Authors.ascx and stored in the folder
Templates underneath the project directory).

Drop a TextBox onto the form and choose the DataBindings property from the Property
viewer. Select "Text" as the bindable property and choose to enter a custom
databinding expression. Enter the following code:

[VB]
DataBinder.Eval(CType(Container, DataGridItem).DataItem, "au_lname")

[C#]
DataBinder.Eval(((DataGridItem)Container).DataItem, "au_lname")

This expression corresponds to the normal databinding syntax for one exception: The
container object has to be cast to the type DataGridItem before the DataItem property
can be accessed.

For each additional child table a new UserControl can be created.

3.7 Using the TemplateSelection event

Let’s take a look at what exactly happens internally when the DataBind method on the
HierarGrid is called: As in the standard DataGrid, it is looped over the first table of the
DataSource (or the one given in the DataMember attribute as shown in the sample
code above). For each row in the table an object of the type DataGridItem is added to
the Items collection of the DataGrid. When the DataGrid is rendered and sent to the
client browser later in the page cycle (for further reference I recommend the Article
Page cycle by Paul Wilson) each DataGridItem is converted into the HTML tag <TR>.
This mechanism is extended in the HierarGrid so that after rendering one row of the
parent table, the related tables are checked for child rows. If any child rows exist, their
corresponding template is loaded and inserted into HierarGridColumn which is itself a
child object of the DataGridItem.

To illustrate this, let’s take a look at the sample picture above (Fig. 1). In the standard
DataGrid, separate rows are generated for the books with the IDs BU1032, BU1111
and BU2075. That is exactly the same with the HierarGrid except that after generating
the row for book ID BU1032 the child tables Authors and Sales are checked for
corresponding child rows. When we take a look at the book BU1111 we can see that
two child rows in the Authors table and one entry in the Sales table were found that
correspond to that book.

Now, to specify which UserControl should be loaded for each child row (the two in the
Author table and the one in the Sales table), I decided to use a special event called
TemplateSelection. This event is raised for each row in every child table. In the
example mentioned above, this is three times for the book BU1111- and of course
several more times for the other books as well.

In your sample main ASPX page, create an event handler as shown below.

http://aspalliance.com/articleViewer.aspx?aId=21&pId=-1 11/26/2009
HierarGrid: A Hierarchical DataGrid that Displays Master-Detail Relationships: ASP Alli... Page 7 of 9

[VB]
Private Sub HG1_TemplateSelection(ByVal sender As Object,
ByVal e As DBauer.Web.UI.WebControls.HierarGridTemplateSelectionEventArgs)
Handles HG1.TemplateSelection
e.TemplateFilename = "Templates\\" + e.Row.Table.TableName + ".ascx"
End Sub

[C#]
private void HG1_TemplateSelection(object sender,
HierarGridTemplateSelectionEventArgs e)
{
e.TemplateFilename = "Templates\\" + e.Row.Table.TableName + ".ascx";
}

In C# please add the following line to the InitializeComponent method to wire up the
new event handler (note that this method is inside the region “Web Form Designer
generated code”):

[C#]
this.HG1.TemplateSelection +=
new HierarGridTemplateSelectionEventHandler(this.HG1_TemplateSelection);

The TemplateSelection event has a parameter "e" which is of type


HierarGridTemplateSelectionEventArgs. This type contains two properties that are
important to understand.

The Row property contains the child row for which the event has been raised. Returning
to the example, when the event is raised for the first time, the property contains
exactly one row from the author table with data about Michael O’Leary.

Based on this data, the developer can decide which template to load. In this case the
filename would be based on the name of the table i.e. "Templates\Authors.ascx".

This filename is sent back to the HierarGrid with the TemplateFilename property. This
property initially doesn’t contain a value, and is reserved as the return value – it’s a
workaround as event handlers do not support return values. The HierarGrid expects to
find the filename of the template in this property which is then loaded internally using
Page.LoadTemplate or Page.LoadControl (fur further reference please check the MSDN
documentation).

This construct allows you to decide, based on any criteria, on the child row which the
template should load. This can either be based on the table name as in the example, or
could also be based on a value in a column or some other logical expression.

3.8 Changing the TemplateCachingBase property

Calling the TemplateSelection event for every single row of each child table would
cause quite a performance hit. Therefore I decided to implement a caching option
called TemplateCachingBase.

This enum property can either be configured to cache based on the tablename of the
childrows. In the example above, the TemplateSelection event would be called once for
the Authors table and once for the Sales table. The second option is to cache based on

the value of a column which has to be specified in the TemplateCachingColumn

http://aspalliance.com/articleViewer.aspx?aId=21&pId=-1 11/26/2009
HierarGrid: A Hierarchical DataGrid that Displays Master-Detail Relationships: ASP Alli... Page 8 of 9

property. This could be helpful if the subitems are not able to be distinguished by the
table name but by a special column in the tables. For example if the Sales table
contained a country column and for each country an individual template should be
selected, the country column can be specified as the CachingColumn.The third option is
to disable caching at all.

3.9 Displaying one template for all child rows

The sample described above relies on displaying a template for each child row. E.g.
when a book has two authors and has been sold three times the total number of
templates loaded is five. Imagine you want to change this to display a nested DataGrid
for all the authors and a DataGrid for all the sales under book.

This can be achieved by setting the TemplateDataMode to "Table" instead of


"SingleRow" (its default value). Doing so loads only one template per child table.

You could change the Authors.ASCX file described above by dropping a DataGrid
instead of a TextBox. In the code-behind you can then use the normal DataBinding
syntax to bind a DataSet to the grid. This DataSet of child rows can be taken from the
BindingContainer property:

[VB]
Dim dgi as DataGridItem = CType(Me.BindingContainer, DataGridItem)
Dim ds as DataSet = CType(dgi.DataItem, DataSet)
DG1.DataSource = ds
DG1.DataMember = "Authors"
DG1.DataBind()

[C#]
DataSet ds = (DataSet) ((DataGridItem) this.BindingContainer).DataItem;
DG1.DataSource = ds;
DG1.DataMember = "Authors";
DG1.DataBind();

3.10 LoadTemplate vs LoadControl

I noticed slight differences with event processing between loading the template using
Page.LoadTemplate and Page.LoadControl. This is why I decided to include a property
called LoadControlMode to enable both possibilities. So, when experiencing problems
with the one method try switching to the other.

4 Conclusion

This tutorial explains how to use the HierarGrid to display parent-child relations in a
DataGrid-like control. It is important to remember to bind the data just like you would
bind it to a standard datagrid and then use the TemplateSelection event to specify
which template to load for which child row.

Additionally there are some properties in place to allow easy customization for the most
common usage scenarios.

I hope this control will be a useful tool in one of your future web applications developed
with ASP.NET, and that it will save you some time writing code.

If you have any feedback regarding the HierarGrid or this article, I’ll be happy if you let

http://aspalliance.com/articleViewer.aspx?aId=21&pId=-1 11/26/2009
HierarGrid: A Hierarchical DataGrid that Displays Master-Detail Relationships: ASP Alli... Page 9 of 9

me know. My contact information is available at http://www.denisbauer.com. Updated


information about the HierarGrid can also be found in my Weblog.

I want to thank Paolo Pignatelli for helping me write this article.

About the Author

Denis Bauer is a Software Design Engineer at Microsoft Germany’s


Consulting Services where he is working on large-scale projects
using leading-edge technology. Denis specializes on web
application development with ASP.NET and the .NET Framework.
Before joining Microsoft in 2001, he held leading positions in two
new economy startup companies.

Product Spotlight
Kindle 2: New Wireless Reading Device
Lighter than a paperback, but holds over 1500 books. 3G wireless - no fees or
service plans. Improved display reads like real paper. Learn more

My SQL Problems?
Find bad queries and tables. Improve
performance. Free download.
www.jetprofiler.com

Exceptioneer
A new service to handle .NET exceptions
www.exceptioneer.com

COBOL Modernization
Maintain and modernize COBOL while
reducting TCO
veryant.com

©Copyright 1998-2009 ASPAlliance.com | Page Processed at 11/25/2009 10:37:23 PM


About ASPAlliance | Newsgroups | Advertise | Authors | Email Lists | Feedback | Link To Us | Privacy |
Search

http://aspalliance.com/articleViewer.aspx?aId=21&pId=-1 11/26/2009

You might also like