Nested Repeater HeaderTemplate issue - asp.net

Hello i am using nested repeaters (3 levels) and wanted to add header to a second level repeater, so I could assign title for item group. The issue is, when I add header template to second level repeater, my code behind can't find third level repeater. Even if header template is empty the issue still persists. Does anyone have any suggestions?
<asp:Repeater runat="server" ID="rpt2nd" OnItemDataBound="rpt2nd_OnDataBound">
<HeaderTemplate></HeaderTemplate>
<ItemTemplate>
<asp:Repeater runat="server" ID="rpt3rd">
<ItemTemplate></ItemTemplate>
</asp:Repeater>
</ItemTemplate>
</asp:Repeater>
protected void rpt2nd_OnDataBound(object sender, RepeaterItemEventArgs e)
{
var rpt3rd= ((Repeater)e.Item.FindControl("rpt3rd"));
rpt3rd.DataSource = ((KeyValuePair<int, IEnumerable<CustomClass>>)e.Item.DataItem).Value;
rpt3rd.DataBind();
}

You need to add if(e.Item.ItemType != ListItemType.Item && e.Item.ItemType != ListItemType.AlternatingItem) return; to the top of the rpt2nd_OnDataBound (also, it should be renamed to rpt2nd_OnItemDataBound to avoid confusion).

Related

Nested repeater and datasource

