Nested Repeaters in ASP.NET

Nested Repeaters in ASP.NET

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

Nested Repeaters in C#

HTML CODE :

<asp:Repeater ID="Repeater1" runat="server" 
onitemdatabound="Repeater1_ItemDataBound">
<ItemTemplate>
<h1>
Repeater 1</h1>
<asp:Repeater ID="Repeater2" runat="server" onitemdatabound="Repeater2_ItemDataBound">
<ItemTemplate>
<h1>
Repeater 2
</h1>
<asp:Repeater ID="Repeater3" runat="server" onitemdatabound="Repeater3_ItemDataBound">
<ItemTemplate>
<h1>
Repeater 3
</h1>
<asp:Repeater ID="Repeater4" runat="server" onitemdatabound="Repeater4_ItemDataBound">
<ItemTemplate>
<h1>
Repeater 4
</h1>
</ItemTemplate>
</asp:Repeater>
</ItemTemplate>
</asp:Repeater>
</ItemTemplate>
</asp:Repeater>
</ItemTemplate>
</asp:Repeater>

C# Code:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Data;

public partial class _Default : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
DataTable dt1 = new DataTable();
//Need to assign the Data in datatable
Repeater1.DataSource = dt1;
Repeater1.DataBind();

}
protected void Repeater1_ItemDataBound(object sender, RepeaterItemEventArgs e)
{
if (e.Item.ItemType == ListItemType.Item || e.Item.ItemType == ListItemType.AlternatingItem)
{
Repeater Repeater2 = (Repeater)(e.Item.FindControl("Repeater2"));

DataTable dt2 = new DataTable();
//Need to assign the Data in datatable
Repeater2.DataSource = dt2;
Repeater2.DataBind();
}

}
protected void Repeater2_ItemDataBound(object sender, RepeaterItemEventArgs e)
{
if (e.Item.ItemType == ListItemType.Item || e.Item.ItemType == ListItemType.AlternatingItem)
{
Repeater Repeater3 = (Repeater)(e.Item.FindControl("Repeater3"));

DataTable dt3 = new DataTable();
//Need to assign the Data in datatable
Repeater3.DataSource = dt3;
Repeater3.DataBind();
}

}

protected void Repeater3_ItemDataBound(object sender, RepeaterItemEventArgs e)
{
if (e.Item.ItemType == ListItemType.Item || e.Item.ItemType == ListItemType.AlternatingItem)
{
Repeater Repeater4 = (Repeater)(e.Item.FindControl("Repeater4"));

DataTable dt4 = new DataTable();
//Need to assign the Data in datatable
Repeater4.DataSource = dt4;
Repeater4.DataBind();
}

}
}

Button inside Nested Repeater

Solution:

Put the AddHandler in the Repeater Item Created.

This link helps me to solve my problem.

https://www.mindstick.com/Forum/45/itemcommand-event-in-nested-repeater-and-listview

ASP.NET repeater dynamic value

You need to nest two repeaters then.

You have

top table - feed the main repeater - 1 record for each.

child table - feed the child repeater - child table - many records for each.

Also, make sure you ALWAYS use if (!isPostBack) to have a real first page load - if you don't do most (if not all) of your loading and databinding inside of the !IsPostback?

you cannot even really quite much build a working page that can survive post-backs.

Ok, so we have the master (top) repeater.

for the child repeater, you might have one record, maybe many. It don't matter. But WHAT DOES matter is you now have a whole new seperate data set and whole new set of Eval() and binding that belongs to that child table or child data set.

So, lets take a main repeater (our hotels), and for each instance, we drop in another repeater to display the child data (in this case people booked in the hotel).

So, we have this mark-up - note the nested 2nd repeater.

        <asp:Repeater ID="repHotels" runat="server" OnItemDataBound="repHotels_ItemDataBound">
<ItemTemplate>
<asp:Label ID="txtHotel" runat="server" Width="240px"
Text='<%# Eval("HotelName") %>' Font-Size="X-Large" >
</asp:Label>

<asp:TextBox ID="txtDescription" runat="server"
Text='<%# Eval("Description") %>'
TextMode="MultiLine" Rows="5" Columns="40" Style="margin-left:20px" >
</asp:TextBox>
<br />
<h4>People Booked</h4>
<asp:Repeater ID="repPeopleBooked" runat="server">
<ItemTemplate>
<asp:Label ID="lFirst" runat="server" Text='<%# Eval("FirstName") %>'>
</asp:Label>
<asp:Label ID="lLast" runat="server" Text='<%# Eval("LastName") %>'
style="margin-left:10px">
</asp:Label>
<br />
</ItemTemplate>
</asp:Repeater>
<%-- our repeat separator --%>
<div style="height:20px">
<hr style="border:1px solid" />
</div>
</ItemTemplate>
</asp:Repeater>

