为什么动态添加用户控件的事件不会在回发时触发?

时间:2011-08-04 21:08:00

标签: asp.net events session user-controls

关于SO(1234等)以及网络(1,{{}上有很多类似的问题3}}等)但没有相应的答案澄清我的情况。

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

现在我想以编程方式在页面上添加用户控件(单击按钮)。虽然添加了控件,但是在一个下拉列表中的选择只会导致回发但不会导致其他下拉列表的操作。

调试时我发现不仅OnSelectedIndexChanged不会触发,还会OnLoad以及所有其他事件。

我所看到的所有讨论的这种行为的常见原因如下:

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

  2. DropDownList会在每次回发时自动分配给动态添加的控件(每次都有不同的回复,以便ID未正确保留且事件不知道应该触发) 。 //好的我已经检查过,现在手动分配ID

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

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

  5. 为了让页面知道控件已添加(以及有多少)我使用OnInit。下面是一些代码然后最后的问题:)

    的.aspx:

    Session

    和背后的代码:

    <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(); } Session中的集合中,只需将控件从此集合中删除并添加即可再次到我的占位符的控件集合,没有此控件的事件触发下一个回发(独立于回调的方式)。其他方式如果我创建 OnInit每个存储在OnInit中的新控件,并添加到占位符控件集合这个新创建的控件 - 一切正常!那么存储在Session控件中有什么问题,为什么他们会丢失事件呢?

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

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

4 个答案:

答案 0 :(得分:1)

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

答案 1 :(得分:1)

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

使用动态构建的控件或ASPX文件中定义的控件,但在代码隐藏中将其事件连接起来,您不会免费获得此功能。对于那些控件,事件在实例化时不知道,因此页面无法自动为您重新建立它们。对于这些控件,您必须在每次回发时重新建立事件。

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

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

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

答案 2 :(得分:0)

让我重新说一下我的想法(在简要查看代码之后):

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

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

如果没有,那么你将服务器控件(微软创建或你创建的)与代表连接起来处理事件,因为当你拖放控件时你错过了“我会为你做魔术”的步骤一页。

我是否至少接近目标?

答案 3 :(得分:-1)

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