在Asp.Net中动态添加的控件

时间:2020-03-06 14:31:32  来源:igfitidea点击:

我试图把头包在asp.net上。我有很长一段时间的php开发人员背景,但是现在我面临着学习asp.net的任务,并且遇到了一些麻烦。这很可能是因为我试图将框架强加到它不打算使用的东西中,所以我想学习如何"正确地"做它。 :-)

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

一个简单的场景可能是在设计器中添加了下拉控件的表单。下拉菜单设置为AutoPostBack。当发生PostBack时,我需要呈现一个或者多个依赖于下拉控件中所选值的控件,并且最好使这些控件的行为就像设计中添加的一样(如"发回时,表现为"正确")。

我在这里走错了路吗?

解决方案

出色地。如果我们不能动态创建控件,则可以这样做,否则,我所要做的是使用Page_Load而不是Page_Init,而是将内容放置在If Not IsPostBack内,然后直接在方法中设置i。

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

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

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

也许我们会感兴趣的是用于创建该stackoverflow.com网站的ASP.NET MVC?这应该是更适合,因为它来自PHP(因此涉及HTML和Javascript)。

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

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

如果我们确实需要使用动态控件,则应执行以下操作:

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

重新添加不需要的并且无论如何都将在OnLoad上删除的"陈旧"控件似乎有些古怪,但是Asp.Net并不是在设计时就考虑到动态控件的创建。如果在加载视图状态期间未保留完全相同的控件层次结构,则所有难以找到的bug都将潜伏在页面中,因为较早的控件的状态已加载到新添加的控件中。

阅读有关Asp.Net页面生命周期的信息,尤其是有关viewstate如何工作的信息,它将变得更加清晰。

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

我同意这里提出的其他观点:"如果我们可以摆脱动态创建控件的行为,那就去做吧……"(@ Jesper Blad Jenson,又名),但这是我过去使用动态创建控件解决的一个技巧。

问题是鸡肉和鸡蛋。我们需要使用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页面,在该页面中,数据集被序列化为ViewState以存储对整个数据网格的更改,从而允许用户使用PostBacks进行多次编辑,最后将所有更改提交到单个"保存"中手术。

唯一正确的答案是由艾德斯曼(Aydsman)给出的。 LoadViewState是添加动态控件的唯一位置,在动态控件中,重新创建它们的视图状态值时,我们可以访问该视图状态以确定要添加的控件。

我在"动态控件创建"部分的" C2008中的Pro ASP.NET 3.5"一书中对此进行了介绍:

If you need to re-create a control multiple times, you should perform the control creation in the Page.Load event handler.  This has the additional benefit of allowing you to use view state with your dynamic control.  Even though view state is normally restored before the Page.Load event, if you create a control in the handler for the Page.Load event, ASP.NET will apply any view state information that it has after the Page.Load event handler ends.  This process is automatic.

我尚未对此进行测试,但是我们可能会对其进行研究。

在一段时间内解决了这个问题之后,我想出了这些似乎有效的基本规则,但是YMMV。

  • 尽可能使用声明性控件
  • 尽可能使用数据绑定
  • 了解ViewState的工作原理
  • 可见性属性可以走很长一段路
  • 如果必须在事件处理程序中使用添加控件,请使用Aydsman的技巧并在覆盖的LoadViewState中重新创建控件。

真正了解ViewState是必读的。

通过示例了解动态控件展示了一些有关如何使用数据绑定而不是动态控件的技术。

真正了解动态控件也阐明了可用于避免动态控件的技术。

希望这对其他有同样问题的人有所帮助。