Adding <tr> from repeater's ItemDataBound Event - asp.net

My repeater's templates generate a table, where each item is a table row.
When a very very specific condition is met (itemdata), I want to add an additional row to the table from this event.
How can I do that?
protected void rptData_ItemDataBound(object sender, RepeaterItemEventArgs e)
{
if (e.Item.ItemType == ListItemType.Item ||
e.Item.ItemType == ListItemType.AlternatingItem)
{
bool tmp = bool.Parse(DataBinder.Eval(e.Item.DataItem, "somedata").ToString());
if (!tmp && e.Item.ItemIndex != 0)
{
//Add row after this item
}
}
}
I can use e.Item.Controls.Add() and add TableRow but for that I need to locate a table right?
How can I solve that?

I would put in the control in the repeater item template, set its visibility to hidden & only in that case, I will show it...
Like:
<asp:Repeater ...>
..
<ItemTemplate>
<tr>
<td>...</td>
</tr>
<tr runat="server" id="tr_condition" Visible="false">
<td> Show only in specific condition </td>
</tr>
Then in the ItemDataBound event:
var tr_condition = e.Item.FindControl("tr_condition") as HtmlTableRow;
tr_condition.Visible = myCondition;
where myCondition is a flag which gets sets on the specific condition
HTH

One way to do this is to include the row in the template code, and set the Visible property based on your 'tmp' variable. The you don't need to 'add' it - it is already there.
Edit: another idea - insert your extra row using a <%# %> data binding block. To do this, you need to generate the <tr><td>.. code as a atring in your ItemDataBound event. Use a protected string variable - like this:
protected string _extraRowHtml;
in ItemDataBound:
_extraRowHtml = tmp ? "<tr><td> -- generated content -- </td></tr>" : string.Empty;
in ItemTemplate:
<tr><td> -- regular row content --</td></tr>
<%# _extraRowHtml %>

Related

Computed variable for every repeater row shows up in the next row

I have a protected variable and used in a repeater itemtemplate, <%# Bonus %> which is calculated in the repeater's OnItemDataBound event based on other columns' values in the same row.
The problem is that the Bonus value for a row is showing in the next row. So for row #1, the value is blank. The second row shows the bonus for row #1, third row shows bonus value for second row and so on. The bound values are showing fine. It's the computed value showing in the wrong row.
It seems in the OnItemDataBound event the computed value is calculated but for some reason, the value is not used in the rendering of the current row but in the next row.
What am I doing wrong?
Updated
Simplified sample code:
.......
protected Decimal Bonus;
.....
(then inside repeater OnItemDataBound handler)
.....
DataRowView row = (DataRowView) e.Item.DataItem;
Bonus = Convert.ToInt32(row["salary"]) * .01;
in ASPx:
.....
<asp:Repeater runat="server">
<ItemTemplate>
<td><%# Eval("salary") %> <== will show correct value
</td>
<td><#% Bonus %> <== doesn't show the bonus computed for that row from OnItemDataBound handler. Shows up in the next repeater row.
</td>
<ItemTemplate>
OnItemDataBound occurs after the row is bound.
All the values of the current row are present in the parameters to the function.
If you need to set a new value, since the repeater is not going to bind the values again after this function, you should set the text of the control yourself.
void myRepeater_ItemDataBound(Object Sender, RepeaterItemEventArgs e) {
// This event is raised for the header, the footer, separators, and items.
// Execute the following logic for Items and Alternating Items.
if (e.Item.ItemType == ListItemType.Item || e.Item.ItemType == ListItemType.AlternatingItem) {
if (((Evaluation)e.Item.DataItem).Rating == "Good") {
((Label)e.Item.FindControl("BonusLabel")).Text= Bonus.ToString();
}
}
}
You can bind to a method that performs the calculation:
code-behind
public string CalculateBonus(object salaryValue)
{
int salary;
string bonus;
if (Int32.TryParse(salaryValue.ToString(), out salary))
{
bonus = (salary * .01).ToString();
}
else
{
bonus = "N/A"; //or whatever default value you want to return
}
return bonus;
}
aspx page
<td><%# CalculateBonus(Eval("Salary")) %></td>

How to get the total count of rows in a repeater?

