LA.NET [EN]

Feb 15

[Update: Thanks to Cyril, I”ve update the code to use the new AsyncPostbackSourceElementID]

In the final version the UpdatePanel has a new cool property (ContentTemplateContainer) which really helps you if you want to add controls to an UpdatePanel through code.

Lets illustrate the usage of this property with a simple page that changes the content of an UpdatePanel when the user clicks on a button. Let”s  start by presenting the 2 user controls used in this example:

control1.ascx

<%@ Control Language=”C#” ClassName=”control1″ %>
<script runat=”server”>
  void HandleClick1(object sender, EventArgs e)
  {
      lbl.Text = txt.Text;
  }
</script>

This is user control 1.
Name: <asp:TextBox runat=”server” id=”txt” />
<asp:Button runat=”server” id=”bt” Text=”Print name” OnClick=”HandleClick1″ />
<br />
<asp:Label runat=”server” id=”lbl” />

 

control2.ascx

<%@ Control Language=”C#” ClassName=”control2″ %>
This is user control 2: <%= DateTime.Now.ToString() %>
<asp:Button runat=”server” id=”bt” Text=”Refresh” />

page.aspx

<%@ Page Language=”C#” %>
<!DOCTYPE html PUBLIC “-//W3C//DTD XHTML 1.0 Transitional//EN” “http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd”>

<script runat=”server”>
  protected override void OnLoad(EventArgs e)
  {
      base.OnLoad(e);

      if( manager.IsInAsyncPostBack )
      {
            string info = manager.AsyncPostbackSourceElementID;
            if( string.CompareOrdinal(info, bt.UniqueID) == 0)
            {
                    CurrentControl = CurrentControl == “control1.ascx” ? “control2.ascx” : “control1.ascx”
            }
            
            string info = this.Request.Params[manager.UniqueID];
            if( info.IndexOf(“|”) > 0 )
            {
                  string ctlId = info.Substring(info.IndexOf(“|”) + 1);
                  if( string.CompareOrdinal(ctlId, bt.UniqueID) == 0)
                  {
                    CurrentControl = CurrentControl == “control1.ascx” ? “control2.ascx” : “control1.ascx”
                  }
             }
      }
      LoadControl();
  }

   protected string CurrentControl
   {
       get
       {
          return ViewState[“CurrentControl”] == null ? “control1.ascx” : (string)ViewState[“CurrentControl”];
       }
       set
       {
           ViewState[“CurrentControl”] = value;
       }
   }
   void LoadControl()
   {
        Control ctl = LoadControl(CurrentControl);
        panel1.ContentTemplateContainer.Controls.Clear();
        panel1.ContentTemplateContainer.Controls.Add(ctl);
    }

</script>
<html xmlns=”http://www.w3.org/1999/xhtml” >
 <head runat=”server”>
   <title>Untitled Page</title>
 </head>
 <body>
    <form id=”form1″ runat=”server”>
           <asp:ScriptManager runat=”server” id=”manager” />
           <asp:UpdatePanel runat=”server” id=”panel1″>
              <Triggers>
                 <asp:AsyncPostBackTrigger ControlID=”bt” />
             </Triggers>
           </asp:UpdatePanel>
           <asp:Button runat=”server” id=”bt” Text=”Change control” />
</form>
</body>
</html>

Yep, that”s it. the tricky part is remembering that you must add dynamicaly generated controls until the end of the Load event or you might not get the expected results. Doing it on the Init event won”t do you any good either because we”re maintaining the current selected item on the viewstate (which is loaded after that event has fired). So, the Load even will be just fine for this.

Unfortunately, the ScriptManager control still doesn”t have a property that let”s you easily know the control that started an async postback. Fortunately, we do have Fiddler. with it, it”s easy to see that there”s a “secret” field that is being sent back to the server on the body of the request. This field is identified by the Id of the ScriptManager and you use it to get the ID of the control responsible for the postback:

manager=manager|bt&__EVENTTARGET=&__EVENTARGUMENT=&__VIEWSTATE=%2FwEPDwUJNzUzMjU3MTgzZGQ6YfDv2DwDFkdpUAnK5uj8PifsMg%3D%3D&ctl03$txt=&__EVENTVALIDATION=%2FwEWBAL7vsHoDgLMhYXhDQL46LbdBgK%2B79rvDBiOVPIc%2BzmCL7ifp3dWkDx8CElm&bt=Change%20control

And now everything looks easy, right? :,,)

I was wrong about this one. ScriptManager does expose a property (AsyncPostbackElementSourceID) which lets you get access to the control that started the partial postback. One thing you must keep in mind: if you handle the Init event (instead of the load event) you”ll have to resort to the previous code which is striked out.

10 comments so far

  1. Cyril
    1:44 pm - 2-16-2007

    Why don”t you use the AsyncPostBackSourceElementID property of the ScriptManager ?

  2. Luis Abreu
    10:38 pm - 2-16-2007

    howdy!
    you”re not going to believe it but I”ve looked at the ScriptManager API and didn”t see that! thanks Cyril!

  3. ASif
    2:48 pm - 3-6-2007

    Hi,
    I have Added a control In udatePanel through loadcontrol() but ehrn I cause some postback from the control then I lost the data of the control. How can I load a control in UpdatePanel and work as any other control.

  4. Luis Abreu
    5:26 pm - 3-17-2007

    Hi.
    Well, note that in the previous example I always load a control during a postback, ie, that”s necessary to make things work out.

  5. Ashish
    6:24 am - 4-2-2007

    I am dynamically loading control in UPDATEPANEL and once the post back on control takes place, the control disappears. I could not find the script managers AsyncPostBackSourceElementID property.

    What can I do to solve this problem?

    Please help me out.

    Thanks
    Ashish

  6. Sudeep
    8:45 am - 9-20-2007

    Thanks a lot luisabreu!

    For more then 2 weeks i was searching a way to add usercontrol on a page dynamically, where the main page and the user control both have update pannels (sort of nested update pannel). What was happeing in my case is that as soon as i call the submitt of my user control the entire user control was disappearing. Now with your solution it is working fine. Thanks a lot once again.

  7. Allan Gobin
    11:27 pm - 12-23-2007

    Just put a panel in the UpdatePanel and add the user control to it. Works fine :)

  8. Max
    9:57 pm - 1-7-2008

    This is a nice explanation.

    I was stuck for 3 days finding a way to add a user control dynamically to an update panel. I used the notation “new Control1″ instead of LoadControl(“Control1.ascx”). That was my trouble. I found no explanation to this behavior but finally am happy to find a solution.

    Thank you Luis!

  9. Ofek Cohany
    5:01 am - 3-11-2008

    Hello,
    Thank you for this beautiful solution! I have a question though:
    I”m trying to raise an event from the ASCX and capture it on the ASPX.
    The event is raised properly (I can see it on the debugger) but for some reason the ASPX does not capture it.
    My code on the ASPX page is:

    Protected objAscx As New ascx1

    on page_load:
    AddHandler objAscx.SaveForm, AddressOf SaveForm

    Protected Sub SaveForm()
    ”Some code here
    End Sub

  10. Daniel
    9:07 pm - 9-28-2008

    Everything works fine until you add more controls,
    it seems that the ViewState values from the controls inside control1 modify the values of the controls in control2

    the only solution I found was to set false the EnableViewState property but wha if my control needs ViewState?

Leave a Reply

Your email address will not be published. Required fields are marked *


*

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>