Nested UpdatePanel causes parent to postback? - asp.net

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

Related

asp:UpdatePanel on individual RadGrid rows

I am making some changes to an internal application that has a Telerik RadGrid component on one .aspx page.
Each row in the RadGrid represents an order and there is a dropdown which allows setting the status of the order. Changing the status of the order updates a couple of other properties of the order which are displayed on that row.
Up until now, changing the order status has resulted in a complete page post back and re-render. I'm keen to change this to a partial post back using an UpdatePanel. I could wrap the status dropdown (a RadCombo) in an UpdatePanel which would take care of the actual database changes that are required (as per the code sample below), but then without updating the other properties on the RadGrid row, the updates are not presented to the user.
<telerik:RadGrid ID="OrdersGrid" runat="server" ...>
<MasterTableView DataKeyNames="OrderId" AllowMultiColumnSorting="false">
<NoRecordsTemplate ...></NoRecordsTemplate>
<Columns>
<telerik:GridBoundColumn ... />
...
<telerik:GridTemplateColumn HeaderText="Order Status" UniqueName="OrderStatus">
<ItemTemplate>
<asp:UpdatePanel runat="server">
<ContentTemplate>
<telerik:RadComboBox ID="RadOrderStatus" DataSourceID="OrderStatusDataSource" runat="server"
SelectedValue='<%# Bind("OrderStatus") %>' Skin="Metro" Width="180px" DataTextField="OrderStatus"
DataValueField="OrderStatus" AutoPostBack="True" EnableLoadOnDemand="False" OnSelectedIndexChanged="RadOrderStatus_SelectedIndexChanged">
</telerik:RadComboBox>
</ContentTemplate>
</asp:UpdatePanel>
</ItemTemplate>
</telerik:GridTemplateColumn>
</Columns>
<SortExpressions>...</SortExpressions>
</MasterTableView>
</telerik:RadGrid>
I'm wondering how I can use the UpdatePanel to effectively wrap the row of the RadGrid so that the entire row can be updated as opposed to just the cell that the dropdown is in. I've tried experimenting already with tag placement but I'm new to Telerik and therefore not very clued up.
At what level in the mark up can I place the UpdatePanel to get this to work as I'd like?
Or is there a Telerik way of doing this?
I could wrap the whole grid but if possible, I'd rather not have the whole grid update on each partial post back, the permitted operations are limited to the row level so I see a full grid update as wasteful.
You can't place an UpdatePanel around each row. For starters, there is no provision to do that (you can't do it with the standard GridView either). Then, if you manage to do that (e.g., override the Render event), you would get invalid markup because you can't have <div> elements inside the <table> and between the other <tr> nodes.
One note on the performance—the AJAX request will have the page go through its entire lifecycle on the server, so all code will be executed again and any time consuming operation will also be executed. The only difference between the AJAX and the full postback is what gets rendered and returned in the response, so you basically shave off network time only.
What you can do is the following:
wrap the entire grid. I would use RadAjaxPanel and RadAjaxLoadingPanel so you have a pretty loading indicator. Something like:
<telerik:RadAjaxLoadingPanel ID="RadAjaxLoadingPanel1" runat="server" Skin="Black"></telerik:RadAjaxLoadingPanel>
<telerik:RadAjaxPanel ID="RadAjaxPanel1" runat="server" LoadingPanelID="RadAjaxLoadingPanel1">
<telerik:RadGrid ID="RadGrid1" runat="server"></telerik:RadGrid>
</telerik:RadAjaxPanel>
OR, use client-side code and jQuery. The combo can call a WebService or a PageMethod that will return the data and you can use jQuery to traverse the DOM and update the other elements (textboxes, dropdowns, whatever you have). This is going to be more difficult.

All UpdatePanels posting back at the same time

I have a C# .net 4.0 website project with a fairly complicated filtered search page on it. There are multiple UpdatePanels that are added within a Repeater. When one UpdatePanel does a postback - all the other UpdatePanels also postback at the same time.
This becomes a problem because there can be lots and lots of UpdatePanels dependent on the number of items the user chooses to view. I know UpdatePanels are not ideal - I didn't write this but have to try and fix it quickly!
There is LandingPage that holds an UpdatePanel with a Repeater control inside. Within the repeater is a user control called Article. The Article control contains some HTML and a second user control called Save. The Save control has an UpdatePanel too.
The problem I have is that only the first btnSave event gets raised. So if I click "btnSave" it works but all subsequent button click events do not fire.
I have also noticed that ALL instances of the UpdatePanel in the Save control postback at the same time - is this normal?
So a simplified view of the page is like so:
LandingPage.aspx
<asp:UpdatePanel ID="pnl" runat="server" UpdateMode="Conditional" ChildrenAsTriggers="false">
<ContentTemplate>
<asp:Repeater ID="resultsRep" runat="server">
<ItemTemplate>
<uc:Article id="Article1" runat="server" />
</ItemTemplate>
</asp:Repeater>
<asp:Button id="btnLoadMore" runat="server" Text="Load More" />
</ContentTemplate>
<Triggers>
<asp:AsyncPostBackTrigger ControlID="btnLoadMore" EventName="Click" />
</Triggers>
<asp:UpdatePanel>
Custom User Control "Article"
<asp:PlaceHolder ID="ArticlePanel" runat="server">
<!-- Assorted HTML stuff here -->
<uc:Save id="Save1" runat="server" />
</asp:PlaceHolder>
Custom User Control "Save"
<asp:UpdatePanel ID="ctl" runat="server" UpdateMode="Conditional" ChildrenAsTriggers="True">
<ContentTemplate>
<asp:LinkButton ID="btnSave" runat="server" OnClick="btnSave_Click" CausesValidation="False" Text="Save" />
</ContentTemplate>
</asp:UpdatePanel>
Thanks in advance as always.
EDIT
After further investigation, using Firebug console I found that the subsequent postbacks don't occur because the following error is thrown:
505|error|500|Invalid postback or callback argument.
Event validation is enabled using <pages enableEventValidation="true"/> in configuration or <%# Page EnableEventValidation="true" %> in a page.
For security purposes, this feature verifies that arguments to postback or callback events originate from the server control that originally rendered them.
If the data is valid and expected, use the ClientScriptManager.RegisterForEventValidation method in order to register the postback or callback data for validation.|
So it seems somehow I am posting back something dodgy?
The problem was down to the use of nested UpdatePanels. I don't know where the error itself actually comes from but after trial and error I figured out that the parent UpdatePanel wasn't configured properly.
The parent UpdatePanel should have been like so :
<asp:UpdatePanel ID="pnl" runat="server" UpdateMode="Always" ChildrenAsTriggers="true">
The difference being UpdateMode="Always" and ChildrenAsTriggers="true". The UpdateMode tells the parent UpdatePanel to refresh when any of the children reload. The ChildrenAsTriggers attribute allows child UpdatePanels to cause the refresh of the parent.
So now it works - mostly. I still have the issue of every single UpdatePanels posting back everytime. Its really inefficient but I can't seem to stop it.

