Injecting table row inside repeater item - asp.net

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.

Related

Data binding along with the value in ASP.NET

How do you achive something like below in asp.net?
<asp:Label runat="server" Text="The '<%# CustomValue %>' you assigned."/>
This usually depends on where your Label resides. If it is a stond alone control, not nested inside a repeater, you just set your code in code behind:
Label1.Text = $"The {CustomValue} you assigned.";
If the label is nested inside an ItemTemplate in some sort of Repeater control, you can strongly type it to an objects property:
<asp:Repeater runat="server" ID="MyRepeater" ItemType="WebFormsSandbox.Person">
<ItemTemplate>
<li>
<%#: Item.FirstName %> <%#: Item.LastName %>
</li>
</ItemTemplate>
</asp:Repeater>
and the corresponding code behind:
protected void Page_Load(object sender, EventArgs e)
{
MyRepeater.DataSource = Persons();
MyRepeater.DataBind();
}
IEnumerable<Person> Persons()
{
for (int i = 0; i < 10; i++)
{
yield return new Person { Id= i, FirstName = $"Foo{i}", LastName = $"Bar{i}" };
}
}
This would create a list of links, where you then could do anything with it. Whether this would pop up a custom window or does a postback .. up to you.
If you really want to do it that way, write the string inside the server tags.
<asp:Label runat="server" Text='<%# "The " + CustomValue + " you assigned." %>'/>
However if the Label is not inside a GridView, Repeater etc you have to call DataBind manually.
protected void Page_Load(object sender, EventArgs e)
{
DataBind();
}
You would typically do this in the code behind on page load, unless you were using a datagrid or repeater control. Assign an ID to your control and reference it like so.
<asp:Label runat="server" ID="Label1" />
protected void Page_Load(object sender,EventArgs e)
{
Label1.Text = "Your Value";
}

Number of items in result in ASP repeater with Sitecore

I'm working with Sitecore ascx file, and got something like this:
<asp:Repeater ID="NavRepeater" runat="server">
<ItemTemplate>
<div class="item">
<img src="<%# ((Sitecore.Data.Items.Item)Container.DataItem).Fields["Image Url"] %>" width="360" height="420" alt="">
</div>
</ItemTemplate>
<SeparatorTemplate></SeparatorTemplate>
</asp:Repeater>
Can't figure out, how to get total number of elements from Sitecore.Data.Items.Item.
I was also wondering how to retrieve given item in collection - like item no 2 ?
It's on the data source of your repeater in C#:
So if your C# is:
NavRepeater.DataSource = someData;
Then the length is someData.Length or someData.Count() based on its type.
For getting a specific item at a location:
Item second = someData[1]; // per 0-indexing
Or based on type, can also be:
Item second = someData.ElementAt(1) // per 0-indexing
You can have something like :
<asp:Repeater ID="NavRepeater" runat="server" OnItemDataBound="rptGallery_ItemDataBound">
<ItemTemplate>
<div class="item">
<sc:fieldrenderer id="fldGalleryItemImageThumbnail" runat="server" fieldname="ImageUrl" disablewebediting="true" />
</div>
</ItemTemplate>
<SeparatorTemplate></SeparatorTemplate>
</asp:Repeater>
On Page_Load you will have:
protected void Page_Load(object sender, EventArgs e)
{
rptGallery.DataSource = listofitemyyouwanttoshow;
rptGallery.DataBind();
}
On rptGallery_ItemDataBound event you will have:
protected void rptGallery_ItemDataBound(object sender, RepeaterItemEventArgs e)
{
if (e.Item.ItemType == ListItemType.Item || e.Item.ItemType == ListItemType.AlternatingItem)
{
Item item = (Item)e.Item.DataItem;
//you can check index here of item
if (item != null)
{
var fldGalleryItemImageThumbnail = e.Item.FindControl("fldGalleryItemImageThumbnail") as FieldRenderer;
if (fldGalleryItemImageThumbnail != null)
{
fldGalleryItemImageThumbnail.DataSource = item.ID.ToString();
fldGalleryItemImageThumbnail.Parameters = String.Format("width={0}", 360);
}
}
}
}
}
I hope it helps you .
You would get these from the enumerable that you are binding you repeater to.

How to render two column layout using an asp.net repeater

