44

I have a class that contains hierarchical data. I want to present this data in my ASP.net webapp using nested repeaters. How do I do this? I've only ever done one level of nesting, how do I do say five levels?

Each item can have zero or many sub items. I'm basically just indenting at each subleveling using some css stuff. I do not want to use the treeview control, I want to strictly stick with a repeater.

Update:
My data comes from a database. I have an item datatable with some basic properties.

Item
{
   ID,
   Name,
   Description,
   ...
}

Then I have a many to many table with:

Parent
{
   ParentID,
   ChildID
}

I'm iterating through each item and displaying its children; and its children's children. I assume this would best be accomplished with nested repeaters, but I could be wrong.

6
  • What type of data are you showing? tabular data? lists? where is the data coming from? (you may end up with issues of many selects if you're getting from the database since you'll get a databind for each repeater) Commented Aug 26, 2010 at 0:59
  • 2
    Send sample data your repeaters will show. Commented Aug 26, 2010 at 1:00
  • Don't understand the edit that was made. While I can't think of anything specific in 4.0 that would affect this, (A) I could be wrong, (B) how's the querant meant to know, if they did they wouldn't be asking this question. Commented Aug 26, 2010 at 1:48
  • The data is basically text, and it comes from a database. I just include what version of things I'm using so that solutions can be presented that target my enviroment. Commented Aug 26, 2010 at 2:09
  • @Jon: it's best to ask about what you don't understand, lest you remain ignorant. The information I removed from the title i already in the tags. It's redundant in the title. Also, C# is totally irrelevant to this question. The answer would be identical if Mike were using VB.NET, with the exception of a little syntax. Finally, "Hi", "Thanks", signatures and taglines are noise and don't belong in a Q&A site. Maybe in a discussion forum, which this is not. Commented Aug 26, 2010 at 3:30

4 Answers 4

87

I've found that the simplest way to do nested repeaters without worrying about databinding events is to just set the DataSource using <%# %> syntax.

For example:

<asp:Repeater runat="server" id="Departments">
  <ItemTemplate>
    Name: <%# Eval("DeptName") %>
    Employees:
    <asp:Repeater runat="server" DataSource='<%# Eval("Employees") %>'>
      <ItemTemplate><%# Eval("Name") %></ItemTemplate>
      <SeparatorTemplate>,</SeparatorTemplate>
    </asp:Repeater>
  </ItemTemplate>
</asp:Repeater>

This is presuming that your Departments class has an Employees property - eg:

public class Department {
  public string DeptName {get; set;}
  public IEnumerable<Employee> Employees {get; set;}
}
public class Employee {
  public string Name {get; set;}
}

If your outer-repeater object doesn't have a property corresponding to the inner-repeater object you can still use this trick, by adding a method in your code-behind that does the calculation. So your inner repeater might become:

<asp:Repeater runat="server" DataSource='<%# GetEmployees(Container.DataItem) %>'>

and then GetEmployees might look something like:

protected IEnumerable<Employee> GetEmployees(object item) {
  var dept = (Department) item;
  // then do whatever is necessary to get the employees from dept
  return employees;
}
Sign up to request clarification or add additional context in comments.

8 Comments

Please fill out this example by showing what the data looks like. In particular, what is in the "Employees" property?
@John it's clearly an enumeration of some sort of object that has a Name property.
@Jon: duh. really? It's clear to me, but maybe not to the next thousand people who read this answer.
Done. I also added a note on how to handle the case where Department doesn't have an Employees property.
Great answer. I've been looking for something clean and simple like this for so long!
|
36

It's always cleaner to deal with the datasource than messing about with ItemDataBound, but this is even more the case when nesting Repeaters:

<asp:Repeater DataSource="<%#ColOfCol%>" runat="server">
  <ItemTemplate>
    <tr>
      <asp:Repeater DataSource="<%#Container.DataItem%>" runat="server">
        <ItemTemplate>
          <td><%#SomeExtractingMethodLikeEval()%></td>
        </ItemTemplate>
      </asp:Repeater>
    </tr>
  </ItemTemplate>
</asp:Repeater>

The inner datasource could also be an evaluated property, or a call to a method that returns the enumeration wanted. Just be aware that it will be called with an object. I prefer to write the specific version, and then overload:

protected IEnumerable<string> GetNames(Family fam)
{
  foreach(Person p in fam.Members)
    yield return p.FirstName + " " + p.Surname;
}
protected IEnumerable<string> GetNames(object famObj)
{
    return GetNames((Family)famObj);
}

One thing to be aware of is that if you want to get the current object in the parent repeater than you have to obtain it with:

((RepeaterItem)Container.Parent.Parent).DataItem

3 Comments

This is a matter of preference. I don't like embedding binding logic in aspx and then still having to resort to codebehind for anything of complexity.
protected IEnumerable<string> GetNames(object famObj) any particular for the function parameter name?
@Yiping no, for some reason getting the full names of a bunch of people in a family was the example that it occurred to me to use, though I can't think why. Maybe it related to something I was working on for real at the time, or something that came up in another question here at the time.
13

You can nest repeaters without a problem. More then 2 levels deep gets nasty though. Here's how:

The html looks something like this:

<asp:Repeater ID="r1" runat="server" OnItemDataBound="r1_ItemDataBound">
<ItemTemplate>
<!-- top level repeater element template here -->
    <asp:Repeater ID="r2" runat="server" onitemdatabound="r2_ItemDataBound">
    <ItemTemplate>
<!-- child repeater element template here -->
    </ItemTemplate>
    </asp:Repeater>
</ItemTemplate>
</asp:Repeater>

The codebehind looks like this:

    protected void r1_ItemDataBound(object sender, RepeaterItemEventArgs e) {
        Repeater r2 = (Repeater)e.Item.FindControl("r2");
        r2.DataSource = yourDataSourceHere; // you'll have to query for appropriate data
        r2.DataBind();
    }

    protected void r2_ItemDataBound(object sender, RepeaterItemEventArgs e) {
        // do the same thing here for the 3rd nested repeater if you have a third, and so on
    }

4 Comments

It gets nasty, but your making it nastier with needless itemdatabound handlers instead of just using datasource.
@Jon: responded to your comment below; I find my approach more extensible and less ugly for more complex binding logic; for instance when a filter for current security context is required in addition to parent context.
Yes, when you have to supply logic for the databinding of each item, then itemdatabound has its value. When you're just going through nested objects though, I don't see what the extra complexity split over so many different places and forcing one to switch modes of thought (with the just aspx way one can keep thinking "this is a bunch of stuff I'm going through" rather than considering when controls receive their databind call) gains.
In a massive codebase that sometimes requires the code-behind pattern, I've found it helpful to use the same pattern even in trivial cases. It's easy enough to find the data-binding logic, and it can be extended without spending time changing patterns.
2
<asp:Repeater ID="R1" runat="server">
    <ItemTemplate>
        <asp:Repeater ID="R2" runat="server">
        </asp:Repeater>
    </ItemTemplate>
</asp:Repeater>


R1.ItemDataBound += (s, e) =>
{
    var r2 = e.Item.FindControl("R2") as Repeater;
    r2.DataSource = something;
    r2.DataBind();
};

Be aware that FindControl is not recursive, it will only get the children.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.