【问题标题】:Why don't events of dynamically added user control fire on postback?为什么动态添加的用户控件的事件不会在回发时触发?
【发布时间】:2011-08-04 21:07:59
【问题描述】:

关于 SO(1234 等)和网络上(12 等)有很多类似的问题,但没有一个相应的答案能说明问题我的情况。

我有一个由两个下拉列表组成的简单自定义用户控件。在一个下拉列表中选择一个值应该会导致另一个下拉列表的填充。只要我在 .aspx 代码中声明用户控件,一切都会按预期工作。

现在我想以编程方式在页面上添加用户控件(单击按钮时)。尽管正在添加控件,但在一个下拉列表中的选择只会导致回发,而不会导致另一个下拉列表的操作。

在调试时,我发现不仅OnSelectedIndexChanged 不会触发,OnLoad 和所有其他事件也会触发。

在我浏览过的所有讨论中,出现这种行为的常见原因如下:

  1. DropDownListAutoPostBack 未设置为 true,或者数据绑定的DropDownList 在每次回发时都被反弹,导致事件丢失。 //这里不是个例,更可能是指动态添加的下拉列表

  2. ID 在每次回发时自动分配给动态添加的控件(并且每次都分配一个不同的控件,这样ViewState 就不会正确持久化,并且事件不知道应该触发)。 // 好的,我已经检查过了,现在手动分配 ID

  3. 该控件只添加一次(与每次回发时添加相反,这是必要的,因为在ViewState 中仅存储服务器控件的状态(值),而不是控件本身)和/或

  4. 在每次回发时添加控件,但在页面生命周期中为时已晚。 //好的,我现在在OnInit事件处理程序中添加我的控件

为了让页面知道添加了控件(以及添加了多少),我使用Session。下面是一些代码,最后是问题:)

.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>

以及背后的代码:

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();
        }

(我很确定用户控件本身的代码与案例并不真正相关,但我可以稍后根据要求添加)。

所以现在的问题是:通过反复试验,我发现如果我将新添加的控件存储在 SessionOnInit 的集合中,只需从该集合中取出控件并再次添加到控件中我的占位符的集合,在下一次回发时不会触发此控件的任何事件(与调用回发的方式无关)。否则,如果我 创建 OnInit 为每个存储在 Session 中的新控件,并将这个新创建的控件添加到占位符控件集合中 - 一切正常!那么存储在Session 控件中有什么问题,为什么它们会丢失事件?

还有一个小问题。为此类控件创建 ID 的最佳做法是什么?我使用特定格式的字符串和计数器,但我怀疑这是最好的方法。例如,如果我添加了不同类型的控件,我会遇到这种方法的麻烦。

感谢大家阅读这么长的问题并提出宝贵意见!

【问题讨论】:

    标签: asp.net events session user-controls


    【解决方案1】:

    我的理解是:必须在每次回发时重新建立事件。如果在 ASPX 文件中定义了事件,则您可以免费获得此功能,因为在实例化页面对象时会重新处理这些属性(OnClick、OnSelectionChanged 等)。

    动态构建的控件或在 ASPX 文件中定义但在代码隐藏中关联事件的控件不是免费的。对于这些控件,事件在实例化时是未知的,因此页面无法自动为您重新建立它们。对于这些控件,您必须在每次回发时重新建立您的事件。

    我对会话存储数据的理解是:当您将一个对象放入会话中时,该对象本身不会在内存中保持活动状态。相反,它是序列化的,序列化的数据存储在会话中。当您从会话中取出对象时,它会被反序列化。

    对象的事件在序列化/反序列化往返过程中无法幸存,因为它们在逻辑上是函数指针 - 所以当对象从内存中删除并稍后重新构建时,整个内存“景观”已经改变,所以这些事件/指针将不再有效。

    我没有对动态生成的控件做很多工作,所以我无法告诉您管理它们及其事件的最有效模式。但是我知道将它们存储在会话中并没有给你带来任何好处,而不是在每次回发时重新创建它们。它不必要地膨胀了会话内存。希望此线程上的其他一些 SO'er 可以为您指出管理它们的最佳实践方式。

    【讨论】:

    • 感谢您的全面回答。现在不仅是为什么而且它到底是如何不工作的很清楚:)
    【解决方案2】:

    控件事件在存储在会话中时不起作用,因为它们现在正在引用不再存在的页面实例。

    【讨论】:

    • 这个问题有解决办法吗?或者我应该简化事物并存储在 Session 中,而不是控件,而只是计数器,并且每次都重新创建相应数量的控件?
    • 您应该每次都重新创建控件。您是否有理由避免这样做?
    • 没有特别的原因,我只是想知道。答案是如此简单,以至于我提出了这么长的问题来得到它,我觉得有点愚蠢:) 所以我会等一下,以防没有人提出更复杂的建议,我会接受它。谢谢。
    【解决方案3】:

    让我重新表述一下我认为你在说什么(在简要查看代码之后):

    当我将用户控件 (ASCX) 动态添加到页面时,用户控件上的控件不会触发。

    如果是这样,则用户控件的代码是相关的,因为它应该处理从其控件触发的事件。

    如果没有,那么您将服务器控件(Microsoft 创建的或您创建的)与代理连接起来以处理事件,因为在拖放控件时缺少“我会为您施展魔法”步骤一个页面。

    我至少离目标很近吗?

    【讨论】:

    • 感谢您的回答,但它并没有真正回答我的实际问题(在长篇介绍的末尾找到它):)
    【解决方案4】:

    您必须在控件中重新创建视图状态才能解决问题

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2011-10-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-03-02
      • 2012-10-14
      相关资源
      最近更新 更多