【问题标题】:ViewState and dynamically removed controlsViewState 和动态移除的控件
【发布时间】:2010-10-13 04:24:36
【问题描述】:

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

【问题讨论】:

    标签: asp.net dynamic controls viewstate


    【解决方案1】:

    几件事。控件是按其 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,没有值。

    框架调用 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;
    }
    

    【讨论】:

    • 我有一个相关的问题 - 我有一个依赖于组合回发的 gridview。当您更改组合时,网格会使用新组合值的数据进行刷新。但是,如果您点击刷新,网格将返回组合中第一个项目的默认数据,但组合将重置为刷新时选择的项目 - 因此网格和组合不同步。我已经尝试绑定并将组合设置为Page_Load if !postback 上的第一个项目,并且 html 显示顶部项目已装饰 selected="selected",但是一些 javascript 然后将其更改为非默认项目!这有关系吗?
    【解决方案2】:

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

    我不确定问题出在哪里,但是当您进行自定义 ViewState crud 时,ViewState 处理并不是世界上最有趣的事情。我必须重建应用程序并设置监视条件以查看发生了什么,但我相当确定这是您的 Page_Load 代码和事件处理程序之间的阻抗不匹配。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-03-20
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多