Saturday, March 13, 2010

ASP.NET UpdatePanel must be added before Event Handling step

This post is specific to AJAX programming in the ASP.NET environment. This is mostly so that if I forget this issue 6 months from now I'll have something to remind me, since I was unable to find anything on the web about this. So if you have no interest in AJAX in the .NET environment, feel free to stop reading now.

Background

Just in case anyone without .NET experience is still reading I will give a brief high level overview of what is going on. When you hit a .aspx page, the execution goes through multiple stages including PageInit, PageLoad, and EventHandling. I am not going to go into the intricacies here, just suffice it to say that they happen in order and you can define specific functionality for each step.

UpdatePanel is the ASP object that you use to wrap parts of your web page that you want to update asynchronously. Content in an UpdatePanel can be updated without a full page reload, and buttons clicked in an UpdatePanel operate in a AJAX fashion and don't cause a full page reload.

Oh, and content to your web page can be added either declaratively in the .aspx file (similar to php) or programmatically in the C# (or other language) code behind file.

Problem

The problem that I have run across is that UpdatePanels that are added programmatically during the event handling phase don't work in AJAX fashion. This means that if you add an UpdatePanel to your page as the result of a button click it doesn't work.

Demo Program
Here is a little demo program that has 3 update panels. The first is defined declaratively, the other two programmatically, one during the init phase, and one during the load phase. There are also two buttons. The "AJAX Update" button causes an AJAX event which causes all 3 panels to update themselves - so they will all show a new time.

The "Add Panel" button adds a fourth UpdatePanel to the page. This one is added during the event handling phase (button click handling). In theory, this panel should act like the others. However after adding this panel, if you click the "AJAX Update" button, you'll see that only the first 3 panels update their time, and not the last one.


Code

Here is the code for the above demo. In case you are curious, code formatting on this post was done by: http://www.manoli.net/csharpformat/ I hope the styles apply correctly when I post this.

ViewForm3.aspx


   1:  <%@ Page Language="C#" AutoEventWireup="true" CodeBehind="ViewForm3.aspx.cs" Inherits="ViewForm3" %>
   2:  <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
   3:  <html xmlns="http://www.w3.org/1999/xhtml" >
   4:  <head runat="server">
   5:      <title>Test Update Panel</title>
   6:  </head>
   7:  <body>
   8:    <form runat="server">
   9:      <asp:ScriptManager runat="server" />
  10:      <asp:UpdatePanel runat="server">
  11:        <ContentTemplate>
  12:          Last Updated at: <%= DateTime.Now %>
  13:          <asp:Button runat="server" Text="AJAX Update" />
  14:        </ContentTemplate>
  15:      </asp:UpdatePanel>
  16:      <asp:PlaceHolder runat="server" ID="PlaceHolder" />
  17:      <asp:Button runat="server" Text="Add Panel" OnClick="Add_Click" />
  18:    </form>
  19:  </body>
  20:  </html>

ViewForm3.aspx.cs


   1:  using System;
   2:  using System.Web.UI;
   3:   
   4:  public partial class ViewForm3 : Page
   5:  {
   6:    protected void Page_Init(object sender, EventArgs e)
   7:    {
   8:      AddUpdatePanel("Init updated at: " + DateTime.Now);
   9:    }
  10:   
  11:    protected void Page_Load(object sender, EventArgs e)
  12:    {
  13:      AddUpdatePanel("Load updated at: " + DateTime.Now);
  14:    }
  15:   
  16:    protected void Add_Click(object sender, EventArgs e)
  17:    {
  18:      AddUpdatePanel("Click updated at: " + DateTime.Now);
  19:    }
  20:   
  21:    private void AddUpdatePanel(string message)
  22:    {
  23:      UpdatePanel up = new UpdatePanel();
  24:      LiteralControl lit = new LiteralControl(message);
  25:      up.ContentTemplateContainer.Controls.Add(lit);
  26:      PlaceHolder.Controls.Add(up);
  27:    }
  28:  }


Why this is?

I have no idea why this is, and that bugs me. I was unable to find any examples on the web of people doing this, so didn't learn anything there. If anyone who reads this understands why this behavior is as it is, please email me, or add a comment to this post. (Note - this was done using Visual Studio 2008 with ASP.NET 3.5, if that makes a difference). If I learn more - I will post an update.