Saturday, November 08, 2008 #

Creating an ItemDataBound Event Delegate for a Nested Repeater

Technorati Tags: ,,

Today I needed to create a nested grid using repeaters.  Pretty common but always a pain in the ass with all Microsoft controls.  I needed to nest my repeaters just like you’d nest any other control like the DataGrid or GridView.  The issue came up for me when I wanted to create a delegate for my nested repeater (repeater inside my parent).  Since a nested control within a control (the nested repeater in this case) is not available at the page level, Intellisense or the compiler will not know that it exists.  Because it’s nested inside a parent, it becomes pat of that parent’s collection of objects. 

In order to access the nested object inside the parent collection, you simply create an instance of that control type (in this case I want to reference the nested repeater), and then do an e.Item.FindControl(“contro name”).  Nothing new here but if you forget the syntax it’s always a pain to figure out even when using Intellisense or hunting around on the net.

My main problem though was to figure out how & where to create this delegate for my nested repeater.  First why would I want to create a delegate for my nested repeater?  Well, in order to do something for this nested repeater in it’s ItemDataBound event, you first need to create a delegate for that event so that you can handle that event (do some logic in the method that you’ve subbed out) once the nested repeater is being bound to its data source.  By creating a delegate (a reference) to the ItemDataBound event of the nested repeater, that means you’re saying hey, when I bind this nested repeater to its datasource, I intend to put in some of my own logic into it’s ItemDataBound event.  Maybe you want to manipulate its rows afterwords, add some row logic, or like I was doing…setting some properties of a custom control I have in the ItemTemplate of that nested repeater.  Well, you’d want to do this in the ItemDataBound event of the nested repeater.  So by creating a delegate, you’re saying hey, Here’s the method that I’m gonna use to handle that event.  This method must have the same name and signature in my code-behind.  So once you create that delegate, you can then perform your logic in your ItemDataBound method that you stubbed out.  Also, since this repeater is nested inside a parent, I cannot create the delegate outside the parent repeater because this repeater is part of the parent’s control collection.

Again, In my case, I needed to set some properties for a custom control that I had placed  inside my nested repeater and set some properties for that custom control so that when the ItemDataBound event was fired for each row of my nested repeater, the properties were set every time for that custom control per row.  The control in this case was a control to output some HTML to show a product image along with some description text and link to the product.  The control provided and renders that output for you dynamically based on the product ID you set to one of that control’s properties.

Also, before I go ahead with the example, a quick note.  Since I am coding on a public .com website, you want to use repeaters as much as possible as opposed to lets say a DataGrid or GridView.  You want to try to eliminate either of those because they are much more heavy especially when you have millions of hits to the site and thousands of hits to that grid.  By using a repeater, yes, you have to do a little more work such as create your own paging, but guess what…you get to do it YOUR way, which can be much lighter as well as most good developers end up having to extend GridView & DataGrid paging anyways or roll their own in the end for more realistic business applications/needs.  Repeaters are the fastest when it comes to lists so on a .com, performance is crucial.  You probably won’t see a lot of the good .coms use DataGrids or GridViews and also you end up caching repeaters which is the most efficient way to grid data at least in my opinion when performance is critical when you have a million hits a day on your site.  And you certainly will never see DataSets, not one in a .com.  Instead, faster and lighter objects (generic lists & dictionaries for example) are cached for performance instead of using the heavy hobbyist poor performing approach of a DataSet which always ends up in more manual maintenance, slow performance, and a pain in the ass because you continually  have to figure out work-arounds for what you can already just do straight up in C#.  You’re also restricted to the way Microsoft pushes you to use that object and all sort of other nasty pains at the cost of “easy”.

Now, the above may seem very confusing to you in terms of summing up the issue,  but the purpose was to give you the story first. 

Now let me show you the code, what I was doing, and how I did it.

