在代码中动态创建控件无法保存状态的问题
在网上找了点资料,算是明白了,这是我觉得有帮助的文章:
原文:http://blog.csdn.net/keleloveni/archive/2007/03/15/1530300.aspx
引文:
今天偶然看到有篇文章谈到了相关的问题,终于又解决了一个自己不是很明白的东西。
页面生命周期中的第一个阶段是实例化,在这个阶段中,自动生成的类会根据页面的 HTML 部分中定义的静态控件构建控件层次结构。构造控件层次结构时,声明性语法中指定的值会赋给添加的每个控件的属性。实例化之后是初始化阶段,在这个阶段,静态控件层次结构已经构造,但还没重新加载视图状态(假定页面请求是回发)。如果页面请求是回发,则在初始化之后是加载视图状态阶段。在这个阶段中,页面会过滤出在隐藏的 VIEWSTATE 窗体字段中发现的视图状态数据,如果需要,控件层次结构中的每个控件会更新自己的状态。
如果页面请求是回发,则在加载视图状态阶段之后是加载回发数据阶段。这个阶段会检查发送的窗体字段值,并据此更新相应控件的属性。例如,通过 POST 机制(发出信号表示 TextBox 控件的名称和用户输入的值),来回送用户在 TextBox Web 控件中输入的文本。页面获得这些值,在控件层次结构中定位恰当的 TextBox,并将接收的值赋给它的 Text 属性。
下一个阶段是加载阶段,发生在 Page_Load 事件处理程序激发时。加载阶段之后还有更多阶段,如引发回发事件、保存视图状态和呈现 Web 页。
动态控件需要在加载视图状态和重新加载回发数据之前添加,因为我们想要正确添加特定于动态控件的任何视图状态或回发值。考虑到这些限制,添加动态控件的正常时间是在初始化阶段,因为它发生在加载视图状态阶段和加载回发数据阶段之前。
然而,在初始化阶段,视图状态和回发数据都还没还原,因此不建议访问或设置可能存储在视图状态或被回发值修改的控件属性(不管是动态还是静态控件),因为这些值将被生命周期后续阶段的视图状态和回发值所覆盖。当处理动态控件时我使用了以下模式:
• |
在初始化阶段,我向控件层次结构添加动态控件并设置 ID 属性 |
• |
在加载阶段,我在 If Not Page.IsPostback 条件语句中为动态控件赋予任何需要的初始值。 原文:http://blog.csdn.net/high_mount/archive/2007/03/18/1532481.aspx 引文: 介绍 背景 基础:新的编译模型和部分类(Partial Classes) 在asp.net 2.0中,我们不需要再定义控件变量,也不需要再在代码文件中写一些事件委托,这一切都要归功于部分类。在asp.net 1.x中,这些代码都会自动的在InitializeComponent()里生成。但是到了2.0版本,runtime将会创建一个部分类,这个类会包含aspx页中的所有信息。这将使得代码文件非常清晰并且易于管理。 这将消除VS2003中的代码文件和aspx页面之间的名字相互联系的改变(如果我们要改变任意控件的ID,都不得不改变aspx页和代码文件)。在VS2005中所有控件的事件都定义在aspx页里。所以代码文件中的事件委托和控件变量将被清除,这是比先前的VS2003方便的地方。
注意:asp.net 2.0中的视图状态由两部分组成,控件状态和视图状态。详细了解请参考这篇文章 下面我们将按照web程序的代码文件中的各个事件的触发顺序来详细的介绍它们 重点提示:除了Init()和Unload()之外的所有事件都是从最外面到最里面被激发的。例如,一个用户控件的init事件在它的父页类的Page_Init()事件之前被激发(译者注:这是从里到外)。 1. PreInit() 这个事件仅仅发生在页级别的类中,用户控件和母版页没有这个事件 下面的代码示例了如何重写这个方法以增加你的自定义代码 使用母版页时的特例 所以如果一个页有其相关联的母版页的话,那么在PreInit()事件里页中的所有控件都不会被初始化。而只有在Init()事件开始之后,你才能直接访问这些控件。为什么? 这个原因就是内容页中的所有控件都包含在“ContentPlaceholder”里,而“ContentPlaceholder”其实就是母版页的一个子控件。现在母版页被处理的过程就相当于内容页中的一个控件,我们早先提到过,除了Init()和Unload()之外的所有事件都是从最外面到最里面被激发的。虽然页的PreInit()是第一个被触发的事件,但是用户控件和母版页是没有这个事件的,所以在页的Page_PreInit()方法中,母版页和用户控件都不会被初始化,而是在Init()事件之后 接下来让我们来看一下Page_Init()事件之后控件的层次结构
4. LoadPostBackData 另一个重要的知识点是如果我们有一个DropDownList控件并动态的给它增加一些选择项,那么runtime将不能得到这些值除非启用了view state(即使控件继承自IPostBackDataHandler接口)。这个原因就是在HTTP的POST数据中的每一个控件只能有一个值,并且POST数据中的所有值都不会被保存,除了使用view state。 5. Page_Load 注意:如果页里有任何用户控件的话,那么用户控件的Load方法将在页类的Load方法之后被触发。这个原因早先已经解释过了,除了Init()和Unload()之外的所有事件都是从最外面到最里面被激发的。所以页的Page_Load()之后,页内的其它控件的Load方法才被触发。 6. Control Event Handlers 7. PreRender 8. SaveViewState 9. Render 10. Unload
开发人员虽然能动态的生成用户控件,但是却不能保存用户控件的状态。当我看了代码后,他们把生成控件的代码写到了Button的Click事件里。根据我们上面所讨论的,Button_Click()在LoadViewState()和LoadPostData()之后触发,而控件的值是要在view state或POST数据中取得的。 所以除非在Page_Init()或Pre_Init()方法里重新创建控件(它们发生在LoadViewState和LoadPostData之前),这样就可以在下一个事件里获得控件的值 现在,如果把代码写到Page_Init()事件里的话,将不能得到用户在TextBox(它是一个静态控件)里输入的值。原因就在于这是Page_Init()事件,控件的值被初始化为它们设计时的默认值,而不会得到用户输入的值 所以如果要在这里访问到用户输入的值话只有一个办法,就是从POST数据中取值。代码如下 // 动态生成控件的代码 原因就是控件一旦被添加到页的控件树里,TrackViewState()方法就负责跟踪其状态。只要控件被添加到控件树里,这个方法就会被自动的触发。因为这个原因,对控件的任何修改(如添加item之类的)都应该在动态控件被添加到页的控件树之后来做,否则其状态将丢失。请看如下代码 if (!IsPostBack) 下面的代码则不会保持动态控件的状态 总结 记住页的整个生存周期的各个事件的顺序是非常重要的,这样我们就可以根据不同的需求在合适的位置写出相应的代码
|