ASP.NET页面ViewState
似乎很难跟上动态删除的控件及其中的值.
我们以下面的代码为例:
ASPX:
CS:
protected void Page_Init(object sender, EventArgs e) { Button b = new Button(); b.Text = "Add"; b.Click +=new EventHandler(buttonOnClick); form1.Controls.Add(b); Button postback = new Button(); postback.Text = "Postback"; form1.Controls.Add(postback); } protected void Page_Load(object sender, EventArgs e) { if (ViewState["controls"] != null) { for (int i = 0; i < int.Parse(ViewState["controls"].ToString()); i++) { controls.Controls.Add(new TextBox()); Button remove = new Button(); remove.Text = "Remove"; remove.Click +=new EventHandler(removeOnClick); controls.Controls.Add(remove); controls.Controls.Add(new LiteralControl("
")); } } } protected void removeOnClick(object sender, EventArgs e) { Control s = sender as Control; //A hacky way to remove the components around the button and the button itself s.Parent.Controls.Remove(s.Parent.Controls[s.Parent.Controls.IndexOf(s) + 1]); s.Parent.Controls.Remove(s.Parent.Controls[s.Parent.Controls.IndexOf(s) - 1]); s.Parent.Controls.Remove(s.Parent.Controls[s.Parent.Controls.IndexOf(s)]); ViewState["controls"] = (int.Parse(ViewState["controls"].ToString()) - 1).ToString(); } protected void buttonOnClick(object sender, EventArgs e) { if (ViewState["controls"] == null) ViewState["controls"] = "1"; else ViewState["controls"] = (int.Parse(ViewState["controls"].ToString()) + 1).ToString(); controls.Controls.Add(new TextBox()); }
然后,假设您创建了4个控件并插入以下值:
[ 1 ] [ 2 ] [ 3 ] [ 4 ]
我们要删除第二个控件; 删除第二个控件后输出为:
[ 1 ] [ 3 ] [ 4 ]
这就是我们想要的.不幸的是,在随后的PostBack中,列表变为:
[ 1 ] [ ] [ 3 ]
所以,我的问题是,为什么会发生这种情况?据我所知,ViewState
应该保存控件的属性与其索引相关,而不是实际控件.
几件事.是否通过其ID或索引加载控件取决于ViewStateModeById属性.默认情况下,它为false(表示按索引加载).
但是,文本框的处理方式不同.除非它们被禁用或不可见,否则它们的视图状态不包含输入值.text属性会被使用其ID的已发布值覆盖.由于您不管理文本框ID,因此会发生这种情况.
添加四个控件后,您有四个文本框:ctrl0,ctrl1,ctrl2和ctrl3,其值分别为1,2,3和4.
接下来,删除ctrl1框,客户端获取三个框:ctrl0,ctrl2和ctrl3,具有相应的值.现在,当您进行任何回发时,这三个值将以ctrl0 = 1&ctrl2 = 3&ctrl3 = 4的形式提交.
然后,在Page_Load上,创建三个控件,这次:ctrl0,ctrl1,ctrl2没有值.
Framework调用LoadRecursive来加载视图状态,然后调用ProcessPostData来分配输入值.它看到提交的ctrl0和ctrl2,找到具有相同id的控件并为它们赋值1和3.它没有找到ctrl3,所以它会跳过它.剩下的ctrl1只执行任何值的w/o.
例如,考虑这个解决方案(不是最好的):
protected void Page_Init(object sender, EventArgs e) { Button b = new Button(); b.Text = "Add"; b.Click += new EventHandler(buttonOnClick); form1.Controls.Add(b); Button postback = new Button(); postback.Text = "Postback"; form1.Controls.Add(postback); } protected void Page_Load(object sender, EventArgs e) { if (ViewState["controls"] != null) { Listids = (List )ViewState["controls"]; for (int i = 0; i < ids.Count; i++) { TextBox textbox = new TextBox(); textbox.ID = string.Format("txt_{0}", ids[i]); textbox.Text = textbox.ID; controls.Controls.Add(textbox); Button remove = new Button(); remove.Text = "Remove"; remove.Click += new EventHandler(removeOnClick); remove.ID = ids[i]; controls.Controls.Add(remove); controls.Controls.Add(new LiteralControl("
")); } } } protected void removeOnClick(object sender, EventArgs e) { Control btn = sender as Control; Listids = (List )ViewState["controls"]; ids.Remove(btn.ID); //A hacky way to remove the components around the button and the button itself btn.Parent.Controls.Remove(btn.Parent.Controls[btn.Parent.Controls.IndexOf(btn) + 1]); btn.Parent.Controls.Remove(btn.Parent.Controls[btn.Parent.Controls.IndexOf(btn) - 1]); btn.Parent.Controls.Remove(btn); ViewState["controls"] = ids; } protected void buttonOnClick(object sender, EventArgs e) { List ids; if (ViewState["controls"] == null) ids = new List (); else ids = (List )ViewState["controls"]; string id = Guid.NewGuid().ToString(); TextBox textbox = new TextBox(); textbox.ID = string.Format("txt_{0}", id); textbox.Text = textbox.ID; controls.Controls.Add(textbox); Button remove = new Button(); remove.Text = "Remove"; remove.Click += new EventHandler(removeOnClick); remove.ID = id; controls.Controls.Add(remove); controls.Controls.Add(new LiteralControl("
")); ids.Add(id); ViewState["controls"] = ids; }