Multiple UpdatePanels and state - asp.net

I have a user control that has multiple update panels
On Page Load, I load a child user control dynamically onto the PlaceHolder (I do this everytime and don't check for postback)
I want to be able to maintain the state of the changes that a user makes in the Child user control. (The child user control has html controls along with asp.net controls and other custom controls).
The child user control loses the state whenever there's a click on the Button A or B that causes the postback. The mode for UpdatePanels have been set to conditional.
In the OnPageLoad,
I load a child control to the PlaceHolderA
This child control has another user control with a lot of JS. A user can potentially make changes in the child control.
When they click on Button A which is a part of the parent control, the OnPageLoad causes the child control to reload causing it to lose the client side changes that a user has made.
I would like to prevent this from happening.
Here's the HTML structure of the ASPX page
<UpdatePanel1>
<UpdatePanelChild1>
<Button A>
<Button B>
</UpdatePanelChild1>
<UpdatePanelChild2>
<PlaceHolderA>
</UpdatePanelChild2>

When dynamically adding controls you need to re-add them everytime your page loads. I've had to tackle similar difficulties and I do this in two ways. Where dynamic fields are added o the fly I will actually add a hidden control to the page before the user clicks to fill it in or add it themselves. By creating this sort of placeholder the system has tim eto start handling viewstate correctly and this preserves the users changes. The second issue that I hear you mentioning is that there might be issues with chich update panel updates. It's fine to have them set to conditional bu tin your code-behind you can also trigger the updates in teh othe rpanels if you need to from code. It's just updatepanel#.update()
alternatively you can also wrap all the update panels in another panel but I advise against this as it wastes resources.

It looks like you're losing the ViewState for your dynamically-added child control.
This is because you're adding it too late in the page lifecycle: the ViewState is loaded before the Page.Load event.
Try adding your child control in Page.Init instead.

You could use a ScriptManager with a form runat server in your control, instead update panels.
This way you dont have the post backs.
Here a good reference: http://www.asp.net/Ajax/Documentation/Live/overview/ScriptManagerOverview.aspx
Greetings.
Josema.
http://www.learning-workshop.com

Blockquote
//Code in your Asp.Net code
<form runat="server" id="form1">
<asp:ScriptManager id="ScriptManager1" runat="server" >
<Services>
<asp:ServiceReference Path="/WebServices/MyService.asmx"/>
</Services>
</asp:ScriptManager>
</form>
<script language="javascript" type="text/javascript">
//call with namespace "WebSite" included
WebSite.WebServices.HelloWorld(EndHelloWorld);
function EndHelloWorld()
{
//do whatever
}
</script>
</form>
Blockquote
//Code in your webservice class
[ScriptService]
public class MyService : System.Web.Services.WebService
{
[WebMethod]
public void HelloWorld()
{
return "Hello world";
}
}
From the script of my page i will do Ajax against my webservice, and the callback EndHelloWorld you could do whatever without losing state.
Hope it helps...
Kind Regards.
Josema.

It looks like you need to remove the nested UpdatePanels. In a structure like this:
<ParentUpdatePanel>
<ChildUpdatePanel1 />
<ChildUpdatePanel2 />
</ParentUpdatePanel />
ALL of the update panels will ALWAYS update, even if the child UpdatePanels are set to conditional, because the parent is actually the one that is updating. So, a postback event in one of the children will cause the parent to update.
Using that type of pattern is bad in general... if you need events from one UpdatePanel to cause updates in another, you should do this:
<asp:UpdatePanel ID="up1" runat="server" UpdateMode="Conditional">
<ContentTemplate>
<asp:Button ID="buttonA" runat="server" />
<asp:Button ID="buttonB" runat="server" />
</ContentTemplate>
</asp:UpdatePanel>
<asp:UpdatePanel ID="up2" runat="server" UpdateMode="Conditional">
<ContentTemplate>
<asp:Placeholder ID="ph" runat="server" />
</ContentTemplate>
<Triggers>
<asp:AsyncPostBackTrigger ControlID="buttonB" EventName="Click" />
</Triggers>
</asp:UpdatePanel>

Related

UpdatePanel update without trigger button

I have an UpdatePanel with ContentTemplate specified. When page loads, user can do some AJAX work in other part of the page. Then, after that work is finished, I would like to update only content inside UpdatePanel, but without pressing any buttons etc. I should be done automatically using JavaScript when previously started AJAX work finishes. How to do it without manual clicking on the trigger button?
EDIT:
Ok, I've followed that _doPostBack rule, and whole page is posted.
<asp:UpdatePanel ID="panelAttachments" runat="server">
<ContentTemplate>
........
</ContentTemplate>
</asp:UpdatePanel>
<input type="text" name="test" onchange="__doPostBack('<%=panelAttachments.UniqueID %>',''); return false;" />
</td>
Thanks, Pawel
To refresh an update panel from javascript:
__doPostBack(updatePanelUniqueID,'');
The first parameter is the UniqueID (not CientID) of the UpdatePanel.The 2nd parameter is optional arguments you can pass which will be available to your server code. Both are stored in hidden form fields by ASP.NET, you can access them in codebehind:
Request.Form["__EVENTTARGET"];
Request.Form["__EVENTARGUMENT"];
But if you just want to refresh a panel and don't need to pass any additional info from the client, you can ignore then 2nd argument.
If you look at the HTML generated by ASP.NET for an async postback control, you'll see it's exactly this.

One user control updating another during AJAX Postback?

I developed a user control that displays a list of products and it works pretty good. Then I dropped this user control into another user control that allows the user to pick different criteria and the product UC updates to show those products, all pretty slick and with AJAX via UpdatePanel.
All was working just fine... then another requirement came in. The "search" control needs to be separate from the product control (so they can be positioned separately). Initially, I thought this was no problem as I would give the search control a reference to the product control and then it would talk to it via reference instead of directly inside the control (which has been removed).
And they do talk. But the product control loads, but refuses to display.
I checked and it is being passed via reference and not a copy ( as best I can tell ).
There is an updatepanel in the search control. There is an update panel in the product control. And then for good measure, there is an update panel surrounding them both in the actual search aspx page.
I've tried setting the product control update panel to conditional and then fire the .Update() method manually.
What's the secret here?
TIA!
SOLVED
Thanks to Jamie Ide for the tip to use events.
Search Control and Product control still have internal update panels, and NO LONGER have them on this particular page.
Search Control now raises an event OnSearchResultsUpdated and exposes the found items in properties. The page subscribes to this event and takes the properties and passes them to the product control and triggers triggers a .Refresh() method on the product control which simply calls the .Update() on its internal updatepanel.
The Product control, FYI, accepts products in several different flavors. A list of distinct SKUs, a list of product ids, a named collection in our database and finally a given product category.
Our designers need to be able to create a new page, drop the control onto it and set some properties and voila! New site page. They don't want to require a programmer's involvement. So keeping the controls self contained is a requirement. Fortunately all the changes I made still work completely with the other uses of the product control.
THANKS AGAIN SO MUCH!
I don't think there's really enough information to work with here, but my best guess is that the Product control is not getting data bound. You may try calling myProdcutsCtrl.DataBind() from the search control (or something inside the Product control that cause a DataBind() for instance myProductCtrl.Search(value1, value2, value3).
One other thing you might try is removing the UpdatePanels and seeing if things work. Then add them back in once you get core functionality going.
UPDATE: I've gone ahead and put some example code that works here which I believe accomplishes what you want. What follows are snippets for the sake of saving space, but include all code necessary to make it run. Hopefully this will at least give you something for reference.
Things to try:
EnablePartialRendering="true|false" setting it to false will force the natural postbacks and is good for debugging UpdatePanel problems.
Make sure you are seeing Loading... come up on your screen. (maybe too fast depending on your dev computer)
Page.aspx
<%# Register Src="~/Product.ascx" TagPrefix="uc" TagName="Product" %>
<%# Register Src="~/Search.ascx" TagPrefix="uc" TagName="Search" %>
...
<asp:ScriptManager runat="server" ID="sm" EnablePartialRendering="true" />
Loaded <asp:Label ID="Label1" runat="server"><%= DateTime.Now %></asp:Label>
<asp:UpdateProgress runat="server" ID="progress" DynamicLayout="true">
<ProgressTemplate><b>Loading...</b></ProgressTemplate>
</asp:UpdateProgress>
<uc:Search runat="server" ID="search" ProdcutControlId="product" />
<uc:Product runat="server" ID="product" />
Search.ascx
<asp:UpdatePanel runat="server" ID="searchUpdate" UpdateMode="Conditional" ChildrenAsTriggers="true">
<ContentTemplate>
<p>
<asp:Label runat="server" AssociatedControlID="filter">Less than</asp:Label>
<asp:TextBox runat="server" ID="filter" MaxLength="3" />
<asp:Button runat="server" ID="search" Text="Search" OnClick="SearchClick" />
</p>
</ContentTemplate>
</asp:UpdatePanel>
Search.ascx.cs
public string ProdcutControlId { get; set; }
protected void SearchClick(object sender, EventArgs e)
{
Product c = this.NamingContainer.FindControl(ProdcutControlId) as Product;
if (c != null)
{
c.Search(filter.Text);
}
}
Product.ascx
<asp:UpdatePanel runat="server" ID="productUpdate" UpdateMode="Conditional" ChildrenAsTriggers="false">
<ContentTemplate>
<asp:Label runat="server">Request at <%= DateTime.Now %></asp:Label>
<asp:ListView runat="server" ID="product">
<LayoutTemplate>
<ul>
<li id="itemPlaceHolder" runat="server" />
</ul></LayoutTemplate>
<ItemTemplate>
<li><%# Container.DataItem %></li></ItemTemplate>
</asp:ListView>
</ContentTemplate>
</asp:UpdatePanel>
Product.ascx.cs
IEnumerable<int> values = Enumerable.Range(0, 25);
public void Search(string val)
{
int limit;
if (int.TryParse(val, out limit))
product.DataSource = values.Where(i => i < limit);
else
product.DataSource = values;
product.DataBind();
productUpdate.Update();
}
Code does NOT represent best practices, just a simple example!
I'm fairly new to AJAX, but I don't think it's a good idea for user controls to have UpdatePanels. I would also advise you not to have the user controls reference each other; they should communicate through events and methods controlled by their container.
I do something similar with two user controls for a master-details display. The master raises an event when an item is selected from a list, the containing page handles the event and calls a method on the details display to display the selected item. If I remember correctly, my first attempt had UpdatePanels in the user controls and I wasn't able to make that work. Having the user controls inside an UpdatePanel on the page works fine.
If I understand you right you have a layout like this:
Outer UpdatePanel
SearchControl
Search UpdatePanel
ProductControl
Product UpdatePanel
Databound Control
Which one of those update panels is actually being called?
I assume that if you check the network traffic with something like Fiddler or Firebug if you're using Firefox, you aren't seeing any HTML for the product update panel coming back?
Have you tried doing something like:
UpdatePanel productUpdate =
Page.FindControl("Product UpdatePanel") as UpdatePanel;
if (null != productUpdate){
productUpdate.Update();
}
By default, if a postback is made from an UpdatePanel, only that control will be updated/re-rendered (this is called partial page-rendering).
To also update/re-render other UpdatePanels, you have to either:
set their UpdateMode property to Always
add the control that makes the postback to their Triggers collection
Check this page in MSDN for details.

Trigger an update of the UpdatePanel by a control that is in different ContentPlaceHolder

I have a page with two ContentPlaceHolders. One has a DropDown and another UpdatePanel with content.
How can I trigger update to the UpdatePanel by the DropDown's selectedItemChanged event when they are in different ContentPlaceholders?
The following would not work since UpdatePanel1 doesn't know about DropDown1:
<asp:UpdatePanel ID="UpdatePanel1" runat="server" UpdateMode="Conditional" ChildrenAsTriggers="true">
<ContentTemplate>
Some content that needs to be updated here...
</ContentTemplate>
<Triggers>
<asp:AsyncPostBackTrigger ControlID="DropDown1" EventName="SelectedIndexChanged" />
</Triggers>
</asp:UpdatePanel>
One way is to make an ajax page method that would be called by javascript on the page when DropDown's item is selected. Then in code behind, inside that page method, call UpdatePanel1.Update().
Is there an easier alternative?
From http://msdn.microsoft.com/en-us/library/system.web.ui.asyncpostbacktrigger.aspx
The control that the
AsyncPostBackTrigger references must
be in the same naming container as
the update panel for which it is a
trigger. Triggers that are based on
controls in other naming containers
are not supported.
The workaround is to use the UniqueID of the control that the
trigger is referencing. Unfortunately the UniqueID isn't qualified
until the control has been added to its parent (and its parent
has been added to its parent, all the way up the control tree).
In your code behind, try:
UpdatePanel1.Triggers.Add(new AsyncPostBackTrigger()
{
ControlID = DropDown1.UniqueID,
EventName = "SelectedIndexChanged", // this may be optional
});
In the code-behind file, you should be able to do:
ScriptManager.RegisterAsyncPostBackControl(dropdown1);
You can enforce update any of page UpdatePanels by call updatePanel1.Update() method on server side.
For example during update updatePanel1 on button1.Click call updatePanel2.Update() and both panels will be updated.

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.)

How do I add Ajax to my ASPnet page so that a dropdown change can immediately affect the backend db

I have a couple of dropdown boxes on a normal ASP.Net page.
I would like the user to be able to change these and to have the page Pseudo-post back to the server and store these changes without the user having to hit a save button.
I don't really need to display anything additional as the dropdown itself will reflect the new value, but I would like to post this change back without having the entire page flash due to postback
I have heard that this is possible using AJAX.Net...
Can someone point me in the right direction?
Add a reference to System.Web.Extensions and System.Web.Extensions.Design to your website. Then put a scriptmanager on your page and wrap your ddl in an updatepanel. Do whatever you want on the back-end.
For example...
<asp:ScriptManager ID="ScriptManager1" runat="server" />
<asp:UpdatePanel ID="UpdatePanel1" runat="server">
<ContentTemplate>
<asp:DropDownList ID="DropDownList1" runat="server" AutoPostBack="true" OnSelectedIndexChanged="yourDDL_SelectedIndexChanged">
</asp:DropDownList>
</ContentTemplate>
</asp:UpdatePanel>
protected void yourDDL_SelectedIndexChanged(object sender, EventArgs e)
{
// do whatever you want
}
Depends upon your Ajax Framework, Ra-Ajax have a sample of that here...

Resources