Setting a asp repeater data source - asp.net

I have a asp.net repeater control in a aspx page, with runat="server" and an id set, however for some reason i can't access its ID from code behind (I can access the id of the asp:detaislview control it sits in though). So instead in the page_load method I am doing the following:
Repeater repeater = (Repeater)PromotionSitesDetailsView.FindControl("estateRepeater");
repeater.DataSource = estateList;
However when run, an error comes up saying the repeater is null! All I want to do is set the datasource of this repeater to a List object. Any ideas?

You said the Repeater sits inside a DataList. The DataList is, itself, a kind of repeater - the controls inside of it don't exist until the DataList is bound to a datasource, and the controls in the template are created once per item in the source. So if you bind the DataList to a source with 3 items, you will get 3 repeaters.
So it looks kind of like this:
Page
MyDataList
Item0
MyRepeater
Item1
MyRepeater
Item2
MyRepeater
So obviously MyDataList.FindControl("MyRepeater") can't work - which "MyRepeater" are we talking about? Since multiple controls cannot have the same ID, ASP.NET solves this by making the ID unique to something called a NamingContainer. Since the DataList repeats the same set of controls many times (once per item in the data source), each item in the DataList is a NamingContainer.
We need to find the NamingContainer we know holds the instance of MyRepeater that we want:
MyDataList.Items[0].FindControl("MyRepeater");
You can iterate over the items in the DataList after it has been bound (of course, before it has been bound it has no items). You can also operate on a given item in the DataList as that item is being created:
<asp:DataList OnItemDataBound="MyDataList_HandleItemDataBound" ... />
//this will get called once per item as it is created
void MyDataList_HandleItemDataBound(object sender, DataListItemEventArgs e)
{
//e.Item is the current item being databound
Repeater myRepeater = e.Item.FindControl("MyRepeater") as Repeater;
myRepeater.DataSource = //ds
myRepeater.DataBind();
}

You can do it without code-behind, simply by assigning the Repeater DataSource. Here is an example of a two level hierarchical menu:
<ul>
<asp:Repeater ID="ctlMenu" runat="server">
<ItemTemplate>
<li>
<asp:HyperLink runat="server"
NavigateUrl='<%#(Container.DataItem as MyPage).GetUrl()%>'
Text="<%# (Container.DataItem as MyPage).GetName() %>"></asp:HyperLink>
<ul>
<asp:Repeater runat="server" DataSource="<%# (Container.DataItem as MyPage).GetChildren() %>">
<ItemTemplate>
<li>
<asp:HyperLink runat="server"
NavigateUrl='<%#(Container.DataItem as MyPage).GetUrl()%>'
Text="<%# (Container.DataItem as MyPage).GetName() %>"></asp:HyperLink>
</li>
</ItemTemplate>
</asp:Repeater>
</ul>
</ItemTemplate>
</asp:Repeater>
</ul>

Related

Set IDs of controls of a repeater

I have a repeater that contains a few controls and I want to set their ID's based on the IDs from database.
The datasource of the repeater is a list so basicly I want to do something like this in code behind, in repeater_ItemDataBound():
var myControl = e.Item.FindControl("controlID");
myControl.ClientIDMode = ClientIDMode.Static;
myControl.ID = e.Item.DataItem("ID"); //but I can't access the ID property, so here's my problem.
considering that I declared my repeater something like:
<asp:Repeater ID="repeater" runat="server">
<ItemTemplate>
<div class="someClass">
<asp:Label ID="controlID" runat="server"><%# Eval("Name")%></asp:Label>
<!-- list of other controls -->
</div>
</ItemTemplate>
</asp:Repeater>
Don't change ID's of controls that were already created with a different ID. That could cause nasty errors. Instead use the right ID in the first place. Or use whatever ID you use and assign the identifier to a different property like CommandArgument if it's a Button, Value if it's a HiddenField or Text if it's an (invisible) TextBox or Label.
So in this case you could use another control to store the ID:
<asp:HiddenField ID="hiddenID" runat="server" Value='<%# Eval("ID")%>' />
<asp:Label ID="lblName" runat="server"><%# Eval("Name")%></asp:Label>
Now, if you need the ID of the current item and you have the reference of the label or another control in that repeater-item:
var item = (RepeaterItem) lblName.NamingContainer;
HiddenField hiddenID = (HiddenField) item.FindControl("hiddenID");
string id = hiddenID.Value;

