Thursday, January 19, 2006

Nested Repeater Controls

I am working on this variation of a main menu navigation where the submenu will display below the main navigational element once it is clicked.  Basically I was struggling with how I was going to databind the submenu to the main menu item it was related too.

I went about solving this problem by using nested repeater controls.

   1:  <asp:repeater id="repMenu" runat="server">
   2:        <itemtemplate>
   3:          <%#  DataBinder.Eval(Container.DataItem, "Name") %><br>
   4:          <asp:repeater id="repSubMenu" runat="server">
   5:               <itemtemplate>
   6:                    <%# DataBinder.Eval(Container.DataItem, "MenuTitle") %>
   7:               </itemtemplate>
   8:          </asp:repeater>
   9:       </itemtemplate>
  10:   </asp:repeater>


The above HTML defines two Repeater Controls.  The first one will display my DataBound Column Name from a Table in SQL Server. The second Repeater will display a column called Menu Title from a second DataBound Column.

Looking at the C# Code:

   1:          /// <summary>
   2:          /// Repeater Control
   3:          /// </summary>
   4:          protected System.Web.UI.WebControls.Repeater repMenu, repSubMenu;
   5:   
   6:          private void Page_Load(object sender, System.EventArgs e)
   7:          {
   8:              Section section = new Section();
   9:              repMenu.DataSource = section.SelectAll();
  10:              repMenu.DataBind();
  11:          }
  12:   
  13:          #region Web Form Designer generated code
  14:          override protected void OnInit(EventArgs e)
  15:          {
  16:              //
  17:              // CODEGEN: This call is required by the ASP.NET Web Form Designer.
  18:              //
  19:              InitializeComponent();
  20:              base.OnInit(e);
  21:          }
  22:          
  23:          /// <summary>
  24:          ///        Required method for Designer support - do not modify
  25:          ///        the contents of this method with the code editor.
  26:          /// </summary>
  27:          private void InitializeComponent()
  28:          {
  29:              this.Load += new System.EventHandler(this.Page_Load);
  30:              this.repMenu.ItemDataBound +=new RepeaterItemEventHandler(repMenu_ItemDataBound);
  31:          }
  32:          #endregion
  33:   
  34:          private void repMenu_ItemDataBound(object sender, RepeaterItemEventArgs e)
  35:          {
  36:              RepeaterItem item = e.Item;
  37:              repSubMenu = (Repeater) item.FindControl("repSubMenu");
  38:              DataRowView drv = (DataRowView) item.DataItem;
  39:              Page page = new Page();
  40:              page.SectionID = (int)drv.Row.ItemArray[0];
  41:              repSubMenu.DataSource = page.SelectAllWSectionIDLogic();;
  42:              repSubMenu.DataBind();
  43:   
  44:          }

I use LLBLGen to generate DataLayer Code automatically, your code may use DataTables, DataSets, XML, etc. The key to the code, however, is the ItemDataBound function (Lines 34-44).  Within this function is where the magic happens.  The first line (line 36) sets the line item that we are on in the first Repeater so that we can work with the data.  In the second line (line 37) we need to find the Second Repeater so we know how to work with it.  Line 38 we need to move the DataItem Row into a DataRowView so that we can access the columns.  I then initialize my datalayer class from LLBLGen and assign the sectionID from the DataRowView from the first Repeater into it. SectionID is specific to my database and tables.  I use the methods/Properties Row.ItemArray from the DataRowView class.  What ItemArray does is return the columns and the data that is bound in the first Repeater as an array.  I found from debugging and looking at the variables while stepping through the code that the column I needed SectionID was at the 0 index of the array.

I then call the method again within the LLBLGen class and methods to bring back a DataTable of all the pages that relate to the section that I am in.  I DataBind the repeater and I set the databound column I want to display MenuTitle in the HTML within the second Repeaters ItemTemplate.

The output of the nested table above would look something like this:

Section
Page
page
Page
Section
Page
Page
Page

I need to do more work to get the navigation menu to be useful like making the Section Name a linkbutton that hides/shows the nested Repeater.  I'll blog on that when I figure that part out.


Update: based on comments from Travis I was able to update my code to streamline it a little better. Instead of this line:
  40:              page.SectionID = (int)drv.Row.ItemArray[0];
I was able to replace it with:
  40:              page.SectionID = (int)drv["ID"];
Thus making it easier to get at the columns needed from the parent repeater. Thanks Travis.

Thursday, January 19, 2006 9:53:25 AM (Eastern Standard Time, UTC-05:00)
The only thing I would change is instead of using the <%# and %> server tags in the *.aspx, I'd either use an asp:literal or use an li runat=server (and reference it in the code-behind with an htmlgenericcontrol). I think Ern discovered once that using the code-behind was faster than the server tags.

Depending on your query, you should be able to do:
drv["SectionID"];
instead of:
(int)drv.Row.ItemArray[0];
I think, I could be wrong. Maybe you'd have to do:
int.Parse(drv["SectionID"].ToString());
Either way though, you wouldn't have to worry about which item index you needed to get the field you want.

Another thing is that if the list were very long, it's going to make a call to the DB for every root item rendered. I'm not sure what I would do differently to fix that though.
Thursday, January 19, 2006 10:11:52 AM (Eastern Standard Time, UTC-05:00)
Yeah next steps in this is to make it so the first repeater menu item is a link button that shows and hides the nested repeater control.

Thanks for the drv["ID"]; code I updated the post with that information.
Wednesday, April 11, 2007 11:32:45 PM (Eastern Daylight Time, UTC-04:00)
hi,

I found your code while i was jus got mad using the repeater controls.. i have a query for you, i am using MVC architechture and do not use any of the .net controls events for writting code, like _ItemDataBound etc., i have a situation where there are around 3 repeater in a single row... like this i have around 10 pages to be done, which is taking lot of time in designing and even in coding, can you tell me any other way of handling this situation other than using repeater controls..
Srenvas
Name
E-mail
Home page

Comment (Some html is allowed: a@href@title, b, blockquote@cite, em, i, strike, strong, sub, super, u)  

Enter the code shown (prevents robots):

Blog Posts by:

Currently Viewable:

My Twitter Updates

View Twitter Page