7

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

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

因为我不能ViewState用来存储这个对象,但在初始化期间它仍然可用,我不得不将它存储在 Session 中。

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

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

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

这里有点如果页面结构

页面 --> 用户控件 --> 中继器 --> 动态创建的 N 个用户控件。

我将覆盖的内容LoadViewState放在 parentUserControl中,因为它被设计为完全封装并且独立于它所在的页面。我想知道这是否是问题所在。

4

7 回答 7

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.
}
于 2008-11-09T20:26:24.223 回答
1

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

为什么你必须显式地清空值(除了内存管理等)?它不是检查 Page.IsPostback 的选项,或者不使用 Session 变量吗?

于 2008-09-05T03:22:46.167 回答
1

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

于 2008-09-05T07:11:55.523 回答
0
protected override void LoadViewState(object savedState)
{
   // Put your code here before base is called
   base.LoadViewState(savedState);
}

Is that what you meant? Or did you mean in what order are the controls processed? I think the answer to that is it quasi-random.

Also, why can't you load the objects you bind to before Page_Load? It's ok to call your business layer at any time during the page lifecycle if you have to, with the exception of pre-render and anything after.

于 2008-09-05T16:59:07.947 回答
0

创建动态控件时...我只在初始加载时填充它们。后记我在页面加载事件中重新创建回发控件,并且视图状态似乎可以毫无问题地处理值的重新填充。

于 2008-09-07T01:05:34.463 回答
0

1)可能有一种方法可以让它工作......你只需要确保在正确的时刻将你的控件添加到树中。太快了,你不会得到 ViewState。太晚了,你没有得到 ViewState。

2)如果你想不通,也许你可以关闭洞页的视图状态,然后只依靠查询字符串来改变状态?以前是回发的任何链接都将是指向另一个 URL 的链接(或回发重定向)。

这可以真正减轻页面的重量,并更容易避免 ViewState 的问题。

于 2008-09-05T14:32:59.750 回答
0

为了模拟 ViewState 的工作方式,我必须在非回发期间显式地清空会话值。

我仍然对为什么你不能存储你在会话中绑定的任何对象感到困惑。如果您可以将该对象存储在会话中,则以下内容应该可以工作:

  1. 在第一次加载时,在 OnPreInit 期间将您的顶级用户控件绑定到对象。将对象存储在会话中。Viewstate 将自动为这些控件存储。如果您必须第一次在 Page_Load 上绑定控件,那没关系,但是如果您按照下一步操作,您最终会得到两个调用 bind 的事件。
  2. 在回发时,将 OnPreInit 方法中的顶级用户用户控件与存储在会话中的对象重新绑定。您的所有控件都应在视图状态加载之前重新创建。然后,当视图状态恢复时,值将设置为视图状态中的任何值。这里唯一需要注意的是,当您在回发中再次绑定时,您必须 100% 确保再次创建相同数量的控件。使用Repeaters、Gridviews等的关键是它们内部有动态控件,它们必须在加载视图状态之前在每次回发时重新反弹。OnPreInit 通常是执行此操作的最佳位置。框架中没有技术限制规定您必须在第一次加载时在 Page_Load 中完成所有工作。

这应该有效。但是,如果由于某种原因您不能使用会话,那么您将不得不采取稍微不同的方法,例如在绑定控件后将绑定的任何内容存储在数据库中,然后将其从数据库中拉出并重新绑定在每次回发时再次。

我是否遗漏了有关您的情况的一些明显细节?我知道在不发布代码的情况下解释情况的微妙之处可能非常棘手。

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

于 2008-09-06T21:50:53.387 回答