当我们无法使用ViewState时该怎么办?
我有一个相当复杂的页面,可以在转发器内部动态构建用户控件。在初始化" ViewState"之前,必须在Init页面事件期间绑定此转发器,否则动态创建的用户控件将不会保留其状态。
这会创建一个有趣的Catch-22,因为我绑定转发器的对象需要在初始页面加载时创建,然后保留在内存中,直到用户选择离开或者保存为止。
因为我不能使用ViewState来存储该对象,但是在初始化期间可以使用它,所以不得不将其存储在Session中。
这也有问题,因为我必须在非回发期间显式地将会话值设置为空,以便模拟" ViewState"的工作方式。
在这种情况下,必须有一种更好的状态管理方法。有任何想法吗?
编辑:关于使用LoadViewState
的一些好的建议,但是当我这样做时,我仍然遇到状态无法恢复的问题。
这有点页面结构
页面-> UserControl->中继器-> N个动态创建的UserControl。
我将重写的LoadViewState放在父UserControl中,因为它被完全封装并且独立于其所在的页面。我想知道那是问题所在。
解决方案
回答
This also has issues, because I have to explicitly null the session value during non postbacks in order to emulate how ViewState works.
为什么我们必须显式取消该值(除了内存管理等)?它不是检查Page.IsPostback的选项,是否对Session变量进行某些操作?
回答
我一直在LoadViewState事件中重新创建动态控件。我们可以在视图状态中存储需要创建的控件数,然后使用LoadViewState事件内的LoadControl方法动态创建许多控件。在这种情况下,我们可以访问ViewState,但尚未将其还原到页面上的控件中。
回答
@DancesWithBamboo:
如果我在那里动态绑定控件,ASP.NET会自动处理它们的状态吗?在Page_Load中执行此操作的问题在于,viewstate已经加载,并且没有在转发器中添加动态控件。我想它会在LoadViewState中做同样的事情?
回答
1)可能有一种方法使其起作用...我们只需要确保在适当的时候将控件添加到树中即可。太早了,我们将无法获得ViewState。为时已晚,我们还没有得到ViewState。
2)如果无法弄清楚,也许可以关闭孔页面的viewstate,然后仅依靠querystring更改状态?以前是回发的任何链接都将是指向另一个URL的链接(或者回发重定向)。
这确实可以减轻页面的重量,并且可以更轻松地避免ViewState问题。
回答
@乔纳森:
是的,只要我们创建正确数量的控件,运行时就会填充控件的视图状态。在此事件之后,将填充控件的viewstate。
回答
Yes, the runtime will populate the viewstate of the controls as long as you create the right number of them. The controls' viewstate will populate after this event.
以什么顺序调用LoadViewState?我添加了一个覆盖的方法签名,它似乎并没有介入其中。
回答
protected override void LoadViewState(object savedState) { // Put your code here before base is called base.LoadViewState(savedState); }
这是你的意思吗?或者我们是说按什么顺序处理控件?我认为答案是准随机的。
另外,为什么不能在Page_Load之前加载绑定到的对象?在页面生命周期中的任何时候都可以调用业务层,除了预渲染和之后的任何事情。
回答
I have to explicitly null the session value during non postbacks in order to emulate how ViewState works.
我仍然不清楚为什么我们不能在会话中存储要绑定的任何对象。如果我们可以将该对象存储在会话中,则应该可以进行以下操作:
- 首次加载时,在OnPreInit期间将顶级用户控件绑定到该对象。将对象存储在会话中。 Viewstate将自动为这些控件存储。如果我们必须第一次在Page_Load上绑定控件是可以的,但是如果我们继续执行下一步,最终将遇到两个调用bind的事件。
- 在回发时,将OnPreInit方法中的顶级用户用户控件与我们存储在会话中的对象重新绑定。在加载视图状态之前,应重新创建所有控件。然后,当恢复viewstate时,这些值将设置为viewstate中的任何值。唯一需要注意的是,当我们在回发上再次绑定时,必须100%确保再次创建相同数量的控件。使用具有内部动态控件的Repeater,Gridviews等的关键在于,在加载viewstate之前,它们必须在每次回发时都重新绑定。通常,OnPreInit是执行此操作的最佳位置。框架中没有技术上的限制,它要求我们必须在首次加载时在Page_Load中完成所有工作。
这应该工作。但是,如果由于某种原因不能使用会话,则必须采取稍微不同的方法,例如在绑定控件后将要绑定的内容存储在数据库中,然后将其从数据库中拉出并重新绑定。每次回发时都会再次。
我是否缺少有关我们情况的一些明显细节?我知道在不发布代码的情况下解释情况的微妙之处可能非常棘手。
编辑:在此解决方案中,我将对OnInit的所有引用都更改为OnPreInit。我忘记了MS在ASP.NET 2.0中引入了这个新事件。根据他们的页面生命周期文档,应在OnPreInit中创建/重新创建动态控件。
回答
创建动态控件时...我仅在初始加载时填充它们。后记我在页面加载事件中在回发时重新创建控件,并且viewstate似乎可以毫无问题地处理值的重新填充。
回答
页面上的LoadViewState方法绝对是答案。这是一般的想法:
protected override void LoadViewState( object savedState ) { var savedStateArray = (object[])savedState; // Get repeaterData from view state before the normal view state restoration occurs. repeaterData = savedStateArray[ 0 ]; // Bind your repeater control to repeaterData here. // Instruct ASP.NET to perform the normal restoration of view state. // This will restore state to your dynamically created controls. base.LoadViewState( savedStateArray[ 1 ] ); }
SaveViewState需要创建我们上面使用的saveedState数组:
protected override object SaveViewState() { var stateToSave = new List<object> { repeaterData, base.SaveViewState() }; return stateToSave.ToArray(); }
不要忘记使用以下代码在Init或者Load中绑定转发器:
if( !IsPostBack ) { // Bind your repeater here. }