ASP.NET 动态控件计数(随着操作创建控件)

5
我正在尝试创建一个复合ASP.NET控件,它可以让您构建可编辑的控件集合。
我的问题是,当我按下添加或postback按钮(除了postback表单之外什么也不做)时,输入框中输入的任何值都会丢失。
当控件数量在postbacks之间发生变化时,我无法使其正常工作。基本上,我需要能够在控件生命周期的两个不同时间点(取决于视图状态属性ControlCount)重新创建控件树。
以下测试可用于重现此问题:
public class AddManyControl : CompositeControl
{
    protected override void OnLoad(EventArgs e)
    {
        base.OnLoad(e);
        EnsureChildControls();
    }

    protected override void CreateChildControls()
    {
        var count = ViewState["ControlCount"] as int? ?? 0;

        for (int i = 0; i < count; i++)
        {
            var div = new HtmlGenericControl("div");
            var textBox = new TextBox();
            textBox.ID = "tb" + i;
            div.Controls.Add(textBox);
            Controls.Add(div);
        }

        ViewState["ControlCount"] = count;

        var btnAdd = new Button();
        btnAdd.ID = "Add";
        btnAdd.Text = "Add text box";
        btnAdd.Click += new EventHandler(btnAdd_Click);
        Controls.Add(btnAdd);

        var btnPostBack = new Button();
        btnPostBack.ID = "PostBack";
        btnPostBack.Text = "Do PostBack";
        Controls.Add(btnPostBack);
    }

    void btnAdd_Click(object sender, EventArgs e)
    {
        ViewState["ControlCount"] = (int)ViewState["ControlCount"] + 1;
    }

    protected override void OnPreRender(EventArgs e)
    {
        base.OnPreRender(e);
        // If I remove this RecreateChildControls call
        // the collection lags behind each postback
        // because the count is incremented in the btnAdd_Click event handler
        // however, the values are not lost between postbacks
        RecreateChildControls();
    }
}
2个回答

5
如果您想使用ASP.NET的自定义控件,就必须按照其规则和挑剔的要求进行。当您开始在自定义控件中使用OnPreRender时,90%的情况下您会走上错误的道路。
一般来说,最好的使用ViewState的方法是声明由它支持的属性,就像标准的ASP.NET控件一样(.NET Reflector已经是我的老师多年了!)。这样,在事件生命周期中,它将自然地被读取和保存。
这里有一个看起来能够自然地做到您想要的事情的代码,没有任何技巧:
public class AddManyControl : CompositeControl
{
    private void AddControl(int index)
    {
        var div = new HtmlGenericControl("div");
        var textBox = new TextBox();
        textBox.ID = "tb" + index;
        div.Controls.Add(textBox);
        Controls.AddAt(index, div);
    }

    protected override void CreateChildControls()
    {
        for (int i = 0; i < ControlsCount; i++)
        {
            AddControl(i);
        }

        var btnAdd = new Button();
        btnAdd.ID = "Add";
        btnAdd.Text = "Add text box";
        btnAdd.Click += new EventHandler(btnAdd_Click);
        Controls.Add(btnAdd);

        var btnPostBack = new Button();
        btnPostBack.ID = "PostBack";
        btnPostBack.Text = "Do PostBack";
        Controls.Add(btnPostBack);
    }

    private int ControlsCount
    {
        get
        {
            object o = ViewState["ControlCount"];
            if (o != null)
                return (int)o;

            return 0;
        }
        set
        {
            ViewState["ControlCount"] = value;
        }
    }

    void btnAdd_Click(object sender, EventArgs e)
    {
        int count = ControlsCount;
        AddControl(count);
        ControlsCount = count + 1;
    }
}

由于每次发生回传时,所有的“文本框”都是从头开始添加的,那么它们如何恢复其文本值呢?难道是ASP.NET做了什么吗? - Rami Alshareef
这真是太微妙了,毫无疑问。但你所说的是,我只需要在 PreRender 期间避免重新创建控件集合,如果我在事件处理完成之后再进行,那就没问题了? - John Leidegren
我现在通过移动到PreRender事件之外使其正常工作,我也不能期望能够重新创建控件并保留视图状态。这真是太遗憾了。我必须仅在该事件期间添加新的控件才能让它正常工作。我只是想重新创建控件集合,并希望它能正常工作。但事实并非如此... - John Leidegren

-2

我相信你必须将控件添加到视图状态中。


不,它不是那样工作的。这是另一回事。由于控件树正在重新创建,某些生命周期事件第二次没有被运行。 - John Leidegren

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接