Event handler not fired for dynamically created controls

I've got this problem with dynamically created TextBox.

When the TextBox is created in PageLoad, it's TextChanged event was fired. But when I dynamically delete and recreated the TextBox, the TextChanged was not fired.

This is the code:

.aspx file

<body>
    <form id="form1" runat="server">
    <div>

    <asp:Table ID="Table1" runat="server">
      <asp:TableRow>
        <asp:TableCell ColumnSpan="2">Fixed content</asp:TableCell>
      </asp:TableRow>
    </asp:Table>   
    </form>
</body>

.cs file

public partial class test : System.Web.UI.Page
{
  string myText = "a";

  protected void Page_Load(object sender, EventArgs e)
  {
    WriteRows();
  }

  private void WriteRows()
  {
    TableRow tr = new TableRow();

    TableCell tc = new TableCell();
    TextBox txt = new TextBox();
    txt.Text = myText;
    txt.TextChanged += new EventHandler(txt_TextChanged); // Assign event handler
    tc.Controls.Add(txt);
    tr.Controls.Add(tc);

    tc = new TableCell();
    tc.Text = txt.Text;
    tr.Controls.Add(tc);

    Table1.Controls.AddAt(1, tr);
  }

  private void txt_TextChanged(object sender, EventArgs e)
  {
    myText = ((TextBox)sender).Text;
    RedrawTable(); // Delete the row (incl. the TextBox) and rewrite it
  }

  private void RedrawTable()
  {
    Table1.Controls.RemoveAt(1);
    WriteRows();
  }
}

Does anyone have a solution so that the event is always fired?

Answers


Event handling is done by ASP.NET by matching up control's ID & the request parameters. In your case, the TextBox created during txtTextChanged() will have an auto ID because you don't specify any explicit ID. That ID will be posted back during the text changed event.

After page load event, ASP.NET will try to find a control with such ID to fire the event for it. Obviously ASP.NET won't be able to find the match because the TextBox created during Page_Load() is different and would have different ID.

To solve this: specify an explicit ID for your textbox:

TextBox txt = new TextBox();
txt.Text = myText;
txt.ID = "txtBox";

Try creating the controls in the Page_Init() method ...


for a postback event to fire, the control that should fire the event needs to be available with the same id and the same data on the postback lifecycle.

If you have static controls (defined in your aspx/ascx/master) and viewstate turned on, then they will be recreated automagically.

If you don't want to use viewstate, or use dynamic controls, you need to databind controls on each page_load, so that the controls are up and running in time for events to fire (happens after Page_load)

if you change the ID of a parent control, or page, you might accidentally throw off the viewstate autobind, as the control IDs contains ancestors IDs. I think you should be safe doing that, so long as you do it in Page_Init (before viewstate is set up)


Re-create dynamically created controls in Page_Init. You won't have access to the viewstate data of a control if you recreate it in Page_Load (viewstate is loaded in PreLoad which happens before Page_Load).

Also, don't forget to assign an ID to the control.


I had a similar problem. I think the issue is that dynamically created controls are not kept in view state and don't survive a postback. Here is a comment ripped from my code that describes the solution that I came up with (it may not be the only one, but it worked for me).

This page is used to define a grid dynamically. The user clicks checkboxes to indicate which fields to include on the grid. The logic of this page does two essential things:

(1) It maintains the GridDefinition object that is kept in ViewState. (2) It reconstructs the programatically added controls (essentially everything in the table object) from the GridDefinition in ViewState on each postback. The dynamically added controls are NOT recreated on the postback from ViewState. Indeed, I found that if you don't recreate the controls, their events won't fire. Apparently:

       "The process that matches controls to posted values occurs 
       after page_load completes, so it has to occur just like this 
       if you are to use this way."

When I get a control event indicating some change to the data, I have to reflect that change in the GridDefinition object stored in ViewState. That way, on the NEXT postback, the control can be recreated properly (e.g. a text box indicating the header text for a grid column).


When I was researching this issue, it was in the context of Dynamic Menus, and there are a bunch of Google responses that, together, got me through it (because it's a common enough requirement, I guess.) I don't have a summary of the answer, but it might be a helpful place to start (i.e. google for Dynamic Menus .NET). There are several questions here on this site too.


...as another suggestion, isn't binding events after Page_Load too late? and therefore they won't be fired on the postback. maybe something to check out..


I'm once again reminded why I avoid asp - to me it seems like one level of abstraction too many. But by analogy with javascript, is it possible you're ending up with more than one handler on the event?


You will be required to just assign an ID property with the Textbox. It should resolve your problem.


Your answer given below with entire code:

string myText = "a";

protected void Page_Load(object sender, EventArgs e)
{
    WriteRows();
}

private void WriteRows()
{
    TableRow tr = new TableRow();

    TableCell tc = new TableCell();
    TextBox txt = new TextBox();
    txt.Text = myText;

    txt.ID = "txt1";

    txt.TextChanged += new EventHandler(txt_TextChanged); // Assign event handler

    txt.AutoPostBack = true;

    tc.Controls.Add(txt);
    tr.Controls.Add(tc);

    tc = new TableCell();
    tc.Text = txt.Text;
    tr.Controls.Add(tc);

   Table1.Controls.AddAt(0,tr);

}

private void txt_TextChanged(object sender, EventArgs e)
{
    myText = ((TextBox)sender).Text;
    RedrawTable(); // Delete the row (incl. the TextBox) and rewrite it
}

private void RedrawTable()
{
    Table1.Controls.RemoveAt(0);
    WriteRows();
}

I had same problem, and in my case txt.AutoPostBack = true was what I missed. Default is false, easy to forget.


Need Your Help

Why the following program giving exit failure?

c compiler-errors

Read the following program and tell me what would be the output.

More elegant way to do this in Ruby

ruby syntax

I've started with Ruby and am finding new, shorter, elegant ways to write code everyday.