ViewState和动态删除的控件

时间:2009-03-13 14:46:17

标签: asp.net dynamic controls viewstate

ASP.NET页面的ViewState似乎无法跟上动态删除的控件及其中的值。

我们以下面的代码为例:

ASPX:

<form id="form1" runat="server">
<div>
    <asp:Panel runat="server" ID="controls" />
</div>
</form>

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

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应该保存控件的属性与其索引的关系,而不是实际的控件。

2 个答案:

答案 0 :(得分:6)

夫妻俩。是否通过其ID或索引加载控件取决于ViewStateModeById属性。默认情况下,它为false(表示按索引加载)。

但是,文本框的处理方式不同。除非它们被禁用或不可见,否则它们的视图状态不包含输入值。 text属性会被使用其ID的已发布值覆盖。由于您不管理文本框ID,因此会发生这种情况。

添加了四个控件后,您有四个文本框:ctrl0,ctrl1,ctrl2和ctrl3,值分别为1,2,3和4.

接下来,删除ctrl1框,客户端获得三个框:ctrl0,ctrl2和ctrl3,具有相应的值。现在,当您进行任何回发时,这三个值将以ctrl0 = 1&amp; ctrl2 = 3&amp; ctrl3 = 4的形式提交。

然后,在Page_Load上,你创建了三个控件,这次是:ctrl0,ctrl1,ctrl2没有值。

Framework调用LoadRecursive来加载视图状态,然后调用ProcessPostData来分配输入值。它看到提交的ctrl0和ctrl2,找到具有相同id的控件并为它们赋值1和3.它没有找到ctrl3,所以它会跳过它。剩下的ctrl1只进行任何值。

作为一个例子,考虑这个解决方案(不是最好的):

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)
    {
        List<string> ids = (List<string>)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("<br />"));
        }
    }
}

protected void removeOnClick(object sender, EventArgs e)
{
    Control btn = sender as Control;

    List<string> ids = (List<string>)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<string> ids;

    if (ViewState["controls"] == null)
        ids = new List<string>();
    else
        ids = (List<string>)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("<br />"));

    ids.Add(id);

    ViewState["controls"] = ids;
}

答案 1 :(得分:0)

您在Page_Load中执行了一半代码,在回发事件中执行了一半代码。在这个烂摊子的某个地方,你遇到了冲突。他们直到第二次回发才出现的事实告诉我你的逻辑中有一些运行。

我不确定问题究竟发生在哪里,但当您使用自定义ViewState crud时,ViewState处理并不是世界上最有趣的事情。我将不得不重建应用程序并设置监视条件以查看发生了什么,但我相当确定它是Page_Load代码与事件处理程序之间的阻碍不匹配。