I have a list of items I want to render using a repeater, the twist is these items need to render in two columns in a table layout. Also if there are odd number of items the last cell will contain a place holder image. any thoughts?
Ex, my list [“string1”,”string2”,”string3”,”string4”,”string5”]
Display
<table>
<tr>
<td>string1</td>
<td>string2</td>
</tr>
<tr>
<td>string3</td>
<td>string4</td>
</tr>
<tr>
<td>string5</td>
<td>string6</td> or [<td>Some place holder Imge if odd number </td>]
</tr>
</table>
So that I end up with a view that looks like this
“String1” “String2”
“String3” “String4”
“String5” “String6”
Or if the list has odd number of Items
“String1” “String2”
“String3” “String4”
“String5” “Some place holder image”
We can render two columns in markup. Also we have to add a placeholder that we will use in code to insert image :
<asp:Repeater ID="rptMyRepeater" runat="server" OnItemDataBound="rptMyRepeater_ItemDataBound">
<HeaderTemplate>
<table>
<tr>
</HeaderTemplate>
<ItemTemplate>
<%# (Container.ItemIndex != 0 && Container.ItemIndex % 2 == 0) ? #"</tr><tr>" : string.Empty %>
<%# string.Format("{0}{1}{2}", #"<td>", Container.DataItem, #"</td>") %>
<asp:PlaceHolder ID="PlaceHolder1" runat="server"></asp:PlaceHolder>
</ItemTemplate>
<FooterTemplate>
</tr>
</table>
</FooterTemplate>
</asp:Repeater>
Now in the code check for last item and check if it is odd. If it is, add an image inside td, append the td in placeholder:
protected void rptMyRepeater_ItemDataBound(object sender, RepeaterItemEventArgs e)
{
if (e.Item.ItemType == ListItemType.Item || e.Item.ItemType == ListItemType.AlternatingItem)
{
int count = ((List<string>)rptMyRepeater.DataSource).Count;
if (e.Item.ItemIndex != 0 && e.Item.ItemIndex % 2 == 0 && e.Item.ItemIndex == count - 1)
{
PlaceHolder PlaceHolder1 = e.Item.FindControl("PlaceHolder1") as PlaceHolder;
Image img = new Image();
img.ImageUrl="pholder.jpg";
TableCell td = new TableCell();
td.Controls.Add(img);
PlaceHolder1.Controls.Add(td);
}
}
}
And the code I have used for testing:
protected void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack)
{
//rptMyRepeater.DataSource = new List<String>() { "String1", "String2", "String3", "String4", "String5", "String6" };
rptMyRepeater.DataSource = new List<String>() { "String1", "String2", "String3", "String4", "String5" };
rptMyRepeater.DataBind();
}
}
Hope it helps!

Repeater inside repeater not binding properly

I have a repeater inside another. Like so :
<asp:Repeater ID="CategoryRepeater" runat="server" OnItemDataBound="ItemBound">
<ItemTemplate>
<div class="groupbox">
<fieldset>
<legend><%# Container.DataItem %></legend>
<table>
<asp:Repeater ID="ItemRepeater" runat="server">
<ItemTemplate>
<tr>
<td>
<asp:CheckBox id="chkItem" runat="server" Text='<%# Eval("Text")%>' />
<asp:Button ID="btnXRefs" Text="x-refs" runat="server" CssClass="xRefButton" OnClick="btnSelectXRefs_Click" />
</td>
</tr>
</ItemTemplate>
</asp:Repeater>
</table>
</fieldset>
</div>
</ItemTemplate>
</asp:Repeater>
CODE BEHIND :
protected void Page_Load(object sender, EventArgs e)
{
if (!Page.IsPostBack)
{
using (var db = new DbContext())
{
CategoryRepeater.DataSource = db.Categories.OrderBy(x => x.Heading).Select(x => x.Heading).Distinct();
CategoryRepeater.DataBind();
}
}
}
protected void ItemBound(object sender, RepeaterItemEventArgs args)
{
if (args.Item.ItemType == ListItemType.Item)
{
Repeater childRepeater = (Repeater)args.Item.FindControl("ItemRepeater");
var item = args.Item as RepeaterItem;
using (var db = new DbContext())
{
childRepeater.DataSource = db.Categories.Where(x => x.Heading == item.DataItem).OrderBy(x => x.Text);
childRepeater.DataBind();
}
}
}
My goal is to first make many groupboxes using the top level, then bind items into each one. So I end up with many small stacked lists of checkboxes.
Problem is, all the top level boxes appear, yet only the first one contains checkbox items, ie. only the first one is bound internally, and the ItemBound method is only called once for the first one.
Any ideas why?
This line
if (args.Item.ItemType == ListItemType.Item)
Should be like this
if(args.Item.ItemType = ListItemType.Item ||
args.Item.ItemType == ListItemType.AlternatingItem)

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

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.

Resources