Alex Alex - 2 months ago 52
ASP.NET (C#) Question

UpdatePanel and dynamically generated controls

I am trying to dynamically build a list of controls inside an updatepanel, and trigger an event from a button on said list of controls.

However, when I click on the control generated, instead of actually firing the trigger, my page does a postback, and never hits the breakpoint I would expect, instead, it only triggers the page load/page init events.

What am I missing?

Default.aspx

<%@ Page Title="Home Page" Language="C#" MasterPageFile="~/Site.Master" AutoEventWireup="true" CodeBehind="Default.aspx.cs" Inherits="Test._Default" %>

<asp:Content ID="BodyContent" ContentPlaceHolderID="MainContent" runat="server">

<asp:Button ID="add" Text="add" runat="server" OnClick="add_Click"/>
<asp:updatepanel runat="server" id="test" ChildrenAsTriggers ="false" UpdateMode="Conditional">
<ContentTemplate></ContentTemplate>

</asp:updatepanel>

</asp:Content>


Default.aspx.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;

namespace Test
{
public partial class _Default : Page
{
protected void Page_Load(object sender, EventArgs e)
{

}

protected void add_Click(object sender, EventArgs e)
{
var container = test.ContentTemplateContainer;
container.Controls.Clear();


Literal literal = new Literal();
literal.Text = DateTime.Now.ToString();
literal.ID = DateTime.Now.Ticks.ToString();
container.Controls.Add(literal);

Button btn = new Button();
btn.Text = "123";
btn.Click += new EventHandler(btnClick);
container.Controls.Add(btn);

AsyncPostBackTrigger trigger = new AsyncPostBackTrigger();
trigger.ControlID = btn.UniqueID;
trigger.EventName = "Click";
test.Triggers.Add(trigger);


}

protected void btnClick(object sender, EventArgs e) {
System.Diagnostics.Debugger.Break(); //never gets hit
var abc = 16;
}
}
}

Answer

The button never hits btnClick because the button does not exist. You are creating the buttton on a button click event, but it is lost on PostBack.

Dynamicly added controls must always be added again for every PostBack action in the Page_Load event handler or they will dissapear from the page. But because you are using an UpdatePanel it still looks that they are present.

Try this below and you will see that this time the button click does work.

    protected void Page_Load(object sender, EventArgs e)
    {
        var container = testje.ContentTemplateContainer;
        container.Controls.Clear();

        Button btn = new Button();
        btn.Text = "123";
        btn.Click += new EventHandler(btnClick);
        container.Controls.Add(btn);
    }

If you want to add more than one set of Literals and Buttons with add_Click you need to store the number of clicks in a ViewState and create a function that loops the count in the ViewState when the page is reloaded.

Here an example of how it should work.

    //declare global integer
    int controlCount = 0;

    protected void Page_Load(object sender, EventArgs e)
    {
        //check if the viewstate exists
        if (ViewState["controlCount"] != null)
        {
            try
            {
                //convert viewstate to int
                controlCount = Convert.ToInt32(ViewState["controlCount"]);
            }
            catch
            {
            }
        }
        //call the funcion to add the controls on every page load
        addControls();
    }


    protected void add_Click(object sender, EventArgs e)
    {
        //pretty obvious what this does...
        controlCount++;
        addControls();
    }


    private void addControls()
    {
        var container = testje.ContentTemplateContainer;
        container.Controls.Clear();

        //loop the currect control count
        for (int i = 0; i < controlCount; i++)
        {
            Literal literal = new Literal();
            literal.Text = "Literal " + i;
            literal.ID = "myLiteral_" + i;
            container.Controls.Add(literal);

            Button btn = new Button();
            btn.Text = "Button " + i;
            btn.ID = "myButton_" + i;
            btn.Click += new EventHandler(btnClick);
            container.Controls.Add(btn);
        }

        //set the viewstate again with the new control count
        ViewState["controlCount"] = controlCount;
    }


    protected void btnClick(object sender, EventArgs e)
    {
        //cast the sender as a button
        Button btn = sender as Button;

        //split the ID to get the count
        string [] btnNr = btn.ID.Split('_');

        //find the literal that goes with the clicked button
        Literal literal = testje.FindControl("myLiteral_" + btnNr[1]) as Literal;

        //alert the literal text
        ScriptManager.RegisterStartupScript(Page, Page.GetType(), "doPopup", "alert('" + literal.Text + "')", true);
    }