42

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.

Mark Hurd
  • 10,175
  • 10
  • 62
  • 96
Mike
  • 433
  • 1
  • 4
  • 7
  • 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) – Jaime Aug 26 '10 at 00:59
  • 2
    Send sample data your repeaters will show. – Amr Elgarhy Aug 26 '10 at 01: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. – Jon Hanna Aug 26 '10 at 01: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. – Mike Aug 26 '10 at 02: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. – John Saunders Aug 26 '10 at 03:30
  • Here's nice detailed article on nested repeater control to display hierarchical data http://goo.gl/I37Nuj – Satinder singh Aug 19 '15 at 05:14

4 Answers4

83

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;
}
Stuart
  • 1,768
  • 2
  • 11
  • 14
  • 1
    Please fill out this example by showing what the data looks like. In particular, what is in the "Employees" property? – John Saunders Aug 26 '10 at 01:19
  • 3
    @John it's clearly an enumeration of some sort of object that has a Name property. – Jon Hanna Aug 26 '10 at 01:32
  • 3
    @Jon: duh. really? It's clear to me, but maybe not to the next thousand people who read this answer. – John Saunders Aug 26 '10 at 03:27
  • 3
    Done. I also added a note on how to handle the case where Department doesn't have an Employees property. – Stuart Aug 26 '10 at 11:12
  • Any idea why I get the error "DataBinding: 'Namespace.MyFile' does not contain a property with the name 'Name'. when it does have a property called "Name" and is public? – Matthew Lock Aug 29 '13 at 04:09
  • Ah I didn't get getters and setters on my object fields. – Matthew Lock Aug 30 '13 at 01:06
  • 1
    Great answer. I've been looking for something clean and simple like this for so long! – brimble2010 Feb 03 '15 at 11:52
  • On the off chance you've tried wrapping your DataSource value in "double quotes" and the example above isn't working -- well, use 'single quotes' _just like_ the example. – badteeth Aug 17 '17 at 12:07
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
Mike Corcoran
  • 12,622
  • 4
  • 31
  • 46
Jon Hanna
  • 102,999
  • 9
  • 134
  • 232
  • 3
    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. – Tahbaza Aug 26 '10 at 01:51
  • protected IEnumerable GetNames(object famObj) any particular for the function parameter name? – Yiping Apr 08 '14 at 02:27
  • @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. – Jon Hanna Apr 08 '14 at 10:02
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
    }
Tahbaza
  • 9,040
  • 2
  • 24
  • 39
  • 2
    It gets nasty, but your making it nastier with needless itemdatabound handlers instead of just using datasource. – Jon Hanna Aug 26 '10 at 01:35
  • 5
    @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. – Tahbaza Aug 26 '10 at 01:52
  • 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. – Jon Hanna Aug 26 '10 at 02:00
  • 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. – Michael Crenshaw Dec 28 '17 at 18:45
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.

BrunoLM
  • 88,362
  • 76
  • 272
  • 427