Why is my ASP.NET Repeater data not available in load events via code behind? - asp.net

I have an ASP.NET repeater that is being populated via a datasource. The data loads and presents perfectly on the screen, but all attempts to access any row of data in that repeater via code behind during the load phase are unsuccessful. I can access everything perfectly on a form submit, once the repeater has rendered on the screen, but in every load-related event I have tried (Load, Init, PreRender, DataBind, etc), it shows as empty. Is it necessary to populate the repeater programatically in code behind, rather than ASPX markup, in order to access this data during the load phase or am I just doing something wrong?
ASPX snippet:
<div runat="server" id="rpt3" style="display: block;">
<p class="approvalHeaderText">
Rejected Items</p>
<asp:Repeater ID="RptRejected" runat="server" DataSourceID="SQL_EmployeeGetRejectedEdits">
<ItemTemplate>
<tr class="gridToggleWhite gridVisible" id="<%# Eval("RecID") %>_cont">
<td>
<%# Eval("OrderID")%>
</td>
Code behind snippet - tried in several events (assume all variables have been declared):
For i = 0 To RptRejected.Items.Count - 1
'Obtain current week's Expenses
curExpVal = RptRejected.Items(i).FindControl("ExpensePay")
If Not IsNothing(curExpVal) Then
If curExpVal.Text = "" Then
insertExp = 0
Else
insertExp = CSng(curExpVal.Text)
End If
Else
insertExp = 0
End If
Next

