动态添加的下拉列表框无法触发SelectedIndexChanged事件

3

我看到了很多关于这个主题的内容,但是我找不到解决方案。 我添加了许多下拉列表框,并使用一个事件,但它们不会触发SelectedIndexChanged事件。 以下是drplist创建代码:

foreach (var row in cmdSelectCats.ExecuteReader())
{
    var id = row["ProductCategoryID"].ToString();

    var dropDownStatus = new DropDownList {ID = "DrpStatus-" + id};

    dropDownStatus.Items.Add(new ListItem("Aktif", "1"));
    dropDownStatus.Items.Add(new ListItem("Pasif", "2"));

    dropDownStatus.AutoPostBack = true; 
    dropDownStatus.SelectedIndexChanged += Status_SelectedIndexChanged;

    var tableCell = new TableCell();
    tableCell.Controls.Add(dropDownStatus);
    dropDownStatus.SelectedValue = row["ProductCategoryStatusID"].ToString();
    tableRow.Cells.Add(tableCell);
    TblCatList.Rows.Add(tableRow);
}

当然还有我的事件:
public void Status_SelectedIndexChanged(object sender, EventArgs e)
{
    //DO SOMETHING
} 

我错过了什么?


你在 Postback 后检查了控件的存在吗?当 页面生命周期结束 时,所有控件都将被 Disposed。每次 Postback 都需要重新创建 Dynamic Control 。上述代码应该在页面的 Initialization 事件中。 - Pankaj
3个回答

17
这是一个常见问题,与页面生命周期有关:
看一下以下问题: 单击数组按钮事件 单击事件后按钮数组消失 动态创建ImageButton 现在,在创建动态控件时需要记住的基本步骤是:
  • 如果您没有使用主页,动态控件应该在PreInit事件中创建;如果您使用了主页,则应该在Init事件中创建控件。
  • 在这些事件中避免设置可以在每个提交中更改的属性,因为当视图状态被应用(在提交事件中)时,属性将被覆盖。
  • 必须每次页面提交时都创建动态控件,请避免使用 if(!this.IsPostBack) this.CreatemyDynamicControls();
  • 当您在PreInitInit事件中创建控件时,它们的状态将自动在提交事件中设置,这意味着在LoadComplete事件中,即使您在每个提交中重新创建它们,甚至在您没有显式设置它们的状态时,您的控件也将包含它们的状态。请注意,在处理设计时创建的控件时,此行为是不同的,此时状态设置的事件是Load事件
  • 事件订阅应该在PageLoadComplete之前进行,否则它们将不会被触发。

请参考以下来自MSDN的描述

如果控件在运行时动态创建或在数据绑定控件的模板中声明,它们的事件最初与页面上其他控件的事件不同步。例如,在运行时添加的控件的 Init 和 Load 事件可能比相同事件的声明性创建控件的事件晚得多。因此,从它们被实例化的时刻起,动态添加的控件和模板中的控件依次引发它们的事件,直到它们赶上将其添加到 Controls 集合期间的事件。
以上内容对我来说不是很清晰,但我找到了以下内容。下面的TextBox是在设计时创建的。
    protected void Page_PreInit(object sender, EventArgs e)
    {
        this.txtDesignTextBox1.Text = "From PreInit";
        this.txtDesignTextBox1.Text += DateTime.Now.ToString();
    }

    protected void Page_Init(object sender, EventArgs e)
    {
        this.txtDesignTextBox2.Text = "From Init";
        this.txtDesignTextBox2.Text += DateTime.Now.ToString();
    }

    protected void Page_Load(object sender, EventArgs e)
    {
        this.txtDesignTextBox3.Text = "From Load";
        this.txtDesignTextBox3.Text += DateTime.Now.ToString();
    }

起初你可能认为每篇文章中的所有文本框都会更新为当前日期,但这并不是事实,因为它们在设计时创建,严格遵循ASP.Net页面生命周期,这意味着,在PreInit和Init事件之后,它们的状态被覆盖,只有txtDesignTextBox3在每篇文章中更新,因为它的Text属性在视图状态被设置之后(在Load事件中)更新。
但对于动态控件,行为是不同的,请记住MSDN的描述:
对于在运行时添加的控件,Init和Load事件可能会在页面生命周期中发生得更晚。
请考虑以下内容:
    protected void Page_PreInit(object sender, EventArgs e)
    {
        var textBox = new TextBox { Text = "From PreInit", Width = new Unit("100%") };
        textBox.Text += DateTime.Now.ToString();
        this.myPlaceHolder.Controls.Add(textBox);
    }

    protected void Page_Init(object sender, EventArgs e)
    {
        var textBox = new TextBox { Text = "From Init", Width = new Unit("100%") };
        textBox.Text += DateTime.Now.ToString();
        this.myPlaceHolder.Controls.Add(textBox);
    }

    protected void Page_Load(object sender, EventArgs e)
    {
        var textBox = new TextBox { Text = "From Load", Width = new Unit("100%") };
        textBox.Text += DateTime.Now.ToString();
        this.myPlaceHolder.Controls.Add(textBox);
    }