First, the .aspx page which contains the nested repeaters:

   1: <asp:Repeater ID="rptDealerships" Runat="server" OnItemDataBound="rptDealerships_ItemDataBound">
   2:     <ItemTemplate>
   3:             <table width="600">
   4:                 <tr>
   5:                     <td class="PagerAlpha_LeftColumn" width="100px">
   6:                         <asp:HyperLink ID="hypDealer" Text=<%#Eval("Name") %> runat="server" />           
   7:                     </td>
   8:                     <td>
   9:                         <table>
  10:                             <tr>
  11:                                 <asp:Repeater ID="rptCars" Runat="server" OnItemDataBound="rptCars_ItemDataBound">
  12:                                     <ItemTemplate>
  13:                                             <td valign="top">
  14:                                                 <ar:CarDisplay runat="server" id="carDisplay"/>
  15:                                             </td>
  16:                                     </ItemTemplate>                         
  17:                                 </asp:Repeater>
  18:                             </tr>
  19:                         </table>
  20:                      </td>
  21:                 </tr>
  22:             </table>
  23:     </ItemTemplate>
  24: </asp:Repeater>

The Code-Behind (parts of it related to this post):

   1: override protected void OnInit(EventArgs e)
   2: {
   3:     base.OnInit(e);
   4:     rptDealerships.ItemDataBound += new RepeaterItemEventHandler(rptDealerships_ItemDataBound);
   5: }
   6:  
   7: public void LoadDealerships()
   8: {
   9:     List<Dealer> dealeshiprList = new List<Dealer>();
  10:     dealershipList = GetDealers();
  11:  
  12:     if (dealershipList.Count > 0)
  13:     {
  14:         rptDealerships.DataSource = dealerList;
  15:         rptDealerships.DataBind();
  16:     }
  17: }
  18:  
  19: public void rptDealerships_ItemDataBound(Object sender, RepeaterItemEventArgs e)
  20: {
  21:     if (e.Item.ItemType == ListItemType.Item || e.Item.ItemType == ListItemType.AlternatingItem)
  22:     {
  23:         Dealer dealer = (Dealer)e.Item.DataItem;
  24:  
  25:         List<Car> carList = new List<Car>();
  26:         if (dealer.DealerID != null)
  27:         {
  28:             carList = GetCarsByDealerID(dealer.DealerID);
  29:         }
  30:  
  31:         if (carList.Count > 0)
  32:         {
  33:             Repeater nestedCarsRepeater = e.Item.FindControl("rptCars") as Repeater;
  34:  
  35:             nestedCarsRepeater.ItemDataBound += new RepeaterItemEventHandler(this.rptCars_ItemDataBound);
  36:             nestedCarsRepeater.DataSource = productsList;
  37:             nestedCarsRepeater.DataBind();
  38:         }
  39:  
  40:     }
  41:  }
  42:  
  43: public void rptCars_ItemDataBound(object sender, RepeaterItemEventArgs e)
  44: {
  45:     // Do whatever you want here
  46: }

What I want you to pay attention to are the following 2 lines:

Repeater nestedProductRepeater = e.Item.FindControl("rptCars") as Repeater;

nestedProductRepeater.ItemDataBound += new RepeaterItemEventHandler(this.rptCars_ItemDataBound);

Since rptCars is nested inside a control (the parent rptDealerships), I had to get a reference to it from within the ItemDataBound event of rptDealerships.  Then I could create the delegate for the ItemDataBound event for rptCars so that I would have an event to manipulate (e.g. set values for custom controls that are in the ItemTemplate of that repeater, add items to the row, etc.) Data Rows inside the nested repeater. 

And because rptDealerships was a control not inside another control, it is freely available in your code-behind class so you can reference it in order to create its delegate anywhere you want.

So those controls that you want to create delegates for that are nested inside other controls.  First you must get a reference to them.  Then you can create the delegate using that reference after you get that reference while inside the parent’s event (ItemDataBound).

Hopefully this was not too confusing as it’s definitely not simple to do if you have not dealt with events a lot with repeaters or other MS grids.

Please kick/dzone/digg if this post if it was helpful.


posted @ Saturday, November 08, 2008 7:50 PM | Feedback (2)