You can use the ItemDataBound event (and you don't need the loop):
Sub rpt3_ItemDataBound(Sender As Object, e As RepeaterItemEventArgs)
If (e.Item.ItemType = ListItemType.Item) OrElse _
(e.Item.ItemType = ListItemType.AlternatingItem) Then
Dim curExpVal = e.Item.FindControl("ExpensePay")
If Not IsNothing(curExpVal) Then
If curExpVal.Text = "" Then
insertExp = 0
Else
insertExp = CSng(curExpVal.Text)
End If
Else
insertExp = 0
End If
End If
End Sub

R1_Load and R1_ItemDataBound should be used to populate your data and R1_ItemCommand should be used to handle the button actions.
aspx
<asp:Repeater runat="server" ID="R1" OnItemCommand="R1_ItemCommand" OnItemDataBound="R1_ItemDataBound"
OnLoad="R1_Load">
<HeaderTemplate>
<table>
</HeaderTemplate>
<ItemTemplate>
<tr>
<td>
<asp:TextBox runat="server" ID="txt"></asp:TextBox><asp:Button runat="server" ID="btn"
Text="Add" CommandName="AddOne" />
</td>
</tr>
</ItemTemplate>
<FooterTemplate>
</table> </FooterTemplate>
</asp:Repeater>
aspx.cs
protected void R1_Load(object sender, EventArgs e)
{
if (!Page.IsPostBack)
{
var items = new List<int>();
for (int i = 0; i < 5; i++)
{
items.Add(i);
}
R1.DataSource = items;
R1.DataBind();
}
}
protected void R1_ItemDataBound(object sender, RepeaterItemEventArgs e)
{
if (e.Item.ItemType == ListItemType.Item || e.Item.ItemType == ListItemType.AlternatingItem)
{
var txt = e.Item.FindControl("txt") as TextBox;
txt.Text = Convert.ToString(e.Item.DataItem);
}
}
protected void R1_ItemCommand(object source, RepeaterCommandEventArgs e)
{
if (e.CommandName == "AddOne")
{
var txt = e.Item.FindControl("txt") as TextBox;
txt.Text = (Convert.ToInt32(txt.Text) + 1).ToString();
}
}

My key mistake was using Eval() instead of the DataBinder.Eval() method. While the result is the same on the screen, Eval() does not trip the OnItemDataBound event. Once I switched my markup over to DataBinder.Eval(), everything fell into place. Thanks, everyone, for your ideas.

Related

How do I display child footer of a repeater if the child repeater is empty?

I have two repeaters nested in my application which is working fine. I would love to display the footer if the child repeater is empty. Due to my code my long html, i will just drop a sample of what my html looks like and post my full code for better understanding. Everything works fine though unless when the child repeater is empty i want to display the footer message
<asp:Repeater ID="ProductRepeater" runat="server" OnItemDataBound="ProductRepeater_ItemDataBound">
<HeaderTemplate></HeaderTemplate>
<ItemTemplate>
<table>
<tr>
<td>
<%#Eval("Name")%>
</td>
<tr>
</table>
<pre>
<asp:Repeater ID="ChildRepeater" runat="server">
<HeaderTemplate></HeaderTemplate>
<ItemTemplate>
<table>
<tr>
<td>
<%#Eval("Description")%>
<td>
</tr>
<table>
</itemTemplate>
<FooterTemplate>
<div>
<h5>
<asp:Label ID="lblDefaultMessage" runat="server" Text="This is empty. " Visible="false" ForeColor="Red" Font-Size="Large">
</asp:Label>
</h5>
</div>
</FooterTemplate>
</asp:Repeater>
</pre>
</ItemTemplate>
</asp:Repeater>
<protected void ProductRepeater_ItemDataBound(object sender, RepeaterItemEventArgs e)
{
if(e.Item.ItemType == ListItemType.Item || e.Item.ItemType == ListItemType.AlternatingItem)
{
string category = "Value"
Repeater rptRelated = e.Item.FindControl("ChildRepeater") as Repeater;
rptRelated.DataSource = getChild(category);
rptRelated.DataBind();
if (rptRelated.Items.Count < 1)
{
if (e.Item.ItemType == ListItemType.Footer)
{
Label lblDefaultMessage = (Label)e.Item.FindControl("lblDefaultMessage");
lblDefaultMessage.Visible = true;
}
}
}
}
Repeater has no EmptyDataTemplate like GridView.
You should place your footer content next to Repeater and change your "databinding" code like this:
Repeater rptRelated = e.Item.FindControl("ChildRepeater") as Repeater;
Label lblDefaultMessage = (Label)e.Item.FindControl("lblDefaultMessage");
var ds = getChild(category);
lblDefaultMessage.Visible = ds != null && ds.Rows.Count != 0;
if (rptRelated != null)
{
rptRelated.DataSource = ds;
rptRelated.DataBind();
}
Find Repeater like:
// find Child Repeater
Control ctrl = (sender) as Control;
Repeater rptRelated = ctrl.Parent.NamingContainer as Repeater;
And Find control from Repeater's Footer like:
Label lblDefaultMessage =
(Label)rptRelated.Controls[rptRelated.Controls.Count-1].FindControl("lblDefaultMessage");
Add this into child Repeater's OnItemDataBound event instead of parent.
protected void ChildRepeater_ItemDataBound(object sender, RepeaterItemEventArgs e)
{
Control ctrl = (sender) as Control;
Repeater rptRelated = ctrl.Parent.NamingContainer as Repeater;
if (rptRelated.Items.Count < 1)
{
if (e.Item.ItemType == ListItemType.Footer)
{
Label lblDefaultMessage = (Label)rptRelated.Controls[rptRelated.Controls.Count-1].FindControl("lblDefaultMessage");
lblDefaultMessage.Visible = true;
}
}
}
Event is:
<asp:Repeater ID="ChildRepeater" runat="server"
OnItemDataBound="ChildRepeater_ItemDataBound">

Injecting table row inside repeater item

I am trying to inject inside repeaters items, the problem is that the row is generated in a wrong location.
If you try this simple example, you will see that the rown is generated under label '2' as it should be generated under label '1'.
Why does it happen? And how to fix that?
protected void Page_Load(object sender, EventArgs e)
{
int [] array = {1,2,3,4,5};
rpt.DataSource = array;
rpt.DataBind();
}
protected string _extraRowHtml;
protected void rpt_ItemDataBound(object sender, RepeaterItemEventArgs e)
{
if (e.Item.ItemType == ListItemType.Item ||
e.Item.ItemType == ListItemType.AlternatingItem)
{
int tmp = ((int)e.Item.DataItem);
_extraRowHtml = tmp == 1 ? "<tr><td class=\"topicsRow\" colspan=\"100%\"> Topics </td></tr>" : string.Empty; ;
}
}
<asp:Repeater ID="rpt" runat="server" onitemdatabound="rpt_ItemDataBound">
<HeaderTemplate>
<table cellpadding="0px" cellspacing="0">
</HeaderTemplate>
<ItemTemplate>
<tr style="height:50px">
<td>
<asp:HyperLink ID="lnkTitle" runat="server" Text='<%# Container.DataItem%>'/>
</td>
</tr>
<%# _extraRowHtml %>
</ItemTemplate>
<FooterTempl
</table>
</FooterTemplate>
</asp:Repeater>
Personally I think a better way of doing is is to replace:
<%# _extraRowHtml %>
with
<%# GetExtraRow(Container.DataItem) %>
then in your code behind implement:
protected string GetExtraRow(object Data)
{
int tmp = (int) Data:
return _tmp == 1 ?
"<tr><td class=\"topicsRow\" colspan=\"100%\"> Topics </td></tr>" :
string.Empty;
}
Then and get rid of the rpt_ItemDataBound function (and the onItemDataBound).
Doing things this way is going to be prone to error, as using a variable in this manner is not something that you are going to have full control over.
What I would do is add something like the following to the template.
<asp:literal id="litExtraRow" runat="server" mode="Passthrough" />
Then in your codebehind on the item databound event
if (((int)e.Item.DataItem) == 1)
{
((Literal)e.Item.FindControl("litExtraRow")).Text = "Your HTML Here";
}
Something like this should be a bit more reliable.
The reason you are having issues is that the template is evaluated with the values as they are as "ItemDataBound" has been called, and row 1, the value is string.Empty, and then for the second row, you updated it after item 1 was databound.

Accessing asp controls within a datalist

How do you access an asp control within a datalist. For example, I would like to, but currently cannot, access the HyperLink control or the ImageButton control by inline code, or in the code-behind file.
<asp:DataList ID="DataList1" runat="server" AlternatingItemStyle-CssClass="altArtStyle">
<HeaderTemplate>
<table>
<tr>
<td>
<asp:HyperLink ID="lnkTitle" runat="server" NavigateUrl="Default.aspx?order_by=title&direction=ASC" >
Title
</asp:HyperLink> <asp:ImageButton id="imgbtnTitle" src="/_images/hover-down.gif" runat="server"/>
</td>
</tr>
</table>
</HeaderTemplate>
Generally, you need to call FindControl on the DataListItem object, in order to find the control on the specific row. In your example, FindControl will only work on a header row, as in the following example:
Protected Sub DataList1_ItemDataBound(ByVal sender As Object, ByVal e As System.Web.UI.WebControls.DataListItemEventArgs) Handles DataList1.ItemDataBound
If e.Item.ItemType = ListItemType.Header Then
Dim btn As ImageButton = e.Item.FindControl("imgbtnTitle")
If btn IsNot Nothing Then
' Do stuff here.
End If
End If
End Sub
same you can do with labels and hyperlinks
private void DataList1_ItemDataBound(object sender, DataListItemEventArgs e)
{
if ((e.Item.ItemType == ListItemType.Item) || (e.Item.ItemType == ListItemType.AlternatingItem))
{
ImageButton imgbutton = (ImageButton)e.Item.FindControl("imgbtnTitle");
imgbutton.ToolTip = "abc";
}
}
It depends. For example, if you wanted to change the header at runtime, in one of the object bind events, you'd do something like for this datalist header, do a findcontrol on the hyperlink and with that reference, do this...
Yes, you can access the asp controls inside the datalist by using the Datalist Item Data bound
protected void DataList1_ItemDataBound(object sender, DataListItemEventArgs e)
{
if (e.Item.ItemType == ListItemType.Item || e.Item.ItemType == ListItemType.AlternatingItem)
{
HyperLink TitleLink = (HyperLink)e.Item.FindControl("lnkTitle");
}
}

Change an ASP.NET control's properties in a Repeater control

My question is fairly simple. This is what I have for the aspx page:
<ul>
<asp:Repeater runat="server" ID="linksList" OnItemDataBound="linksList_OnItemDataBound" >
<ItemTemplate>
<li><asp:HyperLink runat="server" ID="link" /></li>
</ItemTemplate>
</asp:Repeater>
</ul>
I'm trying to get a list of hyperlinks from a SQL server into a list. This is what I have in the codebehind:
protected void Page_Load(object sender, EventArgs e)
{
DataSet ds = Utilities.RunSelectQuery("SELECT *");
DataTable dt = ds.Tables[0];
linksList.DataSource = dt;
linksList.DataBind();
}
How do I change the NavigateUrl and Text properties in the asp:HyperLink after data's been bound to the Repeater? I want to do this in the codebehind, I can get it to work if I do it using <%# Eval("URL") %> in the aspx page but that's sort of against what ASP.NET is all about.
Edit: this is the solution that worked for me thanks to womp:
protected void linksList_OnItemDataBound(object sender, RepeaterItemEventArgs e)
{
if (e.Item.ItemType == ListItemType.Item || e.Item.ItemType == ListItemType.AlternatingItem)
{
DataRowView row = (DataRowView)e.Item.DataItem;
HyperLink link = (HyperLink)e.Item.FindControl("link");
link.Text = row["description"].ToString();
link.NavigateUrl = row["URL"].ToString();
}
}
Actually, using the Databinder syntax in your templates is a great way to do it, I'm not sure what you mean that it's "against what ASP.Net is all about".
However, if you really want to do it in code, you can do it right in your OnItemDataBound handler (which it looks like you've created). Something like this (read: untested) should do the trick:
void linksList_OnItemDataBound(object Sender, RepeaterItemEventArgs e)
{
if (e.Item.ItemType == ListItemType.Item
|| e.Item.ItemType == ListItemType.AlternatingItem) {
DataRow row = e.Item.DataItem as DataRow;
Hyperlink link = e.Item.FindControl("link"));
link.Text = row["URL"];
link.NavigateUrl = row["URL"];
}
}
}
The way to do it is just as you said. Doing it in the code behind adds unnecessary work if you only wish to display the URL. You also would want to put the tags in the HeaderTemplate and FooterTemplate.
<asp:Repeater runat="server" ID="linksList" OnItemDataBound="linksList_OnItemDataBound" >
<HeaderTemplate><ul></HeaderTemplate>
<ItemTemplate>
<li><asp:HyperLink runat="server" ID="link" NavigateUrl='<%# Eval("url") %>' /></li>
</ItemTemplate>
<FooterTemplate></ul></FooterTemplate>
</asp:Repeater>

