Nesting repeaters in .NET

Data repeaters in .NET are very usefull to display database records onto screen. But usually, in a real world situation, you don't have enough functionality with 1 repeater.

Take for example some kind of menu structure. You want to display a category, but every category can have 0 or more subcategories. With 1 repeater, this cannot be done in a simple way. In .NET, you can use nested repeaters for this.

Take the code below in the .aspx file:

... other HTML code ...
<asp:Repeater Runat="server" ID="Category">
<ItemTemplate>
    <a href="category.aspx?category=<%# DataBinder.Eval(Container.DataItem, "Category") %>"><%# DataBinder.Eval(Container.DataItem, "Category") %></a><br>
</ItemTemplate>
</asp:Repeater>
... other HTML code ...


Here you will get a list of categories. If you want to add subcategories, one could try to make the code below work.

... other HTML code ...
<asp:Repeater Runat="server" ID="Category">
<ItemTemplate>
    <a href="category.aspx?category=<%# DataBinder.Eval(Container.DataItem, "Category") %>"><%# DataBinder.Eval(Container.DataItem, "Category") %></a><br>
    <asp:Repeater Runat="server" ID="SubCategory">
    <ItemTemplate>
        <a href="category.aspx?category=<%# DataBinder.Eval(Container.DataItem, "SubCategory") %>"><%# DataBinder.Eval(Container.DataItem, "SubCategory") %></a><br>
    </ItemTemplate>
    </asp:Repeater>
</ItemTemplate>
</asp:Repeater>
... other HTML code ...


To make things work, you have to change several things though.
In your .aspx.cs file (or in the script area if you are not using codebehind files) you will have the following code:


... other c# code ...
DataSet sqlDS = new DataSet();

/* Get the categories */
string Query = "select CategoryId, Category from Categories order by Category";
SqlCommand sqlC = new SqlCommand(Query, objConn);
SqlDataAdapter sqlDA = new SqlDataAdapter();
sqlDA.SelectCommand = sqlC;

sqlDA.Fill(sqlDS, "Category"); /// Our dataset contains the categories

/* Get the subcategories */
string Query = "select SubCategoryId, CategoryId, SubCategory from SubCategories order by SubCategory";
sqlC = new SqlCommand(Query, objConn);
sqlDA.SelectCommand = sqlC;

sqlDA.Fill(sqlDS, "SubCategory"); /// Our dataset contains the categories

/* Add a relationship between the 2 tables
In our case, every SubCategory has a CategoryId to pojnt out there parent
*/
ds.Relations.Add("CategoryRelation", ds.Tables["Category"].Columns["CategoryId"], ds.Tables["SubCategory"].Columns["CategoryId"]);

Category.DataBind();
... other c# code ...


In your .aspx file you will have to change the DataSource to your child repeater (SubCategory) and the syntax in which you get the data.
The code below does so:

... other HTML code ...
<%@ Import Namespace="System.Data" %>

<asp:Repeater Runat="server" ID="Category">
<ItemTemplate>
    <a href="category.aspx?category=<%# DataBinder.Eval(Container.DataItem, "Category") %>"><%# DataBinder.Eval(Container.DataItem, "Category") %></a><br>
    <asp:Repeater Runat="server" ID="SubCategory" DataSource='<%# ((DataRowView)Container.DataItem).Row.GetChildRows("CategoryRelat ion")%>>
    <ItemTemplate>
        <a href="category.aspx?category=<%# DataBinder.Eval(Container.DataItem, "["SubCategory"]") %>"><%# DataBinder.Eval(Container.DataItem, "["SubCategory"]") %></a><br>
    </ItemTemplate>
    </asp:Repeater>
</ItemTemplate>
</asp:Repeater>
... other HTML code ...


If you do both changes, the code should work fine.


Another interesting thing to note is how you can access a dataitem from the parent (from within your child).
For example, we wish the link to include the Category also, even if it is a SubCategory.
Adjust your .aspx file to the following:

... other HTML code ...
<%@ Import Namespace="System.Data" %>

<asp:Repeater Runat="server" ID="Category">
<ItemTemplate>
    <a href="category.aspx?category=<%# DataBinder.Eval(Container.DataItem, "Category") %>"><%# DataBinder.Eval(Container.DataItem, "Category") %></a><br>
    <asp:Repeater Runat="server" ID="SubCategory" DataSource='<%# ((DataRowView)Container.DataItem).Row.GetChildRows("CategoryRelat ion")%>>
    <ItemTemplate>
        <a href="category.aspx?category=<%# DataBinder.Eval(Container.DataItem, "["SubCategory"]") %>"><%# ((DataRow)Container.DataItem).GetParentRow("CategoryRelation")["C ategory"] %> - <%# DataBinder.Eval(Container.DataItem, "["SubCategory"]") %></a><br>
    </ItemTemplate>
    </asp:Repeater>
</ItemTemplate>
</asp:Repeater>
... other HTML code ...


This code is both easy to maintain, and performant.
The same principles count for other items bound to database data, like the DataGrid for example.