And our code to load. We ONLY load the main repeater data source, and THAT will trigger the item data bound event, and in that event then we deal with EACH separate instance of the child repeater.

Code like this:

    protected void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack)
LoadData();
}

void LoadData()
{
SqlCommand cmdSQL = new SqlCommand("SELECT * from MyHotels ORDER BY HotelName");

repHotels.DataSource = MyRstP(cmdSQL);
repHotels.DataBind();
}

protected void repHotels_ItemDataBound(object sender, RepeaterItemEventArgs e)
{
if ( (e.Item.ItemType == ListItemType.Item) ||
(e.Item.ItemType == ListItemType.AlternatingItem))
{
// get child nested repeater
Repeater rChild = e.Item.FindControl("repPeopleBooked") as Repeater;

DataRowView rRow = ((DataRowView)e.Item.DataItem);
string strSQL = "SELECT * FROM People WHERE Hotel_ID = @id";
SqlCommand cmdSQL = new SqlCommand(strSQL);
cmdSQL.Parameters.Add("@id", SqlDbType.Int).Value = rRow["ID"];

rChild.DataSource = MyRstP(cmdSQL);
rChild.DataBind();

}
}
public DataTable MyRstP(SqlCommand cmdSQL)
{
DataTable rstData = new DataTable();
using (SqlConnection conn = new SqlConnection(Properties.Settings.Default.TEST4))
{
using (cmdSQL)
{
cmdSQL.Connection = conn;
conn.Open();
rstData.Load(cmdSQL.ExecuteReader());
}
}
return rstData;
}

and we now get this:

enter image description here

So, we see the 3 main records that drive the main repeater, and then we see the child records for the nested repeater.

Now, in your case, you might only ever have one child record - but it don't matter. What really matters is that you have the ability to deal with, and bind, and have separate Eval() and data for that child repeater.

using nested repeater in asp.net

If you have only two hierarchy levels, you are halfway there: use the ItemDataBound event to set the DataSource for the inner repeater: ((Repeater)FindControl('rep2')).DataSource = <proper data>;

If you have multiple/unknown hierarchy levels, the only way would be creating them all from code behind. You could use a Panel as a container, create the repeaters dinamically and add the parent one to the panel, like: Panel1.Controls.Add(rep1);

Find nested repeater in 3 level repeater

Okay I found the solution. It has to do with the ASP.NET page lifecycle.

After the lnkButton_Click event, a postback occurred, but I needed to "rebind" the repeater datasource on postback, not just initial page_load.

e.g.

protected void Page_Load()
{
if (!IsPostBack)
{
// bind control
}
else
{
// rebind control
}
}

How can I Implement N level nested Repeater control in asp.net?

For this answer I'm going to suggest creating your template programmatically - see here: https://msdn.microsoft.com/en-us/library/aa289501 .
There is probably some way to use templates that have been created in markup, but this seems easier, and definitely more flexible.

I start out with a page with just a repeater (not template)

<body>
<form id="form1" runat="server">
<div>
<asp:Repeater runat="server" ID="TestRepeater">
</asp:Repeater>
</div>
</form>
</body>

and a data class

public class DataClass
{
public string Name { get; set; }
public List<DataClass> Children { get; set; }
}

For the template we use the following class:

public class DataTemplate : ITemplate
{
public void InstantiateIn(Control container)
{
var name = new Literal();
var repeater = new Repeater();

name.DataBinding += BindingLiteral;
repeater.DataBinding += BindingRepeater;

// this here makes it recursive
repeater.ItemTemplate = new DataTemplate();

container.Controls.Add(name);
container.Controls.Add(repeater);
}

private void BindingLiteral(object sender, System.EventArgs e)
{
var name = (Literal)sender;
var container = (RepeaterItem)name.NamingContainer;
name.Text = String.Concat("<h2>", DataBinder.Eval(container.DataItem, "Name").ToString(), "</h2>");
}

private void BindingRepeater(object sender, System.EventArgs e)
{
var name = (Repeater)sender;
var container = (RepeaterItem)name.NamingContainer;
name.DataSource = DataBinder.Eval(container.DataItem, "Children");
}
}

Obviously you'll want to use a more sophisticated template. Notice that if you currently have a template in markup, you could simply take the code that has been generated by the markup parser, and adapt it to your needs.

Now in the code behind of the page we simple assign the ItemTemplate and DataSource:

public partial class Test : System.Web.UI.Page
{
protected void Page_Init(object sender, EventArgs e)
{
TestRepeater.DataSource = GetTestData();
TestRepeater.ItemTemplate = new DataTemplate();
TestRepeater.DataBind();
}
}

Nice thing about this is your template is just a class, so you could add a public Int32 Depth { get; set; } to it, and change the generated controls based on your depth.



Related Topics



Leave a reply



Submit