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();
- 当您在
PreInit
或Init
事件中创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
方法是在 PreInit
和Init
事件之后但在Load
事件之前调用的。 Load
事件被认为是稳定的,因为在这种情况下,您可以访问控件的视图状态。 另外请注意, RaisePostBackEvent
方法表示引起回发的控制事件,这可以是, SelectedIndexChanged
, Click
等,这个事件在Load
事件之后被处理
有关完整的详细规范,请阅读MSDN页面生命周期文档
我通常看到这是由页面生命周期问题引起的。 如果控件仅在触发事件时创build,那么当索引更改事件触发时,控件不存在将其绑定到回发上。
例:
- MyEvent触发。 下拉创build。 事件处理程序指定。
- 索引已更改的事件已触发。 页面重新加载。 下拉不到,不能开火。
您必须确保在.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"; } }