动态加载的用户控件中丢失了Viewstate变量

7
我有一个ViewState的问题。我有一个aspx页面,在左侧有一个treeview,右侧有一个带有ASP.NET Panel的UpdatePanel。在内部Panel中,我加载和卸载动态用户控件。我使用该更新面板来动态加载控件。
我还为我的用户控件制作了一个自定义控件,因为我需要从页面传递一些值。在构造函数中,我使用ViewState来存储这些值。
第一次加载用户控件时,我使用参数调用其构造函数。每次回发时重新加载该用户控件时,我使用它的普通构造函数。
我的问题是,我在连续的回发中存储在ViewState中的值已经变成了null。
更新:
这是我的一个用户控件的一部分:
public class MyUserControl : System.Web.UI.UserControl
{
private int PKId
{
    get { return ViewState["pkId"] as int; }
    set { ViewState["pkId"] = value; }
}

public MyUserControl(int pkId)
{
    this.PKId = pkId;
}

...
}

我正在按照这篇文章动态加载控件:http://msdn.microsoft.com/en-us/magazine/cc748662.aspx#id0070065

第二次更新:
当我首次加载用户控件并在每次重新加载时,我也设置了相同的控件ID。

也许我可以使用另一种方法来存储这些值,例如输入隐藏字段或高速缓存。我选择使用ViewState是因为我不想为每个用户使用Session值过度负载服务器。

第三次更新:
I load the controls with this code:

System.Web.UI.UserControl baseControl = LoadControl(ucUrl) as System.Web.UI.UserControl;
if (baseControl != null)
{
    baseControl.ID = "DestinationUserControl";
    PanelDestination.Controls.Add(baseControl);
}

使用以下代码重新加载:

DynamicControls.CreateDestination ud = this.LoadControl(TrackedUserControl) as DynamicControls.CreateDestination;
if (ud != null)
{
    ud.ID = "DestinationUserControl";
    PanelDestination.Controls.Add(ud);
}

发生了什么?

请提供一些相关代码,以便我们更好地了解正在发生的情况。 - Ricardo Sanchez
为什么在初始加载时您将其键入为UserControl,而在重新加载时将其键入为CreateDestination? - G-Wiz
另外一种方法是手动将控件的状态保存到ViewState中(可以通过覆盖SaveViewState来实现,或者在PreRender中将重要状态复制到ViewState中)。在回传时,您可以覆盖LoadViewState并将手动保存的状态放入某些类实例字段中(或在Load开始时将状态从ViewState转移到动态创建的控件)。 - G-Wiz
我将使用 Session 而不是 ViewState。与动态加载的用户控件相关的某些内容会导致丢失 ViewState 变量。 - VansFannel
4个回答

3

你什么时候加载用户控件?如果你想要保存/恢复ViewState,这必须在Init事件中发生。


用户控件有一些文本框。如果它们有一些文本,那么在 postbacks 上正确地恢复。我在 Page_Load 事件上重新加载用户控件。我正在遵循这篇文章动态加载控件:http://msdn.microsoft.com/en-us/magazine/cc748662.aspx#id0070065 - VansFannel
2
如果那篇文章说要在Page_Load中加载动态控件,那就是完全错误的。这种方法在某些情况下可能有效,但肯定不是全部情况,正如你所发现的那样。ViewState恢复发生在Page_Load之前,这就是为什么你的变量为空的原因。没有更简单的解释方式了。 - Bryan

3

也许我没有理解你的回答,但我正在按照第二条规则进行。我已经更新了我的问题并提供了更多细节。 - VansFannel

0

在UpdatePanel内动态添加控件是一个非常糟糕的想法,会引发很多问题。

如果可能的话,将动态控件的创建移出UpdatePanel,我相信你的问题就会得到解决。


非常抱歉,我犯了一个错误。这是一个带有 ASP.NET 面板的 UpdatePanel。在该内部面板中,我动态加载和卸载用户控件。 - VansFannel

0

正如Bryan所提到的那样,你应该在 Page_Init 中加载动态控件而不是 Page_Load。

正如页面生命周期的这段描述所解释的那样,在Page_Load事件发生时,来自postback的视图状态已经被处理(在PreLoad中)。如果控件尚未重新加载,则视图状态无处可去。

PreLoad:

如果您需要在Load事件之前对页面或控件执行处理,请使用此事件。

在页面实例引发此事件之前,它会为自身和所有控件加载视图状态,然后处理包含在请求实例中的任何postback数据。


也许我可以使用其他方法来存储这些值,比如输入隐藏字段或缓存。我选择了ViewState,因为我不想过载服务器。 - VansFannel
在Load事件中可以动态添加控件,此时它们的生命周期将会“追赶”到当前事件。请参阅此处的“已添加控件的追赶事件”http://msdn.microsoft.com/en-us/library/ms178472.aspx,以及此处的Walkthrough#6 http://www.codeproject.com/KB/aspnet/aspnetviewstatepagecycle.aspx#Walkthroughs(这个演示了在控件添加到层次结构之前和之后行为差异的绝佳例子)。 - G-Wiz
@gWiz,虽然第6个演练很有趣,但依赖“catch-up”事件最多是有问题的。即使演练本身也说:“建议您在“PreInit”或“Init”事件期间添加动态控件”。这就是我向@VansFannel建议的内容。 - Eric King
不要使用PreInit。我曾经有过一次很糟糕的经历...就像使用Page_Load一样。而且我不确定为什么会这样。Init事件绝对是正确的选择。 - Bryan

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接