I am using a Repeater control to display data, The data source contains multiple records
which are displayed in the repeater, what i need is to get the difference of two list items and assign it to another list item, I have used the below mentioned code works fine for the first record but doesn't display all the records
aspx.cs Code
DataSet dsJobCardHistory =new DataSet();
double dcPriceIncl;
double dcPriceExcl;
double dcTax;
protected void Job_History()
{
dsJobCardHistory = objReportManager.Get_JobCard_History(strCustNo, strTranId);
dcPriceIncl = Convert.ToDouble(dsJobCardHistory.Tables[0].Rows[0][6].ToString());
dcTax = dcPriceIncl * 0.14;
dcPriceExcl = dcPriceIncl - dcTax;
}
protected void repJobCard_ItemDataBound(object sender, RepeaterItemEventArgs e)
{
if (e.Item.ItemType == ListItemType.Item)
{
((Label)e.Item.FindControl("lblRepPrice")).Text =
Convert.ToString(dcPriceExcl);
((Label)e.Item.FindControl("lblRepTax")).Text =
Convert.ToString(dcTax);
((Label)e.Item.FindControl("lblRepTotal")).Text=
Convert.ToString(dcPriceIncl);
}
}
}
aspx code
<font style="font-size:14px"><strong><asp:Label ID="lblRepPrice" runat="server"
Text=""></asp:Label></strong></font>
</td>
<td align="center" width="15%">
<font style="font-size:14px"><strong><asp:Label ID="lblRepTax" runat="server"
Text=""></asp:Label></strong></font>
</td>
<td align="center" width="15%">
<font style="font-size:14px"><strong><asp:Label ID="lblRepTotal" runat="server"
Text=""></asp:Label></strong></font>
</td>
As u said its working for first time only so Replace Your this line
if (e.Item.ItemType == ListItemType.Item)
with....
if (e.Item.ItemType == ListItemType.Item || e.Item.ItemType==ListItemType.AlternatingItem)
Give the [xp:repeat] an ID value e.g. idvaluestring
in SSJS uses getComponent("idvalueString").getRowCount()
The Class of the RepeatControl is:
com.ibm.xsp.component.xp.XspDataIterator
<xp:repeat var="voyageChoicesCollection"
indexVar="voyageChoicesCollectionIndex"
value="#{ProfileListRepeat}"
id="voyageChoicesCollectionID">
var vccID:com.ibm.xsp.component.xp.XspDataIterator = getComponent("voyageChoicesCollectionID");
vccID.getRowCount()

Adding CSS Class on RowDataBound

I'm trying to append a CSS class to a row on RowDataBound. I'm using the alternating css class property against the GridView, so I'm assuming this is applied on RowDataBound. If you assign a CSS class programatically to the CssClass property of the row within the RowDataBound event then the alternating row style css class is not applied, even if you append the CSS class.
Here's what I've got:
Protected Sub gvGeneral_RowDataBound(ByVal sender As Object, ByVal e As System.Web.UI.WebControls.GridViewRowEventArgs) Handles gvGeneral.RowDataBound
If e.Row.RowType = DataControlRowType.DataRow Then
If previousExpenseType <> e.Row.Cells(2).Text And previousExpenseType.Length > 0 Then
e.Row.CssClass += "bottom-border"
End If
previousExpenseType = e.Row.Cells(2).Text
End If
End Sub
previousExpenseType is just a String which I'm doing comparisons against. If the expense type changes, then I want to have a border applied to the bottom of the <tr> element.
Any ideas how to get around this? It seems there's an event after RowDataBound that's applying the alternating row style css class to the row.
try this in your RowDataBound event handler...
protected void GridView1_RowDataBound(object sender, GridViewRowEventArgs e)
{
GridView grid = GridView1;
GridViewRow row = e.Row;
if (row.RowType == DataControlRowType.DataRow)
{
bool isAlternating = row.RowState == DataControlRowState.Alternate;
List<string> classes = new List<string>();
/* Setting the CssClass of a row overwrites any CssClass declared in the
* markup, so lets save the value that is in the markup and add to it.
*/
if (isAlternating) { classes.Add(grid.AlternatingRowStyle.CssClass); }
else { classes.Add(grid.RowStyle.CssClass); }
//
//logic for adding other css classes to the row...
//
//set the CssClass property of the row to the combined css classes value
row.CssClass = string.Join(" ", classes.ToArray());
//
//more row processing...
//
}
}
Another work around you may want to try is to perform your checks and CSS Class manipulation after all the rows are databound by using one of the later gridview events and looping through the rows yourself. I am aware that this would add some extra processing time, but depending on the size of the datasets your are displaying, it could be worth it.
Gridview rowdatabound allows you ONLY to manipulate the HTML within a TR so you probably do not have an option to do what you want to do with gridView. Instead i would recommend to use Repeater which gives you much more control over the HTML you generate. here is what you can do with the repeater
<table>
<thead>
<tr><td>Head1</td><td>Head2</td></tr>
</thead>
<asp:Repeater ID="rptTaskItems" runat="server">
<ItemTemplate>
<tr><td>column 1 data</td>
<td>column 1 data</td>
</tr>
<asp:PlaceHolder ID="PHBar" runat="server" visible="false">
<tr><td colspan="2"><hr/></td>
</tr>
</asp:PlaceHolder>
</ItemTemplate>
</asp:Repeater>
</table>
In the ItemDataBound, you can make this placeholder [PHBar] visible ONLY when your condition is true and thats it.
Hope this helps.

