Complex Data Binding with the Accordion Control
page 3 of 6
by Brian Mains
Average Rating: 
Views (Total / Last 10 Days): 43089/ 75

Parent/Child Data

How can we show parent/child data, where the header template represents a parent object and the content panel represents child data related to that parent? To illustrate, I am going to use the following database structure.

Figure 1

For the accordion, the header for each accordion pane will be the customer name. Each customer has zero or more orders displayed in the content template and these orders belong to the customer. These orders will be displayed below the customer in a GridView list, using the template below.

Listing 2

<ajax:Accordion ID="accComplexData" runat="server">
<asp:Label ID="lblLastName" runat="server" Text='<%# Eval("LastName") %>' />,
<asp:Label ID="lblFirstName" runat="server" Text='<%# Eval("FirstName") %>' />
Joined on:
<asp:Label ID="lblJoinedDate" runat="server" Text='<%# Eval("CreatedDate") %>' />
<br /><br />
<asp:GridView ID="gvwOrders" runat="server" 
 DataSource='<%# Eval("Orders") %>' DataKeyNames="OrderKey">
<asp:BoundField HeaderText="Total Amount" DataField="TotalAmount" />
<asp:BoundField HeaderText="Reference" DataField="ReferenceNumber" />
<asp:BoundField HeaderText="Order Date" DataField="OrderDate" 
 DataFormatString="{0:MM/dd/yyyy}" />
<EmptyDataTemplate>No orders have been submitted.</EmptyDataTemplate>

When the accordion binds, a new accordion pane represents the Customer record. The Customer object has an Orders property related to that customer, and is the source for the GridView control. However, the content pane can still contain information about the customer because both the header and content template are bound to the Customer object.

If you try to change the data source, such as to refresh the grid, it may be better to handle binding the grid in the Accordion's ItemDataBound event handler. This event fires once for each bound item in the accordion. To access the controls in the row, use the following approach.

Listing 3

void accComplexData_ItemDataBound(object sender, 
AjaxControlToolkit.AccordionItemEventArgs e)
   if (e.ItemType != AjaxControlToolkit.AccordionItemType.Content)
   Customer customer = e.Item as Customer;
   if (customer == nullreturn;
   GridView grid = e.AccordionItem.FindControl("gvwOrders"as GridView;
   if (grid == nullreturn;
   grid.DataSource = customer.Orders;

This approach retrieves a reference to the GridView control via FindControl. Once found, the reference to the Customer object for that accordion pane is accessible through the Item property.  The Customer object can then have its Orders collection bound to the grid.

The reason this may be better is because if you try to rebind the grid later individually, you may receive an error when trying to bind to a different result set manually. I tried leaving both the DataSource Eval expression in the grid, as well as the code that binds in code-behind, and the following result is an error:  "Databinding methods such as Eval(), XPath(), and Bind() can only be used in the context of a databound control."

How could the underlying grid be refreshed in cases of updates occurring elsewhere?  There are two options to refresh the grid; the first option is to rebind the accordion each time. Let us look at a few approaches. The first approach is to rebind the entire accordion. For this to work, I have added a refresh button that simply rebinds the grid with all of the new customer information.

Listing 4

private void BindAccordions()
  CustomerBAL bal = new CustomerBAL(this.DataContext);
  this.accComplexData.DataSource = bal.GetAll();

As another approach, because each accordion item contains a grid, it is possible to rebind data to a single grid. To do that, some information about the key for the customer for the current record needs stored in that row. What this means is that with each grid there needs to be some way to track the key for the customer that accordion item represents. I usually use a hidden field.

Listing 5

<asp:HiddenField ID="hdnCustomerKey" runat="server" 
    Value='<%# Eval("CustomerKey") %>' />

An alternative approach is to define the CommandArgument property of the button.

Listing 6

<asp:Button ID="btnRefresh" runat="server" Text="Refresh" CommandName="Refresh" 
CommandArgument='<%# Eval("CustomerKey") %>' />

I will use the last approach in the code below; however, both approaches keep the customer key with the grid. When the refresh button for each grid is clicked, the key is retrieved from the command argument property through the button reference. Using the key, the Customer record is queried from the database using the key and the Orders are bound to the grid's reference, also retrieved using the FindControl method.

Listing 7

void accComplexData_ItemCommand(object sender, CommandEventArgs e)
  if (e.CommandName != "Refresh")
  GridView grid = (GridView)this.accComplexData
  CustomerBAL bal = new CustomerBAL(this.DataContext);
  Customer customer = bal.GetByKey((Guid)e.CommandArgument);
  this.BindGrid(grid, customer);

The actual binding of the grid is simple; I always pull binding information out of the event handler code and use a separate method as defined below.

Listing 8

private void BindGrid(GridView grid, Customer customer)
  grid.DataSource = customer.Orders;

However, this could use some refactoring. Rather than retrieving the customer reference in the event handler, it would be better to pass in the GUID instead. This moves the data access calls into one place, instead of repeating the same code separately. This has been refactored as follows.

Listing 9

private void BindGrid(GridView grid, Guid customerKey)
  CustomerBAL bal = new CustomerBAL(this.DataContext);
  Customer customer = bal.GetByKey(customerKey);
  if (customer == null)
  throw new NullReferenceException(string.Format("The customer for {0} is null",
  grid.DataSource = customer.Orders;

This method can be called with: this.BindGrid(grid, (Guid)e.CommandArgument); .

This is a little cleaner and the BindGrid method can be used in multiple situations by passing in the GUID, and the data access code is all in the same place. I think this is a better approach to take and we will see that it turns out useful in the following section.

View Entire Article

User Comments

Title: Need Code   
Name: Dorababu
Date: 2011-11-20 12:55:11 PM
Hi i am unable to view the article can any one post me the code used in this please
Title: I don't have any   
Name: Seyed
Date: 2009-12-08 7:45:02 PM
Big time saver. Thanks for this great posting
Title: Thanks   
Name: Lee
Date: 2009-11-23 7:59:39 AM
It is a great article. will be helpful if u can provide me the all related code + screenshots
Title: Good Article......   
Name: WebEye
Date: 2008-08-30 5:51:42 AM
Very good me a lot.
You can find very much same article here...

And running example of this article here...
Title: Nice Article   
Name: Imran Ahmad Mughal
Date: 2008-07-08 1:09:50 AM
It's really a nice article. Can you please provide me the sample project in which you have implemented this?

Title: Thanks   
Name: Subalakshmi
Date: 2008-07-03 6:39:09 AM
A very Good article,to implement in my website.
Title: Thanks   
Name: Prashanth
Date: 2008-07-03 5:16:01 AM
Great article, one small suggestion, if you have a sample application to download and some screen shots of the page it would be very helpful.
Title: Thank You :)   
Name: Qureshi
Date: 2008-07-01 9:20:20 AM

i realy want to say thanks

bcoz i really want a article on this

thaks a tan

Community Advice: ASP | SQL | XML | Regular Expressions | Windows

©Copyright 1998-2023  |  Page Processed at 2023-09-21 9:55:34 PM  AspAlliance Recent Articles RSS Feed
About ASPAlliance | Newsgroups | Advertise | Authors | Email Lists | Feedback | Link To Us | Privacy | Search