开发者

Why don't events of dynamically added user control fire on postback?

There are plenty of similar questions on SO (1, 2, 3, 4 etc) and on the web (1, 2 etc) but none of the corresponding answers clarifies my situation.

I have a simple custom user control consisting of two drop-down lists. Selection of a val开发者_开发问答ue in one drop-down list should lead to the populating of the other one. As long as I declare the user control in the .aspx code everything works as expected.

Now I want to add the user control on the page programmatically (on button click). Although the control is being added, the selection in one drop-down list causes only a postback but no action of the other drop-down list.

While debugging I've found out that not only OnSelectedIndexChanged does not fire, but also OnLoad and all the other events.

Common reasons for such a behaviour regarding all the discussions I've looked through are the following:

  1. AutoPostBack of the DropDownList is not set to true or the databound DropDownList is being rebound on each postback which causes the losing of events. //not a case here, more likely refers to dynamically added drop-down lists

  2. ID is assigned to the dynamically added control automatically on each postback (and every time a different one so that ViewState is not persisted correctly and events do not know that they should fire). // ok I've checked it and now assign the ID manually

  3. The control is added only once (as opposed to the addition on every postback which is necessary because in the ViewState only the state (values) of server controls are stored, but not the controls themselves) and/or

  4. the control is added on each postback but too late in the Page Life Cycle. //ok I'm adding my control in OnInit event handler now

In order to let the page know that the control was added (and how many of them) I use Session. Below a bit of code and then finally the question :)

.aspx:

<asp:Content ID="Content3" ContentPlaceHolderID="MainContent" runat="server">
    <asp:PlaceHolder ID="PlaceHolder1" runat="server"></asp:PlaceHolder>
    <asp:Button ID="Button1" runat="server" Text="Button" onclick="Button1_Click" />
</asp:Content>

and the code behind:

protected void Page_Load(object sender, EventArgs e)
        {
            if(!this.IsPostBack)
            {
            Session.Remove("Childcontrols");
            }
        }

        private void AddTransitControl()
        {
            List<Control> controls = (List<Control>)Session["Childcontrols"];
            AddTransitPoint atp = (AddTransitPoint)LoadControl("~/UserControls/AddTransitPoint.ascx");
            string id = this.ID + "_eb" + (controls.Count).ToString();
            atp.ID = id;
            controls.Add(atp);

            PlaceHolder1.Controls.Add(atp);
        }

        protected override void OnInit(EventArgs e)
        {
            base.OnInit(e);

            if (Page.IsPostBack)
            {
                if (Session["Childcontrols"] == null)
                {
                    Session["Childcontrols"] = new List<Control>();
                }

                List<Control> controls = (List<Control>)Session["Childcontrols"];
                int count = 0;
                foreach (Control c in controls)
                {
//                    AddTransitPoint atp = (AddTransitPoint) c; //mystically not working (fires no events)
                    AddTransitPoint atp = (AddTransitPoint)LoadControl("~/UserControls/AddTransitPoint.ascx"); //it is working!

                    string id = this.ID + "_eb" + count;
                    count++;
                    atp.ID = id;
                    PlaceHolder1.Controls.Add(atp);
                }
            }
        }

        protected void Button1_Click(object sender, EventArgs e)
        {
            AddTransitControl();
        }

(I'm pretty sure that the code of the user control itself is not really relevant for the case but on request I can add it later).

So now the question: Through trial and error I've found out that if I store the newly added control in the collection in Session and OnInit just take the control out of this collection and add again to the control collection of my placeholder, no events of this control fire on the next postback (independent of the way the postback is called). Other way if I create OnInit a new control for each stored in Session and add to the placeholder control collection this newly created control - everything works! So what is wrong with stored in the Session controls and why do they lose their events?

And one more small question here. What is the best practice for creating IDs for such controls? I use a string of particular format along with a counter, but I doubt it is the best way to do it. For example if I added different types of controls I would face trouble with this method.

Thank you everybody for reading such a long question and for your valuable input!


My understanding is this: events must be re-established on every postback. You get this for free if the event is defined in the the ASPX file, because those attributes (OnClick, OnSelectionChanged, etc.) are re-processed when the page object is instantiated.

You do not get this for free with dynamically-built controls, or controls which are defined in the ASPX file but have their events wired-up in the codebehind. For those controls, the events are not known at instantiation time, thus the page can not automatically re-establish them for you. For those controls, you must re-establish your events on every postback.

My understanding of session-stored data is: when you put an object in the session, that object itself is not kept alive in memory. Instead, it is serialized, and the serialized data is stored in the session. When you get the object back out of the session, it is deserialized.

Objects' events do not survive the serialization/deserialization round-trip because they are logically function pointers - so when the object is removed from memory and later re-built, the entire memory "landscape" has changed, so those events/pointers would no longer be valid.

I haven't done a lot of work with dynamically-generated controls, so I can't tell you the most effective pattern to managing them and their events. But I know that storing them in the session isn't really buying you any benefit over re-creating them on each postback; and it is bloating up the session memory unnecessarily. Hopefully some other SO'ers on this thread can point you to the best-practices way of managing them.


The controls events don't work when stored in the session because they are now referencing an instance of the page which no longer exists.


Let me rephrase what I think you are saying (after a brief look at the code):

When I add a user control (ASCX) to a page dynamically, the controls on the user control do not fire.

If so, the code for the user control is relevant, because it should be handling the events fired from its controls.

If not, then you wire up the server controls (Microsoft created or you created) with delegates to handle the event, as you are missing the "I'll do the magic for you" step when dragging and dropping a control on a page.

Am I at least somewhere close to the target?


You will have to recreate the viewstate within the control to solve the problem

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