Asp Focus on input jumping bug

I've got a web form using asp.net. In this form I have a lot of inputs.
For one of the drop downs whenever you press it, the focus jumps to the next text box.
This is in a update panel, because there is some server side work required for filtering, hiding, etc.
If the User chooses Australia from visaType_filter then it hides visaType_dd and shows visaType_tb. If they choose NZ its the other way around.
Now my question:
Is there a bug or something that makes focus jump off of a drop down when you click on it to go to the next input (or control)?
Code:
<fieldset>
<asp:UpdatePanel ID="visaTypeUpdatePanel" runat="server">
<ContentTemplate>
<label>Visa Type Number</label>
<label>
<asp:DropDownList ID="visaType_filter" runat="server" Width="40%" OnSelectedIndexChanged="visaType_filter_SelectedIndexChanged" AutoPostBack="true"/>
<asp:TextBox ID="visaType_tb" runat="server" Width="40%" OnTextChanged="visaType_tb_blur" AutoPostBack="true"/>
<asp:DropDownList ID="visaType_dd" runat="server" Width="40%"/>
<asp:Literal ID="visaType_literal" runat="server" />
</label>
</ContentTemplate>
</asp:UpdatePanel>
</fieldset>
<fieldset>
I resolved my problem by using jQuery and Ajax in place of UpdatePanels.
AFAIK, when UpdatePanel gets triggered, the focus does not get maintained. So ideally, you shouldn't be getting any focus at all.
Regardless of reason, you can work-around the issue using ScriptManager.SetFocus method to maintain focus on the drop-down.
You can also have client side solutions for maintaining focus - they essentially work by hooking into AJAX requests to remember the focused control before update panel is triggered and then restoring it back when update panel post-back is completed: see this link for one such solution.

Update Panel - Textbox TextChanged Event is interfering with the DropDown SelectedIndexChanged event

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.

Why does the Update Panel do a full post back for custom control?

I have a rather complex custom control - the custom control has a couple of update panels in it.
I am trying to use the control like this inside of an update panel:
<asp:UpdatePanel ID="up1" runat="server">
<ContentTemplate>
<asp:Button ID="btn1" runat="server" Text="Sample Button" /> <asp:Label ID="lblTime" runat="server"></asp:Label>
<cc1:MyCustomControl ID="MyCustomControl1" runat="server" >
</cc1:MyCustomControl>
</ContentTemplate>
</asp:UpdatePanel>
When I click the button in the update panel, it does an async post back and there is no screen "flicker" When I click a button in my custom control the page flickers and does a full post back.
Inside the custom control, there are update panels that are trying to do full postbacks (based on triggers).
How can I make the page level UpdatePanel not do a full postback no matter what is going in inside of the custom control?
Have you thought about explicitly setting an asp:AsyncPostBackTrigger with the btn1 control in the up1 UpdatePanel control.
<asp:UpdatePanel ID="up1" runat="server">
<Triggers>
<asp:AsyncPostBackTrigger ControlID="btn1" EventName="Click" />
</Triggers>
<ContentTemplate>
<asp:Button ID="btn1" runat="server" Text="Sample Button" />
<asp:Label ID="lblTime" runat="server"></asp:Label>
<cc1:MyCustomControl ID="MyCustomControl1" runat="server" />
</ContentTemplate>
</asp:UpdatePanel>
Edit: How you tried to explicitly call the Update method in the button's OnClick event for the Update Panel? This includes the Update panels embedded within the custom control.
Figured out the solution similar issue to this: How can I get an UpdatePanel to intercept a CompositeControl's DropDownList
Except my control causing the postback was in an updatepanel with a full postback trigger. I was able to pull that control out so it was not nested with in update panels and that resolved it.
I would first look if there is some other issue with the custom control causing the full page postback, as in any case what should be happening is that the whole update panel refreshes (still with ajax).
After that, just look at the Nesting UpdatePanel Controls section of this:
http://msdn.microsoft.com/en-us/library/bb398867.aspx#
Also make sure to have the ScriptManager control with the property EnablePartialRendering set to true.
On the UpdatePanel, set the property ChildrenAsTriggers="true". This tells the UpdatePanel to intercept all PostBack invocations that originate from inside the UpdatePanel.
You may want to also explore the UpdateMode property, which determines what kinds of events trigger an update. (By default, an UpdatePanel will refresh if any other panel on the screen gets refreshed. This threw me for awhile until I realized what was going on.)

Resources