This question is unlikely to help any future visitors; it is only relevant to a small geographic area, a specific moment in time, or an extraordinarily narrow situation that is not generally applicable to the worldwide audience of the internet. For help making this question more broadly applicable, visit the help center.
Closed 10 years ago.
I am trying to create:
A form that loads with two text boxes ("item" and "amount") and an "add" button.
Every time the "add" button is clicked, another set of text boxes identical to the first is added.
The text boxes need to retain whatever has been entered in them when the "add" button is clicked.
What is the best way to do this? I have been trying a lot of different things but none are working quite right.
I've had to do something like this a couple times both on win forms and on a web page. Unfortunately on a web page its not as easy as it is on win forms. I'm sure there are JQuery implementations out there but the way that I've done it in the past is using a repeater.
The handling of the item command event is a little tricky but I'll try to slim it down as best I can. A little background on this particular project was that I was coming up with an interface where people could edit the links that would be on their webpage. The original contains a lot more controls in the repeater but I slimmed it down so it wouldn't take up too much space.
First, declaring the repeater is easy enough:
<asp:Repeater ID="rptLinks" runat="server">
<HeaderTemplate>
<table style="width:99%; text-align:center; margin-left:auto; margin-right:auto"><tr>
<td style="width:15%;"><asp:Label ID="lblDisplayName" runat="server" Text="Display Name:"></asp:Label></td>
</tr>
</HeaderTemplate>
<ItemTemplate>
<tr>
<td style="width:15%;"><asp:TextBox ID="txtLinkDisplayName" runat="server" Width="95%" Text='<%#Bind("Name") %>'></asp:TextBox></td>
<td style="width:5%;"><asp:Button ID="btnLinkSave" runat="server" Text="" CssClass="linkButton" style="background-image:url('Images/Save_16.png')" ToolTip="Update Link" CommandName="Update" CommandArgument='<%#Eval("Customer_Link_ID") %>' /></td>
<td style="width:5%;"><asp:Button ID="btnLinkClear" runat="server" Text="" CssClass="linkButton" style="background-image:url('Images/Cancel_16.png')" ToolTip="Delete Link" CommandName="Delete" CommandArgument='<%#Eval("Customer_Link_ID") %>'/></td>
</tr>
</ItemTemplate>
<FooterTemplate>
<tr>
<td style="width:15%;"><asp:TextBox ID="txtLinkDisplayName" runat="server" Width="95%"></asp:TextBox></td>
<td style="width:5%;"><asp:Button ID="btnLinkNew" runat="server" Text="" CssClass="linkButton" style="background-image:url('Images/Add_16.png')" ToolTip="Save New Link" CommandName="New" /></td>
<td style="width:5%;"><asp:Button ID="btnLinkCancel" runat="server" Text="" CssClass="linkButton" style="background-image:url('Images/close_16.png')" ToolTip="Clear" CommandName="Clear" /></td>
</tr>
</table>
</FooterTemplate>
</asp:Repeater>
Basically all this does is create a template for the repeater. The header portion starts the table and contains a single label. The item template is what is used to display the data that is bound to the repeater. The text in this case is bound using server side includes and the value in the "Bind" function needs to match exactly to the column in the table being bound to the repeater. The footer in this case is where your new entries will be typed in and also closes the table tag. The template and the footer both contain 2 buttons. Its important to note that the CommandName value is set accordingly because that will be used later on on the server side.
So once the client side is all set up we will need to handle the item command event on the server. I set my function up like this:
Private Sub rptLinks_ItemCommand(ByVal source As Object, ByVal e As System.Web.UI.WebControls.RepeaterCommandEventArgs) Handles rptLinks.ItemCommand
Try
'This clears the datasource because if I didn't do this I was getting weird behavior.
rptLinks.DataSource = Nothing
rptLinks.DataBind()
Select Case e.CommandName
Case "New"
'Do whatever you need to for a new record
Case "Update"
'Do whatever you need to to update the record
Case "Clear"
'Clear the controls
Case "Delete"
'Delete the link that is in this row
Case Else
End Select
'rebind the repeater to the updated data.
rptLinks.DataSource = LinksDatatable
rptLinks.DataBind()
Catch ex As Exception
End Try
End Sub
This is the easiest way I found to determine which button was clicked but I'm sure there are others ways to accomplish it.
Now once you get into the case that you need to handle you will eventually need to get the values from the controls and the way that you do that is by: e.Item.FindControl("txtLinkDisplayName"). In my case I used that code inside a directcast() call and cast the controls as needed to make them easier to work with.
So once all that is in place, databound and you handle each case then you should be ready to go.
Related
I'm under the impression that a control in a nested UpdatePanel will cause the top level UpdatePanel to refresh (thus refreshing both UpdatePanels) because any events on that control act as an "implicit" trigger. Is that correct?
I've been trying to wire something like this up-
UserControl
Parent UpdatePanel
"Show" button
ASP:Panel
Dynamically added UserControls, each with UpdatePanels
When the Show button is clicked, the ASP:Panel becomes visible and starts adding UserControls to itself dynamically, based on some back-end logic.
Each of the dynamically added controls (henceforth: UserControls) have their own Atlas-enabled buttons and links, so they have UpdatePanels too. Currently when I click on a link in one of the UserControls, the entire contents of the ASP:Panel disappear, as if it's being re-rendered. All of my dynamically-added controls disappear, and none of their click events are caught in the debugger.
I'm assuming what's happening here is that controls that reside in nested update panels are causing the parent UpdatePanel to post back because they're firing "implicit" triggers. Is there a way for my UserControls to operate autonomously and not mess with the ASP:Panel that contains them?
If not, what strategy should I be pursuing here? If I have to re-render the entire ASP:Panel every time an event happens on one of the (possibly many) UserControls, that means I'll have to recreate the UserControls, which take a bit of effort to create. I'll also have to preserve some kind of view state to recreate them. I'm somewhat new to ASP.NET and that sounds intimidating. I'd rather never refresh the top leve UserControl and ASP:Panel if I can avoid it, and let each of the dynamically-added UserControls fire and handle their own events asynchronously.
EDIT: Instead of adding the controls dynamically, I added them to the markup(not a bad solution). So got rid of the controls disappearing problem, because now the controls are not added dynamically but instead exist in the markup. But still the parent UpdatePanel posting is a big performance hit, because all the UserControls are getting posted instead of one. How do I make only one UserControl postback? Also, I would like to know how to get rid of the problem of controls disappearing if added dynamically?
First off, keep in mind: UpdatePanels do not alter the lifecycle of a page.
All of the Control Tree (including the UpdatePanels) must be reconstructed just as with a Normal Postback Life-cycle.1 2 The UpdatePanels ensure that only a portion of the rendered (HTML) view is returned. Removing all UpdatePanels should result in the same behavior, except with a full postback. For instance, only the HTML representing a nested UpdatePanel (presumably because data changed) might be sent back in the XHR response.
To get "true" AJAX consider Page Methods. Alternatively, DevExpress (and perhaps Telerik and others?) offer their own form of "Callback Panels", which are similar to UpdatePanels, but can bypass parts of the life-cycle (and, as a result often do not support the ViewState model entirely or may introduce their own quirks).
While not understanding the above is the most likely reason for the controls "disappearing", here is my rule: Do Not Let [Nested] UpdatePanels Work "Automatically".
Edge cases with dynamic controls and nested UpdatePanels will be encountered. There might be a nice way to handle this, but I have failed on multiple different attempts.
Instead, for every update panel, run with:
UpdateMode="Conditional"
ChildrenAsTriggers="False"
With the "Conditional" UpdateMode, make sure to manually specify the Trigger control or call panel.Update() (although this hard-wires the Control) as required. Depending on needs ChildrenAsTriggers="True" might work as well. The big thing is that UpdateMode is not "Always" which is the default.
After switching to this approach, I have no problem -- well, almost none -- with nested UpdatePanels.
Happy coding!
1 If the page doesn't render correctly where Partial Rendering (in the ScriptManager) is disabled (e.g. all requests are full postbacks) then there is no reason to expect/believe it will work correctly with UpdatePanels.
2 There are times when it's warranted to "cheat" expensive recomputations in the control tree for controls that will not be re-rendered. However, I would consider these advanced cases that should only be done when performance analysis indicates there is a specific need.
I had a similar issue with a heavy ajax Gridview control, and a HTML page with multiple UpdatePanels, some nested, some not.
It took me over 3 weeks of trial and error, reading, testing, debugging and prototyping to finally resolve all the post backs and partial postbacks.
However I did notice a pattern, which worked for me.
Like the #user above's response, make sure your UpdatePanels are set Conditional and only specificy asp:PostBackTrigger on the final or decisive control that you want the page to do a full postback on. I always use asp:AsyncPostBackTrigger where ever possible.
So for example, if you're using UpdatePanels inside a GridView and you want a popup inside a cell of the gridview's row, then you need to use a nested UpdatePanel.
ie
<asp:TemplateField HeaderText="Actions & Communications">
<ItemTemplate>
<asp:UpdatePanel ID="upAction1" runat="server" UpdateMode="Conditional">
<Triggers>
<asp:AsyncPostBackTrigger ControlID="btnActionOK" />
</Triggers>
<ContentTemplate>
...
...
<ajaxToolkit:ModalPopupExtender ID="ajaxMPE" runat="server"
BackgroundCssClass="modalBackground"
PopupControlID="upAction2"
TargetControlID="btnADDaction">
</ajaxToolkit:ModalPopupExtender>
<asp:UpdatePanel ID="upAction2" runat="server" UpdateMode="Conditional">
<ContentTemplate>
<asp:Panel ID="pnlACTION" runat="server" CssClass="pnlACTION">
<div id="divHDR">
<asp:Label ID="lblActionHdr" runat="server">** header **</asp:Label>
</div>
<div id="divBOD">
<table style="width: 98%; text-align:left">
<tr>
<td colspan="2">
Please enter action item:<br />
<asp:TextBox ID="txtAction" runat="server" ValidationGroup="vgAction" TextMode="MultiLine" Rows="3" Width="98%"></asp:TextBox>
<asp:RequiredFieldValidator ID="rfvAction" runat="server" ControlToValidate="txtAction" ValidationGroup="vgAction"
ErrorMessage="* Required" SetFocusOnError="True"></asp:RequiredFieldValidator></td>
</tr>
<tr>
<td colspan="2">
Select staff assigned to this task:<br />
<asp:DropDownList ID="ddlActionStaff" runat="server" AppendDataBoundItems="true" ValidationGroup="vgAction" />
<asp:RequiredFieldValidator ID="rfvStaff" runat="server" ControlToValidate="ddlActionStaff" InitialValue="0" ValidationGroup="vgAction"
ErrorMessage="* Required" SetFocusOnError="True" Width="98%"></asp:RequiredFieldValidator></td>
</tr>
<tr>
<td colspan="2" style="text-align: center">
<asp:Button ID="btnActionOK" runat="server" Text="OK" OnClick="btnActionOK_Click" ValidationGroup="vgAction" CausesValidation="false" />
<asp:Button ID="btnActionCANCEL" runat="server" Text="CANCEL" OnClick="btnActionCANCEL_Click" ValidationGroup="vgAction" CausesValidation="false" />
</td>
</tr>
</table>
</div>
</asp:Panel>
</ContentTemplate>
</asp:UpdatePanel>
</td>
</tr>
</table>
<asp:SqlDataSource ID="sdsTETactions" runat="server" ConnectionString="<%$ ConnectionStrings:ATCNTV1ConnectionString %>"
...
...
</asp:SqlDataSource>
</ContentTemplate>
</asp:UpdatePanel>
</ItemTemplate>
<ItemStyle HorizontalAlign="Center" />
</asp:TemplateField>
Notice a few things here:
The ModalPopupExtender does not refer to the Ok and Cancel buttons. This is because I want them to trigger a partial postback to the server (code-behind)
The nested UpdatePanel (upAction2) is purely to force a partial postback on that panel only (pnlAction)! Therefore this forces the data validation triggers to work and keep the panel open if the user does not provide data in the requiredfield validators
The main UpdatePanel (upAction1) controls the entire lot and keeps all postbacks held within that panel only and NOT affecting the gridview (not shown entirely).
There is a great article here, which explains it better.
I hope this helps someone
I have just started using RadControls so this question might be basic for you, I am using a List View which I am populating using Sql Data Source, I also have insert functionality in that List View but the problem is the insert template just disappear after adding one record and I have to refresh the page to make insert template appear again, am I doing something wrong ?
I have another question regarding Rad List View, is it possible that we use a drop down box instead of a text box inside insert template ? because sometimes you want to restrict the users to pick from predefined values instead of letting them enter anything ? I have tried putting drop down box inside insert template using code view of visual studio and it also appears properly when I run the page but the problem is data binding is not working, I have tried using
SelectedValue=<%# Bind("field_name") %>
as it was used in case of textbox like
Text=<%# Bind("field_name") %)>
but it does not work for some reason.
Please advice,
Thanks.
Ok I got it, maybe it will help someone.
<tr>
<td>
<asp:Label ID="DEPARTMENTLabel2" runat="server"
AssociatedControlID="DEPARTMENTTextBox" Text="DEPARTMENT"></asp:Label>
</td>
<td>
<asp:DropDownList ID="DEPARTMENTTextBox" runat="server" SelectedValue='<%# Bind("DEPARTMENT") %>'>
<asp:ListItem Text="Admin" Value="Admin">Admin</asp:ListItem>
<asp:ListItem Text="Editing" Value="Editing">Editing</asp:ListItem>
<asp:ListItem Text="Sales and Support" Value="Sales and Support">Sales and Support</asp:ListItem>
<asp:ListItem Text="Writing" Value="Writing">Writing</asp:ListItem>
</asp:DropDownList>
</td>
</tr>
Change the default textbox to a dropdown and assign its ID to AssociatedControlID of label that is representing it, here I used the same ID which was originally assigned to it "DEPARTMENTTextBox" (just to be careful), you can change it to "DEPARTMENTDropDown" or any ID you like but make sure you make these changes everywhere in the RADListView so that it performs normally.
As for the insert template disappearing after adding one record, I made a workaround and placed a button for "add another record" such that when it is clicked it calls this code.
protected void btnAddAnother_Click(object sender, EventArgs e)
{
RLVUsers.ShowInsertItem(RadListViewInsertItemPosition.LastItem);
}
This button make insert template reappear and allow the user to add another record.
I'm trying to add a "panel" with controls dynamicaly. Something like the code below, Clicking "Add" button, displays a "Panel" with the controls(which are within a table), the number of "Education" someone can enter is unlimited. I know how to capture the data, but i can't figure out how to implement/code this,Can anyone please give me some pointers for the same?
Should i be reading about AJAX? I don't know anything about AJAX..
<asp:Panel ID="Panel1" runat="server">
<table>
<tr>
<td><asp:TextBox ID="CollUniv_Name" runat="server"></asp:TextBox></td>
<td><asp:TextBox ID="CollUniv_Location" runat="server"></asp:TextBox></td>
</tr>
<tr>
<td><asp:TextBox ID="StartDateText" runat="server"></asp:TextBox></td>
<td><asp:TextBox ID="EndDateText" runat="server"></asp:TextBox></td>
</tr>
</table>
Seems like this would more naturally handled by a repeating control. Something along the lines of a ListView control.
There's a fairly full example in the MSDN page I linked to so I won't repeat it here. The two parts you'll want to take a look at a bit more closely are the ItemTemplate which lets you define what normal items look like and the InsertItemTemplate which lets you specify what your insert row will look like. You can also specify if the InsertTemplate appears at the top or bottom. In the code-behind you'll subscribe to the ItemInserting even and handle user input from there.
Basics:
I have a text box (txtDepositAmount) that people can
enter a deposit amount into and a
drop down (ddlSelectedTerm) that sets the terms.
Through these two values I calculate
the APY (lblCurrentApy).
Rules:
If only one of the values is selected I still want to do an update on the current APY label and clear it.
If either value changes I want to update the current APY and recalculate.
The problem:
As soon as I click away from the textbox and onto the drop down to select my term the drop down flashes and closes because the textbox TextChanged event was just fired.
I have to click on the drop down a second time to be able to select anything!
Do I need to change what event I'm looking at or do I need to move some of the controls outside of the UpdatePanel? Can this only happen if some of the business rules change? Should I just give up and go to javascript?
<asp:UpdatePanel ID="UpdatePanel1" runat="server">
<ContentTemplate>
<table width="100%">
<tr>
<td align="left" style="width: 10%" class="LineAlign">
</td>
<td align="left" style="width: 40%" class="LineAlign">
<asp:Label ID="lblDollarSymbol" runat="server" Text="$"/>
<asp:TextBox ID="txtDepositAmount" runat="server"
AutoPostBack="true" TabIndex="1" MaxLength="14"
ontextchanged="txtDepositAmount_TextChanged"/>
</td>
<td align="left" style="width: 30%" class="LineAlign">
<asp:DropDownList ID="ddlSelectedTerm" runat="server"
AutoPostBack="true" TabIndex="2"
onselectedindexchanged="ddlSelectedTerm_SelectedIndexChanged">
</asp:DropDownList>
</td>
<td align="center" style="width: 20%">
<asp:Label ID="lblCurrentApy" runat="server"/>
<asp:Label ID="lblPercentSymbol" runat="server" Text="%"/>
</td>
</tr>
</table>
</ContentTemplate>
<Triggers>
<asp:AsyncPostBackTrigger ControlID="ddlSelectedTerm" />
<asp:AsyncPostBackTrigger ControlID="txtDepositAmount" />
</Triggers>
</asp:UpdatePanel>
autopostback="true" on your textbox is causing this, since both controls are reloaded after they leave the textbox. I doubt there is a way to avoid this as long as they are in the same UpdatePanel.
Maybe you could set focus to the dropdown list after the textbox-initiated postback, or you could probably make this work by putting the controls in separate UpdatePanels. But really it seems the wrong way to go about it. I would use javascript (and ajax if the logic is complicated) to update the APY. Or just add a "calculate" button...
(Edit)
Here's a slightly ugly hack to avoid ajax, three update panels and still be able to do your logic server side. I do not approve of this method but it is quick and dirty.
Put the Label control that contains the calculation results in an UpdatePanel. The input controls do not need to be in an UpdatePanel.
In the update panel (with the results) have a hidden submit button:
<asp:Button ID="DoCalculate" style="display:none;" UseSubmitBehavior="false"
runat="server" OnClick="Recalculate()" />
This should be the trigger for the UpdatePanel. Then have your two input controls click that button using javascript to cause a partial postback to the "results" panel. Here is the code to add this javascript for the dropdown, for example, in Page_Load:
ddSelectedTerm.Attributes.Add("onSelectedIndexChanged",
"document.getElementByID('" + DoCalculate.ClientID + "').Click()");
Then in put the C# code to do the calculation and update the label in the 2nd update panel in the "Recalculate()" method.
This should work, and give you better layout control and less code flow ugliness then using 3 update panels.
You can just use the Focus() in the server event. For example if you have txtBox1 and txtBox2 and want to have the focus in the box 2 after write something in box 1 and both of them inside an update panel.
Only go to the TextChange event of the box 1 and write txtBox2.Focus() and after the update of the panel the focus will be in the box 2. :)
Hope helps...
I ripped out the UpdatePanel and just did jquery. Screw Microsoft.
I'm having trouble processing a listbox after selecting some items from it. In my markup, the listbox is contained within an asp:panel and is populated during page load in the codebehind. That part works fine.
It's when I select various items and submit that I have trouble. My handler loops through the listbox items but doesn't see any as being selected. I'm not sure why.
Here's the markup:
<asp:Panel ID="panEdit" runat="server" Height="180px" Width="400px" CssClass="ModalWindow">
<table width="100%">
<asp:label runat = "server">Choose your items</asp:label>
<tr>
<td>
<asp:ListBox ID="lstFundList" runat="server" SelectionMode="Multiple" OnLoad="lstFundList_LoadData">
</asp:ListBox>
</td>
</tr>
</table>
<asp:Button ID="btnUpdate" runat="server" Text="Update" OnClick="btnUpdate_OnClick"/>
<asp:Button ID="btnCancel" runat="server" Text="Cancel" OnClientClick="$find('ModalPopupExtender1').hide(); return false;" />
</asp:Panel>
In my btnUpdate_OnClick handler I can't see any listbox items that are marked as selected. I assume something strange is going on with respect to postback and the panel?
I agree, it's most likely a postback problem. Make sure the code that is populating the listbox is wrapped in something like this:
if (!Page.IsPostBack)
{
// populate your list
}
...is populated during page load in the codebehind
Is that wrapped in an IsPostback conditional? If not, then you're just overwriting the returned values.
`OnLoad="lstFundList_LoadData"
You may want to check that method too....
Thanks everyone. Sure enough, it turned out to be an IsPostBack issue. It's used in all of our pages (and no doubt yours) and had become a sort of background noise, and I simply missed it here.