在Asp.Net中动态添加控件

时间:2008-09-22 06:07:54

标签: asp.net web-user-controls

我正试图围绕asp.net。我有一个很长时间的PHP开发人员的背景,但我现在面临学习asp.net的任务,我遇到了一些麻烦。这可能是因为我试图强迫框架进入它不适合的东西 - 所以我想学习如何“正确的方式”。 : - )

我的问题是如何在运行时以编程方式向页面添加控件。据我所知,你需要在page_init创建控件,否则它们会在下一个PostBack中消失。但很多时候我遇到的问题是我不知道要在page_init中添加哪些控件,因为它依赖于之前PostBack的值。

一个简单的场景可能是在设计器中添加了下拉控件的表单。下拉列表设置为AutoPostBack。当发生PostBack时,我需要根据下拉控件中的所选值来渲染一个或多个控件,并且最好使这些控件的行为好像它们已经被设计添加(如“回发时,行为正确”)。

我在这里走错路吗?

9 个答案:

答案 0 :(得分:8)

我同意这里提出的其他观点“如果你可以动态地创建控件,那么就这样做......”(由@ Jesper Blad Jenson aka)但这是我用动态创建的控件制作的技巧在过去。

问题变成了鸡肉和鸡蛋。您需要使用ViewState来创建控制树,并且需要创建控制树以获取ViewState。嗯,这几乎是正确的。在填充树的其余部分之前,有一种方法可以在之前获取ViewState值。这是通过覆盖LoadViewState(...)SaveViewState(...)

在SaveViewState中存储您要创建的控件:

protected override object SaveViewState()
{
    object[] myState = new object[2];
    myState[0] = base.SaveViewState();
    myState[1] = controlPickerDropDown.SelectedValue;

    return myState
}

当框架调用“LoadViewState”覆盖时,您将返回从“SaveViewState”返回的确切对象:

protected override void LoadViewState(object savedState) 
{
    object[] myState = (object[])savedState;

    // Here is the trick, use the value you saved here to create your control tree.
    CreateControlBasedOnDropDownValue(myState[1]);

    // Call the base method to ensure everything works correctly.
    base.LoadViewState(myState[0]);
}

我成功地使用它来创建ASP.Net页面,其中DataSet被序列化到ViewState以存储对整个数据网格的更改,允许用户使用PostBacks进行多次编辑,最后将所有更改提交到一个“保存”操作。

答案 1 :(得分:3)

您必须在OnInit事件中添加控件,并且将保留viewstate。不要使用if(ispostback),因为每次都要添加控件,回发中的事件!
(De)viewInate的序列化发生在OnInit之后和OnLoad之前,因此如果在OnInit中添加它们,则viewstate持久性提供程序将看到动态添加的控件。

但是在您描述的场景中,可能是多视图或简单隐藏/显示(可见属性)将是更好的解决方案 这是因为在OnInit事件中,当您必须阅读下拉列表并添加新控件时,尚未读取(反序列化)viewstate,您不知道用户选择了什么! (你可以做request.form(),但感觉有点不对)

答案 2 :(得分:2)

在我解决这个问题的同时,我已经提出了这些似乎有效的基本规则,但是YMMV。

  • 尽可能使用声明性控件
  • 尽可能使用数据绑定
  • 了解ViewState的工作原理
  • Visibilty属性可以有很长的路要走
  • 如果必须在事件处理程序中使用添加控件,请使用Aydsman的提示并在重写的LoadViewState中重新创建控件。

TRULY Understanding ViewState是必读的。

Understanding Dynamic Controls By Example显示了一些关于如何使用数据绑定而不是动态控件的技术。

TRULY Understanding Dynamic Controls还阐明了可用于避免动态控制的技术。

希望这有助于其他人遇到同样的问题。

答案 3 :(得分:1)

如果您确实需要使用动态控件,则以下内容应该有效:

  • 在OnInit中,重新创建完成上一个请求时页面上完全相同的控制层次结构。 (如果这不是初始请求,当然)
  • 在OnInit之后,框架将从上一个请求加载视图状态,并且所有控件现在应该处于稳定状态。
  • 在OnLoad中,删除不需要的控件并添加必要的控件。此时,您还必须以某种方式保存当前控制树,以便在以下请求期间的第一步中使用。您可以使用会话变量来指示动态控制树的创建方式。我甚至将整个Controls集合存储在会话中一次(将你的干草叉放在一边,它只是用于演示)。

重新添加你不需要的“陈旧”控件,无论如何都会在OnLoad上删除它看起来有点古怪,但是Asp.Net并没有真正考虑到动态控件创建。如果在视图状态加载期间未保留完全相同的控件层次结构,则会在页面中隐藏所有类型的难以发现的错误,因为旧控件的状态会加载到新添加的控件中。

阅读Asp.Net页面生命周期,特别是关于viewstate的工作方式,它将变得清晰。

编辑:这是一篇关于视图状态行为以及在处理动态控件时应该考虑的内容的非常好的文章:http://geekswithblogs.net/FrostRed/archive/2007/02/17/106547.aspx

答案 4 :(得分:0)

好。如果你可以动态地创建控件,那么这样做 - 否则,我应该做的是使用Page_Load而不是Page_Init,而不是将东西放在If Not IsPostBack中,然后直接在方法中设置我。

答案 5 :(得分:0)

我认为这里的答案是在MultiView控件中,因此例如下拉菜单在多视图中的不同视图之间切换。

您甚至可以将多视图的当前视图属性数据绑定到下拉列表的值!

答案 6 :(得分:0)

啊,这就是ASP.NET Web表单漏洞抽象的问题。

也许你会对看看用于创建这个stackoverflow.com网站的ASP.NET MVC感兴趣吗?这应该是一个更容易适合你,来自PHP(因此,当涉及到HTML和Javascript时,踏板到金属)的背景。

答案 7 :(得分:0)

Aydsman给出了唯一正确的答案。 LoadViewState是添加动态控件的唯一位置,在重新创建时将恢复其视图状态值,您可以访问视图状态以确定要添加的控件。

答案 8 :(得分:0)

我在动态控件创建部分的“Pro ASP.NET 3.5 in C#2008”中讨论了这个问题:

  

如果需要多次重新创建控件,则应在Page.Load事件处理程序中执行控件创建。这样做的另一个好处是允许您在动态控件中使用视图状态。即使视图状态通常在Page.Load事件之前恢复,如果在Page.Load事件的处理程序中创建控件,ASP.NET将在Page.Load事件处理程序结束后应用它具有的任何视图状态信息。这个过程是自动的。

我没有对此进行测试,但您可能会对其进行调查。