I have a repeater nested in an other repeater.
My question is: is it possible to generate other ItemTemplate components with datasource.
If yes, how can I do it ?
For example:
ItemTemplate1 of parentRepeater
A
B
C
ItemTemplate2 of parentRepeater
D
E
F
It means that ItemTemplate of childRepeater is changed for each ItemTemplate of parentRepeater.
<asp:Repeater runat="server" ID="repeater1"
onitemdatabound="Repeater1_ItemDataBound" >
<ItemTemplate>
<!--Outer repeater -->
<asp:repeater id="repeater2" runat="server">
<itemtemplate>
<!--Inner repeater repeater -->
</itemtemplate>
</asp:repeater>
</ItemTemplate>
</asp:Repeater>
`
Here is Back end code
protected void Repeater1_ItemDataBound(object sender, RepeaterItemEventArgs e)
{
if (e.Item.ItemType == ListItemType.Item || e.Item.ItemType == ListItemType.AlternatingItem)
{
//it return current row (think in terms of nested loops)
DataRowView drv = (DataRowView)e.Item.DataItem;
// get value of that row
int postID = Convert.ToInt32(drv["PostID"]);
//do what ever you want to do
Repeater2.DataSource = //some data like you did in outer repeater
Repeater2.DataBind();
}
}
If your goal is to have them alternate back and forth between two templates, then use AlternateItemTemplate.
<asp:Repeater runat="server">
<ItemTemplate>
This came from ItemTemplate.
</ItemTemplate>
<AlternateItemTemplate>
This came from AlternateItemTemplate.
</AlternateItemTemplate>
</asp:Repeater>
This is commonly used when you want to vary the background color of each row in a table.

Find controls in GridView

I have something weird happening, maybe i don't know of something?
I am trying to populate a Drop Down List (in the editItemTemplate) and also, when the Grid View Loads populate a column, with strings instead of the id's that it contains now.
ASPX(the fiels that I have problem with are &
<asp:GridView ID="gvAdminArticleAdd".....
<asp:TemplateField HeaderText="invsId" SortExpression="invsId">
<EditItemTemplate>
<asp:DropDownList ID="ddl_invNames" runat="server" AutoPostBack="True" />
</EditItemTemplate>
<ItemTemplate>
<asp:Label ID="lbl_insLabel" runat="server" Text='<%# Bind("invsId") %>'></asp:Label>
</ItemTemplate>
<FooterTemplate>
<asp:DropDownList ID="ddl_invNamesNew" runat="server" AutoPostBack="True" />
</FooterTemplate>
</asp:TemplateField>
CodeBehind
protected void gvAdminArticleAdd_RowDataBound(object sender, GridViewRowEventArgs e)
{
if (e.Row.RowType == DataControlRowType.DataRow)
{
//111111
//finding cotrols into the edit rows event
if (e.Row.RowState == DataControlRowState.Edit)
{
DropDownList ddlImages = (DropDownList)e.Row.FindControl("ddlImages");
ddlImages.DataSource = GetPdfs();
ddlImages.DataBind();
DropDownList ddlinvsNames = (DropDownList)e.Row.FindControl("ddl_invNames");
ArrayList invList = GetInvestigatorNames();
ddlinvsNames.DataSource = invList;
ddlinvsNames.DataBind();
}
//222222
//finding cotrols into rows
Label insLabel = (Label)e.Row.FindControl("lbl_insLabel");
int invsLabelId = int.Parse(insLabel.Text);
insLabel.Text = connection.GetInvsNameById(invsLabelId);
}
}
The problem that I have occur in the RowDataBound event, I can't figure it up what is wrong
//111111 and //22222 are working correctly(if I comment one of them), but not together. how can it be?
if I place them together I am getting an error on this line of code
int invsLabelId = int.Parse(insLabel.Text);
Object reference not set to an instance of an object.
so the error is in the previous line, but I can't figure why.
//111111 and //22222 are working correctly(if I comment one of them),
but not together. how can it be?
You do not need those two to work together. Only one will be available at a given time.
if (e.Row.RowState == DataControlRowState.Edit)
{
// EditItemTemplate - only controls inside EditItemTemplate are available here.
var ddlImages = (DropDownList)e.Row.FindControl("ddlImages");
var ddlinvsNames = (DropDownList)e.Row.FindControl("ddl_invNames");
}
else
{
// ItemTemplate - only controls inside ItemTemplate are available here.
var insLabel = (Label)e.Row.FindControl("lbl_insLabel");
}
The problem is that your dropDownList is added only during the EditMode. This you are retrieving in the if (e.Row.RowType == DataControlRowType.DataRow) code block. That is fine, you will be able to retrieve the dropdown list correctly. But in the same code block you are retrieving the Label ddl_invNames which will not be available in the edit mode, because you have added this label in the ItemTemplate. So the error must be in this place. However if you comment whole of this block, then the code goes directly to /222222, where you are trying to access the label, which will not be there at all during the edit mode of the row

Using Nested Repeaters With Stored Procedures

Hey I would like to use Nested Stored Procedures in ASP.NET 2.
The first stored procedure returns all Campains and second one returns all items in the campaigns.
I have my 2 repeaters set up, and now I am trying to pass a parameter from the parent repeater to child repeater stored procedure i.e campaign id....this proving tricky
In the code behind I wanted to try
public void Repeater1_ItemDataBound(Object sender, RepeaterItemEventArgs e)
{
if (e.Item.ItemType == ListItemType.Item || e.Item.ItemType ==
ListItemType.AlternatingItem || e.Item.ItemType == ListItemType.EditItem)
{
SqlDataSource2.SelectParameters["campaignId"].DefaultValue =
DataBinder.Eval(e.Item.DataItem, "campaignId").ToString();
}
}
But I dont know how to call this method or get it to load if I try this
<asp:Repeater ID="Repeater1" runat="server" DataSourceID="SqlDataSource1" OnDataBinding="Repeater1_ItemDataBound">
I get the error
CS0123: No overload for 'Repeater1_ItemDataBound' matches delegate 'System.EventHandler'
Any help would be greatly appreciated
EDIT : Changed mY Code Behind to
public void Repeater1_ItemDataBound(Object sender, RepeaterItemEventArgs e)
{
if (e.Item.ItemType == ListItemType.Item || e.Item.ItemType ==
ListItemType.AlternatingItem || e.Item.ItemType == ListItemType.EditItem)
{
Response.Write(DataBinder.Eval(e.Item.DataItem, "campaignId").ToString());
SqlDataSource2.SelectParameters["campaignId"].DefaultValue =
DataBinder.Eval(e.Item.DataItem, "campaignId").ToString();
SqlDataSource2.SelectParameters["statusId"].DefaultValue =
"1";
}
foreach (RepeaterItem repeaterItem in Repeater1.Items)
{
((Repeater)(repeaterItem.FindControl("Repeater2"))).DataBind();
}
}
But no joy its passing the correct campaign id to the stored procedure but this isnt displayed correctly on front end
any ideas ?
Instead of trying to attach the DataBinding event, you should be attaching the ItemDataBound event:
<asp:Repeater ID="Repeater1" runat="server"
DataSourceID="SqlDataSource1" OnItemDataBound="Repeater1_ItemDataBound">
The DataBinding event is for the whole repeater, the ItemDataBound will fire per item.
Microsoft publish an step by step guide to display hierarchical data using Nested Repeater Controls: Display Hierarchical Data
Remember that it is posible to return two datasets from a Store Procedure (An store procedure that ends with two SELECTs)
This is an example using EnterpriseLibrary
Try
Using cmd As DbCommand = db.GetStoredProcCommand("spYourStoreProcedure")
db.AddInParameter(cmd, "#customer", DbType.Int32, nCustomer)
Using ds = db.ExecuteDataSet(cmd)
ds.Tables(0).TableName = "Parent"
ds.Tables(1).TableName = "Child"
ds.Relations.Add("MyRelation", ds.Tables("Parent").Columns("customer"), ds.Tables("Child").Columns("customer"))
ParentRepeater.DataSource = ds.Tables("Parent")
ParentRepeater.DataBind()
End Using
End Using
Catch ex As Exception
' Manage your excepion
Exit Sub
End Try
This is a simple nested repeater:
<asp:Repeater ID="ParentRepeater" runat="server">
<HeaderTemplate>
<ul>
</HeaderTemplate>
<ItemTemplate>
<li>
<b><%# Container.DataItem("customer_name")%></b>
<asp:Repeater ID="repFuncionesXArea" runat="server" DataSource='<%# Container.DataItem.Row.GetChildRows("MyRelation") %>' >
<HeaderTemplate>
<ul>
</HeaderTemplate>
<ItemTemplate>
<li><%# Container.DataItem("customer_history")%></li>
</ItemTemplate>
<FooterTemplate>
</ul>
</FooterTemplate>
</asp:Repeater>
</li>
</ItemTemplate>
<FooterTemplate>
</ul>
</FooterTemplate>
</asp:Repeater>

Repeater in Repeater

I have a repeater inside a repeater. Where the parent repeater is bound to a Datatble which has a column with a Datatable in it.
I would like to bind the child repeater to the datatable column in the parent repeater's datarow
Is this possible? i was thinking i could do this directly in the aspx file like:
DataSource="<%# DataBinder.Eval(Container.DataItem, "Products")%>" but it doesn't seem to work.
In the parent repeater, attach a method to the OnItemDataBound event and in the method, find the nested repeater and data bind it.
Example (.aspx):
<asp:Repeater ID="ParentRepeater" runat="server" OnItemDataBound="ItemBound">
<ItemTemplate>
<!-- Repeated data -->
<asp:Repeater ID="ChildRepeater" runat="server">
<ItemTemplate>
<!-- Nested repeated data -->
</ItemTemplate>
</asp:Repeater>
</ItemTemplate>
</asp:Repeater>
Example (.cs):
protected void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack)
{
ParentRepeater.DataSource = ...;
ParentRepeater.DataBind();
}
}
protected void ItemBound(object sender, RepeaterItemEventArgs args)
{
if (args.Item.ItemType == ListItemType.Item || args.Item.ItemType == ListItemType.AlternatingItem)
{
Repeater childRepeater = (Repeater)args.Item.FindControl("ChildRepeater");
childRepeater.DataSource = ...;
childRepeater.DataBind();
}
}
I would add a DataBinding event to the child repeater itself:
<asp:Repeater ID="parentRepeater" runat="server">
<asp:Repeater ID="childRepeater" runat="server"
OnDataBinding="childRepeater_DataBinding" />
</asp:Repeater>
Then just implement it:
protected void childRepeater_DataBinding(object sender, System.EventArgs e)
{
Repeater rep = (Repeater)(sender);
int someIdFromParentDataSource = (int)(Eval("ParentID"));
// Assuming you have a function call `GetSomeData` that will return
// the data you want to bind to your child repeater.
rep.DataSource = GetSomeData(int);
rep.DataBind();
}
I prefer to do it at the control level instead of the ItemDataBound level so that if you ever have to remove controls or items within your templates you don't have to worry about looking for code in the parent controls that use it. It get's all localize witht he control itself. Plus you never have to do a FindControl.
If you want to replace a control in the future you can just delete it and your code will still work since it is all self contained. Using the ItemDataBound would cause your code to still compile but crash or act unexpectedly at runtime because of it's reliance on child controls.
Here is how it's done:
DataSource='<%# ((System.Data.DataRowView)Container.DataItem)[3] %>'
So if you know the column in the parent table that holds the child table/datasource for the nested repeater you can put this directly in the aspx file.
Repeater1 OnItemDataBound event, then FindControl Repeater2. The code-behind will not find the nested Repeater2! You have to use FindControl("Repeater2").
protected void Repeater1_ItemDataBound(object sender, RepeaterItemEventArgs e)
{
if (e.Item.DataItem != null)
{
MemberView dataRow = (MemberView)e.Item.DataItem;
var cat = MemberPresenter.getMemberID(dataRow.memID);
Repeater rp2 = (Repeater)e.Item.FindControl("Repeater2");
rp2.DataSource = cat;
rp2.DataBind();
}
}
If I need to do that, I usually do it using the ItemDataBound event of the parent repeater to bind the child repeater. If e is your EventArgs parameter, you'll have access to the child repeater via e.Item.FindControl(), and access to the data via e.Item.DataItem.
Here is an example of how to do this:
Article for nested repeater control
protected void MainRepeater_ItemDataBound(object sender, RepeaterItemEventArgs args)
{
if (args.Item.ItemType == ListItemType.Item || args.Item.ItemType == ListItemType.AlternatingItem)
{
Repeater childRepeater = (Repeater)args.Item.FindControl("ChildRepeater");
DataTable innerTable= ((DataRowView)args.Item.DataItem)["InnerTableColumnName"] as DataTable;
childRepeater.DataSource = tasksDetails;
childRepeater.DataBind();
}
}

Dynamically adding controls in ASP.NET Repeater

I find my self having a repeater control which is being databound to an xml document. My client is now requesting that the Textbox's which are being repeater can be either a Textbox or a Checkbox.
I cannot seem to find an easyway to essentially do the following:
if ((System.Xml.XmlNode)e.Item.DataItem.Attributes["type"] == "text")
<asp:TextBox runat="server" ID="txtField" Text='<%#((System.Xml.XmlNode)Container.DataItem).InnerText %>' CssClass="std"></asp:TextBox>
else
<asp:CheckBox runat="server" ID="txtField" Text='<%#((System.Xml.XmlNode)Container.DataItem).InnerText %>' CssClass="std"></asp:TextBox>
Is there a nice way I can extend my current implementaion without have to rewrite the logic. If I could inject the control via "OnItemDataBound" that would also be fine. But I cannot seem to make it work
In your repeater, drop a Panel, then create an event handler for the repeater's data binding event and programmatically create the TextBox or CheckBox and add it as a child control of the Panel. You should be able to get the DataItem from the event args to get information like your "type" attribute or values to feed your Text properties or css information, etc.
I would go with mspmsp's sugestion. Here is a quick and dirty code as an example of it:
Place this in your aspx:
<asp:Repeater ID="myRepeater" runat="server" OnItemCreated="myRepeater_ItemCreated">
<ItemTemplate>
<asp:PlaceHolder ID="myPlaceHolder1" runat="server"></asp:PlaceHolder>
<br />
</ItemTemplate>
</asp:Repeater>
And this in your codebehind:
dim plh as placeholder
dim uc as usercontrol
protected sub myRepeater_ItemCreated(object sender, RepeaterItemEventArgs e)
if TypeOf e Is ListItemType.Item Or TypeOf e Is ListItemType.AlternatingItem Then
plh = ctype(e.item.findcontrol("myPlaceHolder1"), Placeholder)
uc = Page.LoadControl("~/usercontrols/myUserControl.ascx")
plh.controls.add(uc)
end if
end sub
What about something similar to this in your markup in each the textbox and checkbox controls?
Visible=<%= Eval("type").tostring() == "text") %>
If there is needed to add controls based on data then there can be used this approach:
<asp:Repeater ID="ItemsRepeater" runat="server" OnItemDataBound="ItemRepeater_ItemDataBound">
<itemtemplate>
<div>
<asp:PlaceHolder ID="ItemControlPlaceholder" runat="server"></asp:PlaceHolder>
</div>
</itemtemplate>
</asp:Repeater>
protected void ItemRepeater_ItemDataBound(object sender, RepeaterItemEventArgs e)
{
var placeholder = e.Item.FindControl("ItemControlPlaceholder") as PlaceHolder;
var col = (ItemData)e.Item.DataItem;
placeholder.Controls.Add(new HiddenField { Value = col.Name });
placeholder.Controls.Add(CreateControl(col));
}

Resources