ean5533
ean5533

Reputation: 8994

Dynamic controls: associated events don't fire if recreated during Page_Load

Disclaimer: I have read the ASP.net page life cycle, and I've also read several good articles pertaining to dynamic controls, but I must be missing something.

Background: I am currently working on a website which needs to create a lot of dynamic content based on the user's input. I understand that, in order for dynamic controls to persist and for their events to wire up correctly, I need to re-create those dynamic controls on every page post-back.

Because of the nature of my project, my code doesn't know WHAT controls to create unless it has some information about what the user selected. I'm storing the user's choices in the ViewState, which is unavailable in Page_Init because it has not yet loaded. Consequently, I have to wait until Page_PreLoad or Page_Load to read the ViewState and then re-create my dynamic controls.

The part I don't understand: When I try re-creating my controls during Page_Load, the controls persist but the associated events don't seem to fire. For example, clicking on a LinkButton I created does not fire the method that I wired to its Click event, even though the button itself does persist.

A strange solution that I discovered by accident is that I can instead re-create my controls during Page_PreLoad and then the events fire correctly.

My question (or questions, rather): Why does my problem appear to go away by re-creating my controls during Page_PreLoad instead of Page_Load? Is this a safe practice? I've never seen any other code that used Page_PreLoad, which makes me wary. This solution is working for me, but are there any pitfalls I might be missing? Am I unknowingly setting myself up for failure later on?

My code, where LoadDocument() is a method that creates controls and stuffs them into a static Panel control:

protected void Page_PreLoad(object sender, EventArgs e)
{
    if (ViewState["xmlfilename"] != null)
    {
        LoadDocument(ViewState["xmlfilename"].ToString());
    }
}

Upvotes: 1

Views: 1064

Answers (2)

Jupaol
Jupaol

Reputation: 21365

Probably you read one of my answers on this topic:

I can tell you that I have code on production using the PreLoad event and it has worked fine

But for new development I am using the Init event, Why? Because it is the Microsoft recommendation and therefore it can be considered as an standard, and the technical benefits such the automatic load of the ViewState, theme support, and the most important (from my point of view), the dynamic controls events are sync with the static controls.

Your concern is right, in the Init event the ViewState has not been loaded yet, but that doesn't stop you to access the Form collection

I created a page for learning purpose where I'm creating dynamic controls on demand, and I'm doing it in the Init event. I'm creating TextBoxes and on each post they raise their TextChanged event when the text is changed.

NOTE: before continue I would like to remind you that the ViewState is loaded matching the control's ID's, that's why it's a good practice to re-create always the dynamic controls using the same ID

This is the code:

ASPX

<asp:HiddenField runat="server" ID="numberOfDynamicControls" Value="0" />
<asp:Panel runat="server" ID="myPanel">
</asp:Panel>
<asp:Button Text="Add Control" runat="server" ID="addControl" OnClick="addControl_Click" />
<asp:Label ID="lblMessage" runat="server" />

ASPX code behind

    protected void Page_Init(object sender, EventArgs e)
    {
        this.CreateDynamicControls();
    }

    protected void addControl_Click(object sender, EventArgs e)
    {
        var n = int.Parse(this.numberOfDynamicControls.Value);
        n++;
        this.numberOfDynamicControls.Value = n.ToString();
        this.myPanel.Controls.Add(this.CreateTextbox(n));
    }

    private void CreateDynamicControls()
    {
        int n = 0;

        if (!string.IsNullOrWhiteSpace(this.Request.Form["numberOfDynamicControls"]))
        {
            n = int.Parse(this.Request.Form["numberOfDynamicControls"]);
        }

        for (int i = 0; i < n; i++)
        {
            var t = this.CreateTextbox(i + 1);
            t.TextChanged += (x, y) => this.lblMessage.Text += "<br/>" + (x as TextBox).ID + " " + (x as TextBox).Text;

            this.myPanel.Controls.Add(t);
        }
    }

    private TextBox CreateTextbox(int index)
    {
        var t = new TextBox { ID = "myTextbox" + index.ToString(), Text = "de" };

        return t;
    }

Upvotes: 1

nunespascal
nunespascal

Reputation: 17724

Your events are processed during the ProcessPostData. Which control triggered the postback, too is post data. If your control does not exist at that time, it will not receive the event.

I agree Init would be too early, and Load too late.

What you need to do here is create these controls as soon as your view state is loaded.

There is no event for this in the Page life cycle. However all the functions being virtual, you can override the functions called in between.

The best place to load such controls that depend on values stored in the ViewState is the LoadViewState function.

  • Override this function.
  • Remember to call base.LoadViewState at the very start.
  • Create your controls depending on the ViewState values.

Now all your controls events should fire properly.

Upvotes: 1

Related Questions