asp:repeater - headers at section change

is there any way to bring up a subheader row at changes in a field in an databound asp:repeater control e.g.
Instead of
country | colour | number
uk | red | 3
uk | green | 3
france | red 3
Do this:
==UK==
colour | number
red | 3
green 3
==FRANCE==
colour | number
red | 3
Many thanks for any help.
There's no built-in support, but that doesn't mean it's impossible.
You'll need to over-ride the OnItemDataBound event, and have something like this in markup:
<asp:Repeater OnItemDataBound="NextItem" ... >
<ItemTemplate><asp:Literal Id="Header" Visible="False" Text="{0}<strong>{1}</strong><br/><table>" ... />
<tr>
<td><asp:Label id="Color" Text="<%# Eval("Color")" ... /></td>
<td><asp:Label id="Number" Text="<%# Eval("Number")" ... /></td>
</tr>
</ItemTemplate>
</asp:Repeater></table>
Then in the code-behind:
private string CurCountry = string.Empty;
private void NextItem(object sender, RepeaterItemEventARgs e)
{
if ( e.Item.ItemType != ListItemType.Item
&& e.Item.ItemType != ListItemType.AlternatingItem) return;
DbDataRecord row = (DbDataRecord)e.Item.DataItem;
if (CurCountry != row["country"].ToString() )
{
string prev = (CurCounter == string.Empty)?"":"</table>";
CurCountry = row["country"].ToString();
Literal header = (Literal)e.Item.FindControl("Header");
Literal footer = (Literal)e.Item.FindControl("Footer");
header.Text = string.Format(header.Text, prev, CurCountry);
header.Visible = true;
}
}
Another solution is to simply use two repeaters, one nested within the other. You can pass your groups with the child records to the first repeater, and on the ItemDataBound of the groups repeater, pass the child records to the child repeater and call DataBind() there.
This is more code but does actually give you more control over the layout without having HTML creation code in your code-behind.
As you can see here, we have a parent repeater and in the item template we can customise each group as we see fit. In the ChildRepeater, we have our item template in which we can customise each item inside the grouping. Very clean and all with declarative UI.
<asp:Repeater runat="server" id="GroupRepeater">
<ItemTemplate>
<asp:Literal runat="server" id="HeaderText" />
<asp:Repeater runat="server id="ChildRepeater">
<ItemTemplate>
<asp:Literal runat="server" id="InfoGoesHere" />
</ItemTemplate>
</asp:Repeater>
</ItemTemplate>
</asp:Repeater>
In the code behind we can have something like this:
private void GroupRepeater_ItemDataBound(object sender, RepeaterItemEventArgs e)
{
//Get the child records, this can be any data structure you want
SomeChildCollection children = ((SomeGroupCollection)e.Item.DataItem).Children;
//Find the child repeater
Repeater childRepeater = e.Item.FindControl("ChildRepeater") as Repeater;
childRepeater.ItemDataBound += SomeMethod;
childRepeater.DataSource = children;
childRepeater.DataBind();
}
After binding each child you can subscribe to the ItemDataBound event and do the child binding to controls as you see fit.
To joel Solution, I may advice following - change this line:
header.Text = string.Format("<strong> {0} </strong><br/><table>", CurCountry);
to
header.Text = string.Format(header.Text, CurCountry);
And than you can customize look & feel from .as?x file.
Ah - you're after a grouping repeater. The solution that I've adapted in the past seems to have disappeared. Maybe you can find it. But anyway, a search for grouping repeaters should help.
There seem to be a few bugs in the dotnetjunkies code but I've found a much simpler solution here http://www.west-wind.com/weblog/posts/140753.aspx
Now repeaters don't support grouping, so you have to do this on your own, but it's pretty easy to do. In the repeater above the following data expression is responsible for the grouping:
<%# this.RenderGroup(Eval("Group") as
string) %>
The expression calls a method on the form to render an extra div tag. The code for this simple method looks like this:
string LastGroup = "###~";
protected string RenderGroup(string Group) {
if (Group == this.LastGroup)
return ""; // *** Group has changed
this.LastGroup = Group;
return "<div class='groupheader'>" +Group + "</div>";
}
This code gets called for every item and it simply checks to see if the group has changed. If it hasn't nothing is returned otherwise a simple div tag is returned as a string to embed into the markup. I chose to send back the HTML but I suppose you could also return true or false and conditionally render a control in the Repeater which would make the CSS Nazis happier .

