当你不能使用ViewState时你会怎么做?

时间:2008-09-05 03:16:34

标签: asp.net webforms viewstate page-lifecycle

我有一个相当复杂的页面,可以在转发器内动态构建用户控件。在初始化ViewState之前,必须在初始化页面事件期间绑定此转发器,否则动态创建的用户控件将不会保留其状态。

这会创建一个有趣的Catch-22,因为我需要在初始页面加载时创建绑定转发器的对象,然后将其保留在内存中,直到用户选择离开或保存为止。

因为我不能使用ViewState来存储这个对象,但是在Init期间可以使用它,我被迫将它存储在Session中。

这也有问题,因为我必须在非回发期间显式地使会话值为空,以便模拟ViewState的工作方式。

在这种情况下,必须有一种更好的状态管理方法。有什么想法吗?

编辑:关于使用LoadViewState的一些好建议,但是当我这样做时,我仍然遇到状态未恢复的问题。

如果页面结构

,则有所不同

页面 - > UserControl - >中继器 - > N量的UserControls动态创建。

我将重写的LoadViewState放在父UserControl中,因为它被设计为完全封装并独立于它所在的页面。我想知道问题出在哪里。

10 个答案:

答案 0 :(得分:4)

页面上的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需要创建我们上面使用的savedState数组:

protected override object SaveViewState() {
  var stateToSave = new List<object> { repeaterData, base.SaveViewState() };
  return stateToSave.ToArray();
}

不要忘记使用以下代码绑定Init或Load中的转发器:

if( !IsPostBack ) {
  // Bind your repeater here.
}

答案 1 :(得分:1)

  

这也有问题,因为我必须在非回发期间显式地使会话值为空,以便模拟ViewState的工作方式。

为什么 明确地将值清空(除了内存管理等)?它不是检查Page.IsPostback的选项,是否可以使用Session变量执行某些操作?

答案 2 :(得分:1)

我总是在LoadViewState事件中重新创建动态控件。您可以在viewstate中存储需要创建的控件数,然后使用LoadViewState事件中的LoadControl方法动态创建其中的许多控件。在这种情况下,您可以访问ViewState,但尚未恢复到页面上的控件。

答案 3 :(得分:0)

@DancesWithBamboo:

如果我动态绑定那里的控件,ASP.NET会自动处理它们的状态吗?在Page_Load中执行此操作的问题是viewstate已经加载,并且它不会在转发器中添加动态控件。我认为它会在LoadViewState中做同样的事情吗?

答案 4 :(得分:0)

1)可能有办法让它工作......你只需要确保在适当的时候将控件添加到树中。太快了,你没有得到ViewState。太晚了,你没有得到ViewState。

2)如果你无法弄清楚,也许你可以关闭井页的viewstate,然后只依赖查询字符串进行状态更改?之前回发的任何链接都是指向另一个URL(或回发重定向)的链接。

这确实可以减轻页面的重量,并且更容易避免ViewState出现问题。

答案 5 :(得分:0)

@Jonathan:

是的,只要您创建正确数量的控件,运行时就会填充控件的视图状态。控件的viewstate将在此事件之后填充。

答案 6 :(得分:0)

  

是的,只要您创建正确数量的控件,运行时就会填充控件的视图状态。控件的viewstate将在此事件之后填充。

LoadViewState调用的顺序是什么?我添加了一个重写的方法签名,它似乎没有踩到它。

答案 7 :(得分:0)

protected override void LoadViewState(object savedState)
{
   // Put your code here before base is called
   base.LoadViewState(savedState);
}
这是你的意思吗?或者您的意思是控件处理的顺序是什么?我认为答案就是准随机。

另外,为什么不能在Page_Load之前加载绑定的对象?如果必须,可以在页面生命周期的任何时间调用业务层,除了预渲染和之后的任何内容。

答案 8 :(得分:0)

  

我必须显式地使会话无效   非回发期间的价值   模拟ViewState的工作方式。

为什么你不能存储你在会话中绑定的任何对象,我仍然模糊不清。如果您可以将该对象存储在会话中,则以下内容应该有效:

  1. 首次加载时,在OnPreInit期间将顶级用户控件绑定到对象。将对象存储在会话中。将自动为这些控件存储Viewstate。如果你必须第一次在Page_Load上绑定控件就可以,但是如果你按照下一步操作,你最终会有两个调用bind的事件。
  2. 在回发时,针对您在会话中存储的对象,在OnPreInit方法中重新绑定您的顶级用户用户控件。应在viewstate加载之前重新创建所有控件。然后,当恢复viewstate时,值将设置为viewstate中的任何值。这里唯一需要注意的是,当你再次绑定回发时,你必须100%确保再次创建相同数量的控件。使用Repeater,Gridviews等的关键......在它们内部使用动态控件是在加载视图状态之前, 在每次回发时都会反弹。 OnPreInit通常是最好的地方。框架中没有技术限制要求您必须在第一次加载时在Page_Load中完成所有工作。
  3. 这应该有效。但是,如果由于某种原因无法使用会话,则必须采取稍微不同的方法,例如在绑定控件后将数据库中存储的任何内容存储在数据库中,然后将其从数据库中拉出并重新绑定每次回发都会再次出现。

    我错过了一些关于你情况的明显细节吗?我知道在不发布代码的情况下解释情况的微妙之处可能非常棘手。

    编辑:我在此解决方案中将对OnInit的所有引用更改为OnPreInit。我忘了MS在ASP.NET 2.0中引入了这个新事件。根据他们的page lifecycle documentation,OnPreInit是应该创建/重新创建动态控件的地方。

答案 9 :(得分:0)

创建动态控件时...我只在初始加载时填充它们。后来我在页面加载事件中重新创建了回发控件,并且视图状态似乎处理了值的重新填充而没有任何问题。