How can I obtain a reference to a control created inside a repeater?

I have one control named thumbviewer inside repeater. I want to set its imageurl in
code. Currently it's done in aspx itself as
<asp:Repeater ID="Repeater1" runat="server" >
<ItemTemplate>
<span style="padding:2px 10px 2px 10px">
<bri:ThumbViewer Id="Th1" runat="server" ImageUrl='<%# Eval("Name", "images/{0}") %>' Height="100px" Width="100px"/>
</span>
</ItemTemplate>
</asp:Repeater>
How can i set ImageUrl in code?
protected void rpter_ItemDataBound(object sender, RepeaterItemEventArgs e)
{
if (e.Item.ItemType == ListItemType.Item || e.Item.ItemType == ListItemType.AlternatingItem)
{
ThumbViewControl control = e.Item.FindControl("Th1") as ThumbViewControl;
if (control != null)
{
control.ImageUrl = "";
}
}
}
and on the aspx
<asp:Repeater ID="Repeater1" runat="server" OnItemDataBound="rpter_ItemDataBound" >
<ItemTemplate>
<span style="padding:2px 10px 2px 10px">
<bri:ThumbViewer Id="Th1" runat="server" Height="100px" Width="100px"/>
</span>
</ItemTemplate>
</asp:Repeater>
Is how I would personally do it.
If you wish to get the data for it, at that point, I believe e.Item.DataItem (or something similar) get its.
Cheers,
T
Your repeater has a onitemdatabound event.
<asp:Repeater ID="Repeater1" runat="server" onitemdatabound="Repeater1_ItemDataBound"></asp:Repeater>
In your code behind you can have an Event handler called
protected void Repeater1_ItemDataBound(object sender, RepeaterItemEventArgs e)
{
// ensure that we only look in the item template for our control
if( e.Item.ItemType == ListItemType.Item)
{
ThumbViewer tv = (ThumbViewer)e.Item.FindControl("Th1");
tv.ImageUrl = "images/"+((<The object type you are binding>)e.Item.DataItem).Name;
}
}
This is the most orthodox way to access data being bound to a repeater. IMHO
HTH
You need to find the repeater and then look through the controls:
I do a similar thing here and load a control into a placeholder inside of a repeater..
if (ResultRepeater != null && ResultRepeater.HasControls())
{
foreach (Control oControl in ResultRepeater.Controls)
{
if (oControl != null && oControl is RepeaterItem)
{
PlaceHolder oSuggestMorePlaceholder = (PlaceHolder) oControl.FindControl("SuggestMorePlaceholder");
if (oSuggestMorePlaceholder != null)
{
SuggestMoreTabbedControl oTabbedControl = (SuggestMoreTabbedControl) Page.LoadControl("controls/SuggestMoreControl.ascx");
if (oTabbedControl != null)
{
oSuggestMorePlaceholder.Controls.Add(oTabbedControl);
}
}
}
}
}

Resources