I have a link button in a repeater with a couple of databound fields. I'm trying to get to where I can set the buttons onClientClick after the databinding however everytime I try to access the link button I keep getting Null returned.
I've looked through every single question involving repeaters and controls here and haven't been able to figure it out.
The .aspx
<asp:Repeater ID="DailyRepeater" OnItemCommand="DailyRepeater_ItemCommand" runat="server">
<HeaderTemplate>
<tr>
<td class="coltitle">
Time
</td>
<td class="coltitle">
Activity
</td>
<td class="coltitle">
Hours
</td>
</tr>
</HeaderTemplate>
<ItemTemplate>
<tr class="evenrow">
<td>
<%# Eval("StartTime","{0:HH:mm}") %>-<%# Eval("EndTime","{0:HH:mm}") %>
</td>
<td>
<%# Eval("Description") %>
</td>
<td>
<%# Eval("Hours","{0:0.0}") %>
</td>
<td>
<asp:LinkButton runat="server" CausesValidation="false" ID="editbutton" Text="Edit">Edit</asp:LinkButton>
</td>
</tr>
</ItemTemplate>
The .aspx.cs
protected void DailyRepeater_ItemDataBound(object sender, RepeaterItemEventArgs e)
{
LinkButton myButton = (LinkButton)e.Item.FindControl("editbutton");
myButton.OnClientClick = (popupWindow.GetTargetPopupCode("URL");
}
From what I've read, I should have been able to get to the button using the RepeaterItemEventargs. However I can't seem to find it here. The other thing I thought of was that the binding wasn't happening by the time this happened (which made no sense to be as this is a databound event) but for some reason e is coming back e.Item has a dataItem of null and an itemIndex of -1...
I'm just really confused and lost any help would be greatly appreciated.
Thanks!
It sounds like you haven't guarded against the item type. Typically, it fires the header, all the items, and then the footer. You need to do this:
if(e.Item.ItemType == ItemType.Item || e.Item.ItemType == ItemType.AlternatingItem)
{
LinkButton myButton = (LinkButton)e.Item.FindControl("editbutton");
myButton.OnClientClick = (popupWindow.GetTargetPopupCode("URL");
}
Why not setting the property on the sender object, like this:
sender.OnClientClick = //whatever//
Related
I have 8 items in a SQL table with a Description, ItemNumber, and ImagePath.
protected void Page_Load(object sender, EventArgs e)
{
//fill the datalist for the Signs Table
string tCallFrom = "Program." + System.Reflection.MethodBase.GetCurrentMethod().Name;
string tQry = #"SELECT * FROM [js_Signs]";
DataTable tblSigns = objCommMethods.fReturnTableFromQry(clsDBSelect.enumDBSelect.ProjectMaster, tQry, tCallFrom);
SignsList.DataSource = tblSigns;
SignsList.DataBind();
}
I am using a DataList to show all of the items along with a textbox that will allow a user to input the number of items they want:
<asp:DataList ID="SignsList" runat="server" RepeatColumns="4" CellPadding="2" Width="90%" ItemStyle-HorizontalAlign="Center">
<ItemTemplate>
<table>
<tr>
<th>
<%#DataBinder.Eval(Container.DataItem, "Description") %>
</th>
</tr>
<tr>
<th>
<%#DataBinder.Eval(Container.DataItem, "ItemNumber") %>
</th>
</tr>
<tr>
<td>
<asp:Image ID="SignImage" runat="server" ImageUrl='<%#DataBinder.Eval(Container.DataItem, "ImagePath") %>' />
</td>
</tr>
<tr style="text-align: center;">
<td>
<asp:TextBox ID="txtSignQuantity" runat="server" CssClass="txtBox" BackColor="White" Enabled="true"
type="number" min="0" ToolTip="Please choose quantity of signs needed."/>
</td>
</tr>
<tr>
<td>
<p> </p>
</td>
</tr>
</table>
</ItemTemplate>
</asp:DataList>
<div class="btnGroup">
<div class="btnDiv">
<asp:Button ID="btnSave" runat="server" Text="Save" CssClass="btnSave" Width="90px" Visible="true" OnClick="btnSave_Click"/>
</div>
<div class="btnDiv">
<asp:Button ID="btnCancel" runat="server" Text="Clear" CssClass="btnCancel" Width="90px" Visible="true" OnClick="btnCancel_Click"/>
</div>
</div>
After the saved button is clicked in C# I want to find out the value that a user entered but it is always returning null:
protected void btnSave_Click(object sender, EventArgs e)
{
TextBox txtSignQuantity;
string tempSignQuantity;
try
{
foreach (DataListItem item in SignsList.Items)
{
txtSignQuantity = item.FindControl("txtSignQuantity") as TextBox;
tempSignQuantity = txtSignQuantity.Text;
}
}
catch (Exception ex)
{
Console.WriteLine(ex);
}
}
I also checked and the Count of SignsList.Items was 8, so I know that it is retrieving the correct information. Sorry, I've never used a Data List before so I'm not really sure how to go about this...
The short answer is that data binding on postback is causing the problem. Check the Page.IsPostBack property before doing data binding:
protected void Page_Load(object sender, EventArgs e)
{
if (!Page.IsPostBack)
{
//fill the datalist for the Signs Table
string tCallFrom = "Program." + System.Reflection.MethodBase.GetCurrentMethod().Name;
string tQry = #"SELECT * FROM [js_Signs]";
DataTable tblSigns = objCommMethods.fReturnTableFromQry(clsDBSelect.enumDBSelect.ProjectMaster, tQry, tCallFrom);
SignsList.DataSource = tblSigns;
SignsList.DataBind();
}
}
ASP.NET WebForms attempts to abstract away the fact that the control instances that were used to render the page are not the same instances it has when handling postback events. Data binding compounds the abstraction because it has to create controls in response to the DataBind call, and then on postback, recreate them based on the ViewState it saved.
Initializing controls from ViewState happens on the Init event, so when the Load event is fired later in the page lifecycle and you call DataBind on the control, everything it restored gets wiped out and recreated.
As for why you were getting null, it may have been that the controls were wiped out but not recreated; it may have had to do with other event handlers that didn't get rewired after the second data binding.
I have an ASP.NET WebForms project that displays data read from a database via a Repeater. The code for the repeater looks like this:
<asp:Repeater ID="repRMAproduct" runat="server">
<ItemTemplate>
<tr>
<td>
<%# Eval("Description") %>
</td>
<td>
<%# Eval("Qty") %>
</td>
<td>
<asp:TextBox ID="tbNewQty" runat="server"></asp:TextBox>
</td>
</tr>
</ItemTemplate>
</asp:Repeater>
What I want is for the user to enter a new Quantity only on the rows that he wants to enter. Then, when he clicks the save button, I can read these new Quantities along with their associated Descriptions.
In MVC I should build the URL using jQuery and pass it to the Controller. But in WebForms it seems less intuitive. Where do I go from here?
In your save-button-click event handler you can loop all items and get the TextBox with FindControl:
foreach(RepeaterItem item in repRMAproduct.Items)
{
TextBox tbNewQty = (TextBox) item.FindControl("tbNewQty");
string newQuantity = tbNewQty.Text.Trim();
int quantity;
if(int.TryParse(newQuantity, out quantity))
{
SaveNewRmaQuantity(product, quantity);
};
}
If you'd use a Label for your product-description you can use FindControl("LblDescriptionId") to get it and to pass it's Text to SaveNewRmaQuantity. You can also use invisible controls(Visible=false), f.e. a Label, to store the ProductId.
I've got this code in the code-behind on my page, which works perfectly fine for a repeater:
protected void AcctAssnRepeater_ItemCommand(object source, RepeaterCommandEventArgs e)
{
SqlConnection con = new SqlConnection(ConfigurationManager.ConnectionStrings["PBRConnectionString"].ConnectionString);
con.Open();
{
string str10 = ((HtmlInputText)e.Item.FindControl("txtFlgUpdatedOn")).Value;
}
}
I'm trying to do something similar on another page, but it is telling me that "item" isn't valid:
protected void btnSubmit_OnClick(object sender, EventArgs e)
{
string str10 = ((HtmlInputText)e.Item.FindControl("txtDtSentToCIS")).Value;
}
I'm still a C# n00b, can anyone tell me how to reference the value inside this control? Both pages have a runat="server" on the aspx side, so I would expect that there's a way to do it, I'm sure my syntax just needs adjusting.
Thanks!
EDIT: Here's a piece of the aspx. Maybe I should note that it's all inside a Tab Control.
<div style="width:430px;border:1px solid blue;float:left;">
<asp:Panel ID="Panel3" runat="server" Height="220px" style="float:left; margin-left: 19px"
Width="410px">
<table>
<tr>
<td width="210px">BIL Name:</td>
<td width="200px"><asp:textbox id="txtCISName" runat="server"></asp:textbox></td>
</tr>
<tr>
<td width="210px">Date Sent To BIL:</td>
<td width="200px"><input type="text" id="txtDtSentToCIS" class="datepicker" name="txtDtSentToCIS" runat="server" style="height: 14px; width: 70px" /></td>
</tr>
<tr>
<td width="210px">BIL Sign Off Received:</td>
<td width="200px"><asp:DropDownList ID="cboCISSignOff" runat="server" Height="16px"
AutoPostBack="True" onselectedindexchanged="chkCISSignOff_CheckedChanged">
<asp:ListItem>N</asp:ListItem>
<asp:ListItem>Y</asp:ListItem>
</asp:DropDownList></td>
</tr>
<tr>
<td width="210px"><asp:Label runat="server" Text="BIL Response:" ID="CISResp" /></td>
<td width="200px"><asp:textbox id="txtCISResponse" runat="server"
textmode="MultiLine" rows="9" Width="180px"></asp:textbox></td>
</tr>
</table>
</asp:Panel>
</div>
You should be able to use the ID to reach the control. For ex. a textbox:
<asp:TextBox ID="txtDtSentToCIS" runat="server" />
In your code behind you can do something as
SomeMethod(this.txtDtSentToCIS.Text);
or
string enteredByUser = this.txtDtSentToCIS.Text;
If you want to access repeater containing textbox(es) from outside the repeater, you may code like this:
Iterating the RepeaterItemCollection:
foreach (RepeaterItem item in Repeater1.Items)
{
TextBox txtDtSentToCIS = item.FindControl("txtDtSentToCIS") as TextBox;
}
Or simply accessing through indexer:
TextBox txtDtSentToCIS = Repeater1.Items[1].FindControl("txtDtSentToCIS") as TextBox;
Good luck.
If you are using asp I would suggest that you use an asp Text box.
<asp:TextBox ID="textBoxName" runat="server" />
When the button is clicked and you OnClick method is called you can search for the text box by name and assign the text to your string.
string str10 = textBoxName.text;
You don't really need to use the find control unless that control is buried in another control like a grid view or login view.
What could be happening is that the FindControl method only works for the first control collection in this case the only control contained is "Panel3", try this:
var panel = e.Item.FindControl("Panel3") as Panel;
string str10 = ((HtmlInputText)panel.FindControl("txtFlgUpdatedOn")).Value;
Good luck
I have a sales simulator written in C#. I did not write this however I am currently modifying it to suit different requirements.
It basically displays a list of products from a database, and has a textbox for each product where you can enter in the quantity sold. The quantity sold is calculated based on it's price in the database.
Now it's all working fine, however there is one SMALL issue. When "Buy" is clicked, it returns me back to the list of products which IS correct, however the quantity I have entered for the previous product remains.
I would like to know how to restore the text boxes to their default value when the after the data is submitted to the database.
I always thought the way to do this would be in the .cs code behind file
txtQuantity.Text = "";
However, for some odd reason, txtQuantity will not show up.
Can anyone think of anything I am doing wrong? Here is a snippet of code from the aspx file.
<form id="form1" runat="server">
Date:
<asp:TextBox ID="txtDate" runat="server" />
Retailer:
<asp:DropDownList ID="dlStore" runat="server"
onselectedindexchanged="dlStore_SelectedIndexChanged" />
<asp:ListView ID="lbProducts" runat="server">
<LayoutTemplate>
<layouttemplate>
<table border="1" cellpadding="1" style="width:800px">
<tr style="background-color:#E5E5FE">
<th>ID</th>
<th>ProductCode</th>
<th>Product Title</th>
<th>RRP $</th>
<th>Quantity</th>
<th>Sale Price $</th>
</tr>
<tr id="itemPlaceholder" runat="server"></tr>
</table>
</layouttemplate>
</LayoutTemplate>
<ItemTemplate>
<tr>
<td style="width: 50px;">
<%#Eval("ProductID") %>
</td>
<td>
<%#Eval("ProductCode") %>
</td>
<td>
<%#Eval("ProductTitle") %>
</td>
<td>
<%#Eval("USListPrice") %>
</td>
<td style="width: 50px;">
<asp:TextBox ID="txtQuantity" runat="server" Text="0" />
</td>
<td style="width: 50px;">
<asp:TextBox ID="txtSalePrice" runat="server" Text="0.00" />
</td>
</tr>
</ItemTemplate>
</asp:ListView>
<asp:Button ID="btnBuy" Text="Buy These Items" runat="server" OnClick="btnBuy_Click" />
<asp:Button ID="btnClear" Text="Clear Existing Sales" runat="server"
onclick="btnClear_Click" />
</form>
There's a lot going on in the code behind file and I wouldn't expect anyone to go through it, but how I collect the data from txtQuantity is done with the following line of code:
Int32 quantity = Int32.Parse(((TextBox)item.FindControl("txtQuantity")).Text);
So what I want to be able to do is set this textbox either to be empty, or back to zero.
Any help is appreciated, thanks.
Because that txtQuantity control is within a ListView, there could be any number of instances of that control generated. So you can't access all of them through a single variable.
You will need to look through all controls within that ListView (and several levels deep) to find all your txtQuantity controls.
The same of course for the txtSalePrice control.
EDIT
You could find those textboxes with code like (untested)
public IEnumerable<TextBox> FindTextBoxes(Control parent)
{
if (parent == null) yield break;
foreach (Control child in parent.Controls)
{
TextBox tb = child as TextBox;
if (tb != null)
yield return tb; // found one!
else
foreach(TextBox tb in FindTextBoxes(child))
yield return tb; // found it deeper
}
}
and call it like:
foreach(TextBox tb in FindTextBoxes(lbProducts)
{
if (tb.Name == "txtQuantity")
{
// found a quantity
}
else if (tb.Name == "txtSalePrice")
{
// found the salesprice
}
}
I have a repeater control:
<table style="width: 100%">
<asp:Repeater runat="server" ID="rptOptions" OnItemDataBound="rptOptions_ItemDataBound">
<HeaderTemplate>
<tr>
<td class="GridHeader">Account</td>
<td class="GridHeader">Margin</td>
<td class="GridHeader">Symbol</td>
<td class="GridHeader">Price</td>
</tr>
</HeaderTemplate>
<ItemTemplate>
<tr>
<td class="GridRow"><asp:Label runat="server" ID="lblOptionAccount"></asp:Label></td>
<td class="GridRow"><asp:Label runat="server" ID="lblOptionMargin"></asp:Label></td>
<td class="GridRow"><asp:Label runat="server" ID="lblOptionSymbol"></asp:Label></td>
<td class="GridRow"><asp:Label runat="server" ID="lblOptionPrice"></asp:Label></td>
</tr>
</ItemTemplate>
</asp:Repeater>
</table>
And the following code-behind databound method:
protected void rptOptions_ItemDataBound(object sender, RepeaterItemEventArgs e)
{
if (e.Item.ItemType == ListItemType.Item || e.Item.ItemType == ListItemType.AlternatingItem)
{
Option rowOption = (Option)e.Item.DataItem;
((Label)e.Item.FindControl("lblOptionAccount")).Text = rowOption.Account;
((Label)e.Item.FindControl("lblOptionMargin")).Text = rowOption.Margin ? "Y" : "N";
((Label)e.Item.FindControl("lblOptionSymbol")).Text = rowOption.Symbol;
((Label)e.Item.FindControl("lblOptionPrice")).Text = rowOption.Price.ToString("C", currencyFormat);
}
}
There are more columns in that grid, but I've slimmed it down just for the question.
Now, what I would like to do is change the tr's background color based on the price amount. If it is within different levels, I would like to change the rows background color correspondingly.
Do I have to do this with javascript or is there some way I can get access to the table rows in the code-behind to set this color?
make it to runat="Server"
<tr runat="server" ID="trHeader"></tr>
Then find that Table Row in by ID in your code behind in databound event, like what you are doing for other server side control and change color.
another option:
<tr class="<%# GetClassForPrice( Container.DataItem ) %>">
and in code-behind
protected string GetClassForPrice( object data )
{
var rowOption = (Option)data;
if(rowOption.Price > 100) return "red";
else return "green";
}
also... any reason you're not using data binding? it would let you eliminate your ItemDataBound code-behind method.
<tr>
<td class="GridRow"><%# Eval("Account") %></td>
<td class="GridRow"><%# ((bool)Eval("Margin")) ? "Y" : "N" %></td>
<td class="GridRow"><%# Eval("Symbol") %></td>
<td class="GridRow"><%# Eval("Price", "{0:c}") %></td>
</tr>
use this code inside repeter databound event.
HtmlTableRow tr = (HtmlTableRow)e.Item.FindControl("trID");
tr.Visible = false;
Another option you can use is jQuery. Seeing you're using a repeater, that gives you full control over your output. Jquery can get into your table, look at the data and format it how you want it.
Look at:
http://plugins.jquery.com/project/Plugins/category/54
http://plugins.jquery.com/project/Colorize
http://franca.exofire.net/js/demo_cross.html
Alternately you can use the Code behind in the repeater to set the CSS Class of the cells/rows/columns you need changed.
You'll need to make those controls server controls (require: ID="my_thing" runat="server"), create those controls (use the find control and bind them), then set the CSS class after you determine the value.
Dave Thieben's answer works nicely, except that the css is missing.
If you add the following then the example will work:
table tr.red td { background-color:red; }
table tr.green td { background-color:green; }