ASP.NET动态添加控件与页面生命周期

3
我对ASP.NET是全新的,所以请耐心等待。
以下是我情景的简化解释。我在页面上有一个按钮。当我点击该按钮时,我从数据库中检索一系列字符串,并动态生成一系列链接按钮,其中检索到的字符串作为链接按钮的文本。
我已经了解到由于几个原因,我不能在按钮的单击事件处理程序中执行此操作。 我还了解到,动态控件需要在Page_Init或Page_Load事件处理程序中生成,以便可以连接链接按钮的单击事件。
因此,这里是我考虑的过程:
1. 单击按钮 2. 在单击事件处理程序中,检索字符串并将其存储在ViewState变量中 3. 在Page_Load事件处理程序中,遍历ViewState变量中的字符串并生成链接按钮
问题在于:由于页面的生命周期,直到Page_Load事件之后才处理按钮的单击事件。因此,我用于生成链接按钮的字符串直到Page_Load事件之后才被检索和分配给ViewState变量(我希望在其中创建链接按钮)。
因此,在第二次单击按钮时才生成链接按钮,当ViewState变量被分配时。显然,这是一个问题。
我想出的解决方法可行,但我不知道它是否是最佳解决方案。这是我使用的方法:
1. 单击按钮 2. Page_Load事件触发 3. 在Page_Load事件处理程序中,如果是PostBack,则检查ViewState变量是否为空以及哪个控件触发了PostBack 4. 如果ViewState变量为空且单击了按钮,则检索字符串,将其保存到ViewState变量中,并基于ViewState变量生成链接按钮 5. 如果ViewState变量不为空,则基于ViewState变量生成链接按钮
现在,这个解决方案实际上是有效的。但是,如果我刷新页面,我会得到确认表单重新提交弹出窗口(我不喜欢,但我可以接受)。
我对此情况的问题是 - 你们这里的专业编码人员会如何处理?
public partial class _Default : System.Web.UI.Page
{
    protected void Page_Load(object sender, EventArgs e)
    {
        if (IsPostBack)
        {
            if(ViewState["ControlsArray"] != null)
            {
                ArrayList controlsList = (ArrayList)ViewState["ControlsArray"];
                GenerateLinkButtons(controlsList);
            }
        }
    }

    //The event handler for the dynamically generated linkbuttons
    void linkButton_Click(object sender, EventArgs e)
    {
        LinkButton btn = (LinkButton)sender;
        string btnName = btn.Text;

        Response.Write(btnName);
    }

    //The click event handler for the Generate Controls button
    protected void Button1_Click(object sender, EventArgs e)
    {
        ArrayList PageArrayList = new ArrayList();

        PageArrayList = new ArrayList();
        PageArrayList.Add("Text 1");
        PageArrayList.Add("Text 2");
        PageArrayList.Add("Text 3");
        PageArrayList.Add("Text 4");
        PageArrayList.Add("Text 5");

        GenerateLinkButtons(PageArrayList);

        ViewState["ControlsArray"] = PageArrayList;
    }

    void GenerateLinkButtons(ArrayList controlsList)
    {
        int i = 1;

        foreach (string s in controlsList)
        {
            LinkButton lnkButton = new LinkButton();
            lnkButton.ID = "btn" + i;
            lnkButton.Text = s;
            this.form1.Controls.Add(lnkButton);
            this.form1.Controls.Add(new LiteralControl("<br />"));
            lnkButton.Click += new EventHandler(linkButton_Click);
            i++;
        }
    }
}

为什么你不直接在按钮点击的事件处理程序中生成链接按钮?出于关注点分离的原因,你不应该在那里检索字符串的代码,但像生成按钮这样的UI交互是正确的。 - Axel
当然可以,但我的理解是你不能在单击事件处理程序中为新添加的控件连接事件处理程序。这必须在Page_Init或Page_Load事件处理程序中完成。 - Jeremy Choate
正如Mt.Scheniders在他的回复中所说,我也不这么认为。 - Axel
1个回答

2
动态控件需要在Page_Init或Page_Load事件处理程序中生成,以便可以连接链接按钮的单击事件。实际上,您可以在按钮单击事件中创建控件。但是,您必须确保这些控件在以下PostBacks中重新创建。假设您有一个名为Data的属性,该属性指向一个ViewState变量。还假设您正在使用Repeater来创建LinkButtons。那么,您可以像下面这样做:
private List<string> Data
{
    get { return (List<string>)ViewState["Data"]; }
    set { ViewState["Data"] = value; }
}

protected void Button_Click(object sender, EventArgs e)
{
    Data = GetData();
    LoadRepeater();
}

protected void Page_Load(object sender, EventArgs e)
{
    if(Data != null)
    {
        LoadRepeater();
    }
}

private void LoadRepeater()
{
    repeater.DataSource = Data;
    repeater.DataBind();
}

Repeater:

<asp:Repeater id="repeater" runat="server">
    <ItemTemplate>
        <asp:LinkButton id="link" runat="server" OnClick="link_Click" />
    </ItemTemplate>
</asp:Repeater>

protected void link_Click(object sender, EventArgs e)
{
    //Do Stuff
}

此外,如果您想避免“确认表单重新提交”的问题,请使用ASP.NET的UpdatePanel控件。 例如:

<asp:UpdatePanel id="upp" runat="server" >
    <ContentTemplate>
        <!-- Button-->
        <!-- Repeater-->
        <!-- Other Stuff-->
    </ContentTemplate>
</asp:UpdatePanel>

另一种方法(不确定是否有效):

尝试在标签定义上设置中继器数据源:

<asp:Repeater id="repeater" runat="server" DataSource='<%# Data %>' >
    <ItemTemplate>
        <asp:LinkButton id="link" runat="server" OnClick="link_Click" />
    </ItemTemplate>
</asp:Repeater>

protected void Button_Click(object sender, EventArgs e)
{
    Data = GetData();
    LoadRepeater();
}

protected void Page_Load(object sender, EventArgs e)
{
}

private void LoadRepeater()
{
    repeater.DataBind();
}

谢谢回复!我原本以为你可以在单击事件处理程序中创建控件,但是你只能在Page_Init或Page_Init事件处理程序中为这些控件添加事件处理程序。我的理解有误吗? - Jeremy Choate
@Jeremy Choate,我不这么认为。你有任何证据吗? - Mateus Schneiders

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