在这种情况下,控件的行为稍有不同,在每个帖子中,控件甚至包括在Load事件中创建的控件都不会被更新
原因是它们的生命周期事件在页面生命周期后期发生,这意味着它们的状态被覆盖,即使是在Load事件之后也是如此。
要解决这个问题,您可以使用LoadComplete事件,在该事件中可以更改动态控件的状态:
    protected void Page_LoadComplete(object sender, EventArgs e)
    {
        var textBox = new TextBox { Text = "From LoadComplete", Width = new Unit("100%") };
        textBox.Text += DateTime.Now.ToString();
        this.myPlaceHolder.Controls.Add(textBox);
    }

在这种情况下,状态将在每个帖子中更新。
但是,请注意,在LoadComplete事件之前订阅动态控件事件,否则它们将不会被引发。
作为设计时创建的控件的快速参考:请注意,LoadViewState方法在PreInitInit事件之后但在Load事件之前被调用。Load事件被认为是稳定的,因为在此事件中,您可以访问您的控件的视图状态。还要注意,RaisePostBackEvent方法表示导致回发的控件事件,这可以是SelectedIndexChangedClick等,此事件在Load事件之后处理。

enter image description here

如需完整详细规范,请阅读MSDN页面生命周期文档


非常感谢,这是一条非常有用的信息。 - gelupa
非常感谢!我的页面现在可以工作了。我只是在Page_LoadComplete()中添加了动态下拉列表中的项目,现在一切都正常了。 - te7

1

我通常看到这是由于页面生命周期问题引起的。如果控件仅在触发事件时创建,则当您的索引更改事件触发时,控件不存在以在回发上绑定它。

例如:

  1. MyEvent触发。下拉列表创建。指定事件处理程序。
  2. 索引更改事件被触发。页面重新加载。找不到下拉列表,无法触发。

您必须确保在.NET尝试处理事件之前创建下拉列表。


-4

你缺少了以下内容:
1- 覆盖 SaveViewState 方法
2- 覆盖 LoadViewState 方法

我为这个问题提供了一个示例代码。我测试过了,它是有效的。

ASPX:

<form id="form1" runat="server">
<asp:Button ID="Button1" runat="server" Text="Button" OnClick="Button1_Click" />
<div id="myDiv" runat="server">
</div>
<asp:Label ID="lblDescription" runat="server"></asp:Label>
</form>

后端代码:

public partial class Default : System.Web.UI.Page
    {
        private List<string> values = new List<string>();

        protected void Page_Load(object sender, EventArgs e)
        {

        }        

        protected override object SaveViewState()
        {
            object baseState = base.SaveViewState();            
            object[] allStates = new object[2];
            allStates[0] = baseState;
            allStates[1] = values;
            return allStates;
        }

        protected override void LoadViewState(object savedState)
        {
            object[] myState = (object[])savedState;
            if (myState[0] != null)
                base.LoadViewState(myState[0]);
            if (myState[1] != null)
            {
                values = (List<string>)myState[1];
                MyRender();
            }
        }

        protected void Button1_Click(object sender, EventArgs e)
        {
            for (int i = 0; i < 10; i++)
            {
                DropDownList ddl = new DropDownList();
                ddl.ID = "ClientID" + i;

                ddl.Items.Add("Item 1");
                ddl.Items.Add("Item 2");
                ddl.AutoPostBack = true;                
                values.Add(ddl.SelectedValue);

                myDiv.Controls.Add(ddl);
            }
        }

        private void MyRender()
        {
            for (int i = 0; i < values.Count; i++)
            {
                DropDownList ddl = new DropDownList();
                ddl.ID = "ClientID" + i;

                ddl.Items.Add("Item 1");
                ddl.Items.Add("Item 2");
                ddl.AutoPostBack = true;
                ddl.SelectedIndexChanged += new EventHandler(ddl_SelectedIndexChanged);

                myDiv.Controls.Add(ddl);
            }
        }

        void ddl_SelectedIndexChanged(object sender, EventArgs e)
        {
            lblDescription.Text = ((DropDownList)sender).ID + ": Selected Index Changed";
        }
    }

你不应该放置整个代码...节省资源...相反,应该发布一个解释你答案的链接。 - Haresh Chaudhary

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接