dynamic添加DropDownlists不会触发SelectedIndexChanged事件

我看到很多关于这个话题的东西,但我不能find一个解决scheme。 我添加了许多下拉列表与一个事件,但他们并没有发射SelectedIndexChanged evet。 这里是drplist的创build者代码:

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 } 

我错过了什么?

这是一个常见的问题,它与页面生命周期有关:

看看下面的问题:

点击buttonarrays上的事件

点击事件后buttonarrays消失

dynamic创build一个ImageButton

现在创builddynamic控件时要记住的基本步骤是:

  • 应该在PreInit事件中创builddynamic控件,如果您不使用母版页,则在Init事件中创build控件
  • 避免设置这些事件中每个post中可以更改的属性,因为应用视图状态(在事件后),属性将被覆盖
  • dynamic控件必须在每次发布页面时创build ,避免这个if(!this.IsPostBack) this.CreatemyDynamicControls();
  • 当您在PreInitInit事件中创build控件时,它们的状态将自动设置为后期事件,这意味着在LoadComplete事件中,即使在每个post中再次创build它们,甚至当您没有明确设置他们的状态。 请注意,在处理在devise时创build的控件时,这种行为是不同的,在这种情况下,已经设置状态的事件是Load事件
  • 事件订阅应该在PageLoadComplete之前发生,否则将不会被提出

考虑以下来自MSDN的描述

如果控件是在运行时dynamic创build的,或者是在数据绑定控件的模板中声明式创build的,则它们的事件最初不会与页面上其他控件的事件同步。 例如,对于在运行时添加的控件,初始化和加载事件可能比页面生命周期中稍晚于声明性创build的控件的相同事件发生。 因此,从实例化的时间开始,dynamic添加的模板中的控件和控件一个接一个地提升它们的事件,直到它们赶上事件,并将其添加到Controls集合中。

以上对我不太清楚,但是我发现了以下几点。 以下TextBox是在devise时创build的

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

乍一看,你可能会认为在每个post中,所有的文本框都会更新当前的date,但事实并非如此,因为它们是在devise时创build的,所以严格遵循ASP.Net页面生命周期,也就是说, 它们的状态txtDesignTextBox3 和Init事件之后被覆盖 ,只有txtDesignTextBox3在每个post中被更新,因为它的Text属性在视图状态被设置(在Load事件中) 之后被更新。

但是对于dynamic控件的行为是不同的,请记住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); } 

在这种情况下,控件的行为稍有不同,在这种情况下,在每个post中, 控件永远不会更新,即使在Load事件中创build的控件

原因在于它们的生命周期事件发生在页面生命周期的很晚的地方,这意味着它们的状态即使在Load事件之后被覆盖

为了解决这个问题,你可以使用LoadComplete事件,在这种情况下你可以改变dynamic控件的状态:

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

在这种情况下,状态将在每个post中更新。

但是,考虑到您应该在LoadComplete事件之前订阅dynamic控件事件,否则将不会引发它们。

…我知道我讨厌这种行为,这就是为什么我喜欢MVC

作为在devise时创build的控件的快速参考:请注意LoadViewState方法是 PreInitInit事件之后但在Load事件之前调用的。 Load事件被认为是稳定的,因为在这种情况下,您可以访问控件的视图状态。 另外请注意, RaisePostBackEvent方法表示引起回发的控制事件,这可以是, SelectedIndexChangedClick等,这个事件在Load事件之后被处理

在这里输入图像描述

有关完整的详细规范,请阅读MSDN页面生命周期文档

我通常看到这是由页面生命周期问题引起的。 如果控件仅在触发事件时创build,那么当索引更改事件触发时,控件不存在将其绑定到回发上。

例:

  1. MyEvent触发。 下拉创build。 事件处理程序指定。
  2. 索引已更改的事件已触发。 页面重新加载。 下拉不到,不能开火。

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

你错过了:
1-覆盖SaveViewState
2-覆盖LoadViewState

我为这个问题提供了一个示例代码。 我testing它。 是工作。

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"; } }