J'ai une classe qui contient des données hiérarchiques. Je souhaite présenter ces données dans ma webapp ASP.net à l'aide de répéteurs imbriqués. Comment puis-je faire cela? Je n'ai jamais fait qu'un seul niveau d'imbrication, comment dire cinq niveaux?
Chaque élément peut avoir zéro ou plusieurs sous-éléments. Je suis essentiellement en retrait à chaque sous-niveau en utilisant des trucs CSS. Je ne veux pas utiliser le contrôle treeview, je veux m'en tenir strictement à un répéteur.
Mise à jour:
Mes données proviennent d'une base de données. J'ai un élément datatable avec quelques propriétés de base.
Item
{
ID,
Name,
Description,
...
}
Ensuite, j'ai une table plusieurs à plusieurs avec:
Parent
{
ParentID,
ChildID
}
Je parcourt chaque élément et affiche ses enfants; et ses enfants. Je suppose que cela serait mieux accompli avec des répéteurs imbriqués, mais je peux me tromper.
Il est toujours plus propre de traiter la source de données que de jouer avec ItemDataBound, mais c'est encore plus le cas lors de l'imbrication des répéteurs:
<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>
La source de données interne peut également être une propriété évaluée ou un appel à une méthode qui renvoie l'énumération souhaitée. Sachez simplement qu'il sera appelé avec un objet. Je préfère écrire la version spécifique, puis surcharger:
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);
}
Une chose à savoir est que si vous voulez obtenir l'objet actuel dans le répéteur parent, vous devez l'obtenir avec:
((RepeaterItem)Container.Parent.Parent).DataItem
J'ai constaté que la façon la plus simple de faire des répéteurs imbriqués sans se soucier des événements de liaison de données est de simplement définir la source de données à l'aide de <%# %>
syntaxe.
Par exemple:
<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>
Cela suppose que votre classe Departments possède une propriété Employees - par exemple:
public class Department {
public string DeptName {get; set;}
public IEnumerable<Employee> Employees {get; set;}
}
public class Employee {
public string Name {get; set;}
}
Si votre objet répéteur externe n'a pas de propriété correspondant à l'objet répéteur interne, vous pouvez toujours utiliser cette astuce, en ajoutant une méthode dans votre code-behind qui effectue le calcul. Ainsi, votre répéteur intérieur pourrait devenir:
<asp:Repeater runat="server" DataSource='<%# GetEmployees(Container.DataItem) %>'>
puis GetEmployees pourrait ressembler à quelque chose comme:
protected IEnumerable<Employee> GetEmployees(object item) {
var dept = (Department) item;
// then do whatever is necessary to get the employees from dept
return employees;
}
Vous pouvez imbriquer des répéteurs sans problème. Plus de 2 niveaux de profondeur deviennent méchants. Voici comment:
Le html ressemble à ceci:
<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>
Le codebehind ressemble à ceci:
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
}
<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();
};
Sachez que FindControl
n'est pas récursif, il n'obtiendra que les enfants.