Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / web / ASP.NET

Nested and Grouped Repeaters

4.00/5 (4 votes)
15 Aug 20052 min read 1   974  
Create a data-driven report web page with nested ASP.NET repeaters.

Image 1

Introduction

Using nested ASP.NET Repeaters, you can easily create professional report style web pages, that include standard ASP.NET controls. Other articles on CodeProject show how you can do this with a UserControl. This example, just uses the standard Repeater Control, and in particular the ItemDataBound method to create a sophisticated interface.

Background

I needed to create a report, but the users need to be able to update it via the same interface, so SQL Reporting Services, and Crystal Reports were unsuitable. I had to do a fair amount of research to get round some problem; like DataBinding syntax, techniques to locate the nested control, so I decided to share the results. Thanks to people who posted answers to posts in the excellent MSDN Forum.

Setup

If you wish to run the sample, carry out the following steps:

  1. The connection string needs to be modified to connect to your database instance.
  2. I have included an SQL script that creates the report stored procedure. This needs to be run against the Northwind database.

Data

The stored procedure returns two resultsets from the Northwind database.

  1. Product categories
  2. Products and total sales

A DataSet is populated in the standard way.

Code

ASP.NET
<asp:Repeater id="rptProducts" runat="server" 
    OnItemDataBound="rptProducts_ItemDataBound">

For the child repeater, you need to specify the OnItemDataBound method to call in the ASPX file. If you just wire the event in the normal way, it does not get called.

C#
1:  protected void rptProducts_ItemDataBound(object sender, 
            System.Web.UI.WebControls.RepeaterItemEventArgs e)
2:  {
3:    if(e.Item.ItemType == ListItemType.Item || 
        e.Item.ItemType == ListItemType.AlternatingItem)
4:    { 
5:      string product =  
          ((DataRowView)e.Item.DataItem).Row.ItemArray[2].ToString(); 
6:      ImageButton i =  
          (ImageButton)e.Item.Controls[0].FindControl("imgDelete"); 
7:      i.CommandName = "Delete"; 
8:      i.CommandArgument = product; 
9:      string script = string.Format(
              "javascript:alert('Delete Product: {0}');", product); 
10:     i.Attributes.Add("onclick", script); 
11:   }
12: }

When rptProducts_ItemDataBound method is called, we can locate product data by casting the RepeaterItem into a DataRowView. [5] Here, we retrieve the product name, which we use to pass into the CommandArgument of the ImageButton[6-8]. This CommandArgument can be pulled out in the ImageButton_Command event, and then used to delete or modify the relevant product in the database. This method [1] must be protected, and not private, otherwise it will be inaccessible.

C#
1:  private void rptCategories_ItemDataBound(object sender, 
            System.Web.UI.WebControls.RepeaterItemEventArgs e)
2:  {
3:    if(e.Item.ItemType == ListItemType.Item || 
            e.Item.ItemType == ListItemType.AlternatingItem)
4:    {
5:       // find category in RepeaterItem
6:       string category = 
           ((DataRowView)e.Item.DataItem).Row.ItemArray[0].ToString();
7:       Repeater rep = (Repeater)e.Item.FindControl("rptProducts") ;
8:       // pass in category to construct "where clause", 
         // then set DataView as DataSource 
9:       DataView dv = getChildRows(category) ; 
10:      rep.DataSource = dv ; 
11:      rep.DataBind() ; 
12:      // calculate total for product category 
13:      SalesTotal = this.calculateSalesTotals(category) ; 
14:      // Find item in group "footer" and populate label with total 
15:      Repeater r = (Repeater)e.Item.Parent ; 
16:      Label lblSalesTotals = 
                 (Label)r.Controls[e.Item.ItemIndex+ 1].FindControl(
                                                     "lblSalesTotals"); 
17:      lblSalesTotals.Text =  SalesTotal.ToString("C") ; 
18:   }
19: }

The parent repeater ItemDataBound method is responsible for filtering the category data. This is the key piece of code on the page, that performs the nesting. FindControl can return a reference to the child Repeater [7]. A DataView control can then be passed to the child Repeater as the DataSource[9-11]. Finally, we calculate the Sales Totals for the category, for the Group Footer [15-17].

C#
1: private DataView getChildRows(string Filter)
2: {
3:   string whereClause = 
         string.Format("CategoryName = '{0}'", Filter) ; 
4:   DataView dv = new DataView(ds.Tables["Products"], 
                           whereClause, "", 
                           DataViewRowState.CurrentRows) ; 
5:   return dv ; 
6: }

A DataView object is returned with the products that belong to a specified category. The DataView constructor used takes the DataTable, an SQL like where clause, a sort expression, and DataViewRowState[4]. No DataRelations are needed.

Debugging

I found it really helpful to set breakpoints on the ItemDataBound methods and take advantage of the Locals, Watch and Command windows in Visual Studio .NET to locate control/objects. This helps you to locate the controls and objects that you want to manipulate.

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.

A list of licenses authors might use can be found here