Moving repeater in form removes serverside declaration

I have two repeaters bound to object collections. They both work fine. But when I move repeater B to repeater A's FooterTemplate the serverside declaration of repeater B disappear.
This is not a parent/child relation.
Any ideas why this happens?
Update:
<asp:Repeater ID="myOuterRepeater" runat="server">
<ItemTemplate>
...
</ItemTemplate>
<FooterTemplate>
<asp:Repeater ID="myInnerRepeater" runat="server">
<ItemTemplate>
<asp:Button OnCommand="btnRemove_Click" ID="btnRemove" RunAt="server" />
<%#Eval("ItemId")%>
<%#String(Eval("Amount"))%>
<ItemTemplate>
</asp:Repeater>
</FooterTemplate>
</asp:Repeater>
When I bind:
myOuterRepeater.DataSource = myCollection
myOuterRepeater.DataBind()
Dim innerRepeater As Repeater =
myOuterRepeater.Controls(myOuterRepeater.Controls.Count - 1).Controls(0).FindControl("myInnerRepeater")
innerRepeater.DataSource = myInnerCollection
innerRepeater.DataBind()
When nesting a control (including a second repeater) inside a repeater, it is not longer part of the page but part of the enclosing repeater.
You will need to use FindControl(string) to get a reference to the nested repeater:
Repeater nested = enclosingRep.FindControl("nestedRepeaterId");

Changing WebControl ID Inside of a Repeater

<ItemTemplate>
<asp:Label runat="server"><%#DataBinder.Eval(Container.DataItem, "Question")%></asp:Label>
<asp:DropDownList runat="server" id="<%#DataBinder.Eval(Container.DataItem, "QuestionID")%>">>
<asp:ListItem value="1" text="Yes" />
<asp:ListItem value="0" text="No" />
</asp:DropDownList>
<ItemTemplate>
This is roughly what I'm trying to do. Obviously, the implementation is faulty, but I can't find any information on how I'd go about this in practice. Any help is appreciated.
Edit: What I'm trying to do exactly is add a DropDownList for each item in this Repeater, and upon submission of the form, use the ID's of each Yes/No answer to input into a database. The SqlDataReader that I'm using has two fields: The question content and the questionID.
I think you'd be better off using the built in support for IDs inside a Repeater. If the goal is to assign it an ID to make it easy to find the proper control after the data has been bound you might try something like this:
<asp:Repeater ID="Repeater1" runat="server>
<ItemTemplate>
<asp:Label ID="QuestionID" Visible="False" Runat="server"><%#DataBinder.Eval(Container.DataItem, "FieldContent")%></asp:Label>
<asp:DropDownList ID="MyDropDownList" Runat="server"></asp:DropDownList>
</ItemTemplate>
</asp:Repeater>
Then, in your code you can loop through the items in the Repeater until you find the label you're looking for:
foreach (RepeaterItem curItem in Repeater1.Items)
{
// Due to the way a Repeater works, these two controls are linked together. The questionID
// label that is found is in the same RepeaterItem as the DropDownList (and any other controls
// you might find using curRow.FindControl)
var questionID = curRow.FindControl("QuestionID") as Label;
var myDropDownList = curRow.FindControl("MyDropDownList") as DropDownList;
}
A Repeater basically consists of a collection of RepeaterItems. The RepeaterItems are specified using the ItemTemplate tag. Each RepeaterItem has its own set of controls that are, by the very nature of a Repeater, associated with each other.
Say you're pulling the Repeater data from a database. Each Repeater item represents data from an individual row in the query results. So if you assign the QuestionID to a label and the QuestionName to a DropDownList, the ID in the label would match up with the name in drop down.
Could you remove the control from the markup file, and hook the repeater's onItemDataBound event. In that event, you should be able to create the dropdown control "manually", assigning whatever ID you want.

ASP.NET: "Object Required" when repeating LinkButtons in an UpdatePanel