Nested listview databinding - property not found?

I have a listview, where I set the datasource in the code-behind - this works fine.
When I add another listview (or databound control) to the listview's itemtemplate, and set the datasource for that control in the codebehind, the fields returned by the query seem to be unavailable to the nested listview; ASP.NET throws the following error: DataBinding: 'System.String' does not contain a property with the name 'j_Name'.
In the example below, d_Description works fine, whereas j_Role throws the error indicated above. I can see the data being returned by the query, and I know the column names match, so what's causing the error (and how do I solve it)?
ASPX page
<asp:ListView ID="LV1" runat="server">
<LayoutTemplate>
<table runat="server" id="tblSummary">
<tr runat="server" id="itemPlaceholder" />
</table>
</LayoutTemplate>
<ItemTemplate>
<tr>
<td>
<%#Eval("d_Description")%>
</td>
</tr>
<tr>
<td>
<asp:ListView ID="LV2" runat="server">
<ItemTemplate>
<%#Eval("j_Role")%>
</ItemTemplate>
<LayoutTemplate>
<asp:placeholder id="itemPlaceholder" runat="server" />
</LayoutTemplate>
</asp:ListView>
</td>
</tr>
</ItemTemplate>
</asp:ListView>
CODE BEHIND
var qry1 = from q in context.Descriptions select q.d_Description;
LV1.DataSource = qualificationQry;
LV1.DataBind();
var qry2 = from q in context.Roles select q.j_Role;
LV2.DataSource = qualificationQry;
LV2.DataBind();
EDIT:
I've added code similar to that below to the ItemDataBound event of the outer listview, and I am still encountering the same error. Presumably I am misunderstanding the instructon?
protected void LV_ItemDataBound(object sender, ListViewItemEventArgs e)
{
using (dbDataContext context = new dbDataContext()
{
var qry2 = from q in context.Roles select q.j_Role;
ListView tempLV = (ListView)e.Item.FindControl("LV2");
tempLV.DataSource = qry2;
tempLV.DataBind();
}
}
EDIT: 2
After reading around the web some more (now that I have an idea of what to search for) the proposed answer seems to be correct - however, it's not working - can anyone suggest why?
EDIT: 3
If I ditch the outputting of j_Name, and just have a hardcoded string, there is no error, and the hard-coded string ouputs the expected number of times. This would indicate that it's just the column name (j_Name) that is wrong - even though I can see the dataset coming back from the query with that exact column name.
EDIT: 4
Fixed it.
This is wrong
var qry2 = from q in context.Roles select q.j_Role;
This is correct
var qry2 = from q in context.Roles select q;
You need to bind the inner list on every row of the outer list in the ItemDataBoundEvent for the outer list.
So this code:
var qry2 = from q in context.Roles select q.j_Role;
LV2.DataSource = qualificationQry;
LV2.DataBind();
will need to go in an even handler for the outer lists ItemDataBound event:
protected void ContactsListView_ItemDataBound(object sender, ListViewItemEventArgs e)
{
if (e.Item.ItemType == ListViewItemType.DataItem)
{
// Bind the inner list on every repeat of the outer list
var qry2 = from q in context.Roles select q.j_Role;
LV2.DataSource = qualificationQry;
LV2.DataBind();
}
}
And presumably you'll want to filter the values for the inner list based on a value from the current item of the outer list

Resources