Dynamic drill-down DataGrid

The DataGrid is a good way to display the hierarchical nature of data. This tip demonstrates the use of the DataGrid in ASP.NET applications.

This tip demonstrates how to "hack" the DataGrid. By hack in this context I mean to use it in (what I'm guessing was) an unintended way.

Suppose you have two related DataTables, and you want to demonstrate the hierarchical nature of the data. You want it to work the way the DataGrid in WinForms and Microsoft Access works, where a tree-like plus and minus symbol allow you to see all the related records for a given parent record. I'll show how to accomplish this in ASP.NET.

See Figure 1.

I'll show how to use an XML file in this capacity, populating it into a DataSet for easy data binding. In the next tip, I'll show how to dynamically pull records from a data source based on the user's selection, so you don't have to fetch all the related records to all the parent rows at the same time (which could be a performance nightmare).

I need to acknowledge my friend David Findley for turning me on to this technique. Admittedly, he is the real genius behind this tip. Also, I've created a complete 16-minute screen-cam video tutorial, complete with commentary for this tip available in the Supporter's area at www.LearnVisualStudio.NET.

I encourage you to download the source code for this tip (written in Visual Basic .NET) to examine it. I'll refer to it in a narrative format, so you'll need it close by.

The MusicCollection.xml has a list of David's favorite albums in the following style:

<MusicCollection>
 <album title="Dark Side Of The Moon" artist="Pink Floyd" year="1973">
  <track title="Speak To Me" length="4:00"/>
  <track title="On The Run" length="3:33"/>
  <track title="Time" length="7:06"/>
.
.
      </album>

To get the XML file into a DataSet, the Global.asax file contains the following code:

    Sub Session_Start(ByVal sender As Object, ByVal e As EventArgs)
        ' Fires when the session is started
        Dim ds As New DataSet()
        ds.ReadXml(Server.MapPath("MusicCollection.xml"))
        Session("MusicCollection") = ds
    End Sub

You'll notice that there is no schema in this project. The schema is inferred in this example.

Now that the DataSet is populated and loaded in the Session object, we can turn our minds to the meat of the discussion. WebForm1.aspx has a DataGrid with 5 columns. The middle columns will bind to the album information like artist, title and year. The first column (a template column) will contain our + and –graphics for the given album row. The last column (also a template column) is defined in a rather odd way:

<asp:TemplateColumn>
 <ItemStyle Width="1" />
 <ItemTemplate>
 <asp:PlaceHolder ID="ExpandedContent" Visible="False" Runat="server">
   </td></tr>
  <tr>
   <td width="9"> </td>
   <td colspan="3">
.
.
.

The column contains a PlaceHolder object called ExpandedContent that basically is hidden (Visible="False") until someone clicks the little + icon, at which time the event defined in the code behind for this web form is fired. (I'll talk about that in a moment.)

What is displayed is some HTML table cell and row tags that effectively intercept the current output that will be generated by the DataGrid with our own special implementation, namely we are telling the table (when its drawn) to close the current cell, the current row, and now add a new row with one blank column (for spacing) and another column (spanning three columns) that we'll use to display a second DataGrid.

<asp:DataGrid id="Datagrid2" DataSource='<%# 
Container.DataItem.CreateChildView("album_track") %>' runat="server" 
BorderColor="#E7E7FF" BorderStyle="None" BorderWidth="1px" BackColor="White" 
CellPadding="3" GridLines="Horizontal" Width="503px" 
AutoGenerateColumns="False">
.
.
.
 <Columns>
  <asp:BoundColumn DataField="title" HeaderText="Title" />
  <asp:BoundColumn DataField="length" HeaderText="Length" />
 </Columns>
</asp:DataGrid>

This DataGrid will contain the columns bound to the title and length nodes of the XML file for each track. Notice that the DataGrid's DataSource is set to the Container.DataItem.CreateChildView("album_track"). I discussed the CreateChildView method in previous tips, but let me remind you that the name of the relation that is defined here ("album_track") must match the two names of the related DataTables in your DataSet or else this will NOT work!

You might be wondering how much effort is involved in the code behind to accomplish the hiding and displaying on the secondary grid, but surprisingly there is very little code.

    Private Sub DataGrid1_ItemCommand(ByVal source As Object, ByVal e As
 System.Web.UI.WebControls.DataGridCommandEventArgs) Handles 
DataGrid1.ItemCommand
        Select Case e.CommandName
            Case "Expand"
                Dim ExpandedContent As PlaceHolder = e.Item.Cells
(DataGrid1.Columns.Count - 1).FindControl("ExpandedContent")
                ExpandedContent.Visible = Not ExpandedContent.Visible


                Dim btnExpand As ImageButton = e.Item.Cells(0).FindControl("btnExpand")
                If btnExpand.ImageUrl = "˜/Images/Plus.gif" Then
                    btnExpand.ImageUrl = "˜/Images/Minus.gif"
                Else
                    btnExpand.ImageUrl = "˜/Images/Plus.gif"
                End If
        End Select
    End Sub

The ItemCommand event for the first grid is employed. We evaluate the e.CommandName to determine whether we are handling the correct command from the grid. But wait. Where is the CommandName set? Actually, we set as we defined the ImageButton that contains the + and –images in the first column:

<asp:ImageButton ImageUrl="˜/Images/Plus.gif" CommandName="Expand" 
ID="btnExpand" Runat="server">

The DataGrid passes this CommandName through to the code behind event handler and allows us to do whatever we wish with the information that our ImageButton was clicked. In this case, we do a quick check to see whether we are currently in "expanded" mode or "closed" mode, and perform the opposite action. In other words, if we are currently "expanded", then make is "closed". Expanding and closing involves setting the visibility of the appropriate PlaceHolder object ("ExpandedContent") to True or False.

As I alluded to earlier in this tip, next time I'll demonstrate how to take this a step further by dynamically adding data into the child DataTable as the user requests it. I'll also make sure to not have to grab information again, in case the user wants to expand the child grid again.


About the Author

Robert Tabor is a Microsoft Certified Professional in Visual Basic with over six years of experience developing n-tier Microsoft-centric applications for some of the world's most prestigious companies and consulting organizations, such as Ernst & Young, KPMG, Cambridge Technology Partners, Sprint, American Heart Association, and the Mary Kay Corporation. Bob is the author of Microsoft .NET XML Web services by Sams Publishing, and contributes to SoapWebservices.com and LearnVisualStudio.NET. He is currently working on initiatives within Mary Kay, the second largest eCommerce site in retail volume on the net, of how to utilize .NET within their e-business group.


This was first published in June 2002

Dig deeper on ASP.NET development best practices

0 comments

Oldest 

Forgot Password?

No problem! Submit your e-mail address below. We'll send you an email containing your password.

Your password has been sent to:

-ADS BY GOOGLE

SearchCloudComputing

SearchSoftwareQuality

SearchSOA

TheServerSide

SearchCloudApplications

Close