I have an UpdatePanel which has a Repeater repeating LinkButtons. When I click a LinkButton, the page does a partial postback, then I get a javascript error: "Object required". I tried debugging the javascript, but couldn't get a call stack. If I remove the UpdatePanel, the LinkButtons do a full postback, and they disappear from the page. How can I get this UpdatePanel to work?
<ajax:UpdatePanel ID="wrapperUpdatePanel" runat="server" UpdateMode="Always">
<ContentTemplate>
<asp:Repeater ID="endpointRepeater" runat="server" OnItemDataBound="EndpointDataBound">
<HeaderTemplate>
<div class="sideTabs">
<ul>
</HeaderTemplate>
<ItemTemplate>
<li>
<asp:LinkButton ID="endpointLink" runat="server" OnClick="EndpointSelected" />
</li>
</ItemTemplate>
<FooterTemplate>
</ul>
</div>
</FooterTemplate>
</asp:Repeater>
</ContentTemplate>
</ajax:UpdatePanel>
binding code:
protected void Page_Load(object sender, EventArgs e)
{
if (!this.IsPostBack)
{
this.SelectedEndpoint = Factory.Get<IEndpoint>(Enums.EndPoints.Marketing);
}
IEndpointCollection col = EndpointCollection.GetActivelySubscribingEndpointsForPart(this.Item);
if (this.Item.IsGdsnItem)
col.Add(Factory.Get<IEndpoint>(Enums.EndPoints.Gdsn));
if (col.Count > 0)
col.Insert(0, Factory.Get<IEndpoint>(Enums.EndPoints.Marketing));
this.endpointRepeater.DataSource = col;
this.endpointRepeater.DataBind();
if (this.endpointRepeater.Items.Count > 0)
{
LinkButton lb = this.endpointRepeater.Items[0].FindControl("endpointLink") as LinkButton;
this.EndpointSelected(lb, new EventArgs());
}
}
thanks,
mark
This may not be your main issue, but when including an object inside a Repeater that needs an event, you shouldn't be using that control's native events. Instead you should use the Repeater's OnCommand event.
If I were to guess, your problem is caused by the repeater not maintaining its DataBound state across PostBacks. The Linkbutton disappears from view because it is not bound to the page on every PostBack, so when the response is sent back to the client, it has nothing bound to it.
It sounds like the UpdatePanel is expecting the same (or similar) markup to be returned from the AJAX response as what is on the page already, so returning nothing for the repeater causes problems.
Try binding your repeater to the page/control in its OnInit() method. This should allow the ViewState for the repeater to be loaded on every PostBack.

Switching a Repeater's ItemTemplate at runtime

Is it possible to define multiple templates for a Repeater's ItemTemplate and switch between them according to some condition?
I use a repeater to view a list of posts but want to have a different view for rows that belong to the current user (e.g. contains a LinkButton for deleting the post)
If this is not possible, then is it possible to use a Multiview control inside a Repeater's ItemTemplate?
I tried to use a MultiView control inside the ItemTemplate and it worked very well, hope this helps someone with the same problem:
<asp:Repeater ID="Repeater1" runat="server" OnItemCommand="Repeater1_ItemCommand">
<ItemTemplate>
<asp:MultiView ID="MultiView1" runat="server" ActiveViewIndex="<%# ((Post)Container.DataItem).Member.ID == CurrentMemberID ? 1 : 0 %>">
<asp:View ID="View1" runat="server"><!-- some links --></asp:View>
<asp:View ID="View2" runat="server"><asp:LinkButton CommandName="DeletePost" CommandArgument="<%# ((Post)Container.DataItem).Id %>" ID="LinkButton1" runat="server">Delete Post</asp:LinkButton></asp:View>
</asp:MultiView>
</ItemTemplate>
</asp:Repeater>
I don't know if it's possible to switch between templates, but I've found the the Repeater.OnItemDataBound event most useful for modifying the display of individual repeater items.
For example, to show a link button based on the current user...
protected void repeater_ItemDataBound(object sender, RepeaterItemEventArgs e)
{
LinkButton = e.Item.FindControl("btnDelete");
LinkButton.Visible = (e.Item.DataItem as DataRow)["CreatedBy"] == getCurrentUser();
}
Generally I keep the layout of the data consistent for each repeater item and modify the visual appearance by altering the CssStyle and Visible properties of controls in the template. If there are more radical layout changes, I'll put each layout option inside a placeholder and use logic to determine which placeholder to show.

Resources