在页面生命周期中的 .net ViewState

4
我有一个包含名为PhoneInfo.ascx的控件的页面。通过使用LoadControl()动态创建PhoneInfo,然后调用initControl()函数并传入初始化对象来设置PhoneInfo中一些文本框的初始值。
然后用户更改这些值并单击页面上连接到“submit_click”事件的提交按钮。此事件在PhoneInfo内部调用GetPhone()函数。返回值具有所有新的用户输入值,但是phoneId值(存储在ViewState中且未由用户编辑)始终返回为空。
我相信viewstate负责跨postback跟踪用户输入的数据,因此我无法理解为什么用户值会返回,而明确设置的ViewState["PhoneId"]值却不会返回!如果我在PhoneInfo的page_load事件中设置ViewState ["PhoneId"]值,则在postback后正确检索它,但这不是一个选项,因为只有在页面准备好提供它时才能初始化该值。
我确定我只是在某种程度上搞乱了页面生命周期,任何建议或问题都将非常有帮助!我在下面包含了实际代码的大大简化版本。
包含页面的代码后台
protected void Page_Load(object sender, EventArgs e)
{
    Phone phone = controlToBind as Phone;
    PhoneInfo phoneInfo = (PhoneInfo)LoadControl("phoneInfo.ascx"); //Create phoneInfo control
    phoneInfo.InitControl(phone); //use controlToBind to initialize the new control
    Controls.Add(phoneInfo);
}
protected void submit_click(object sender, EventArgs e)
{
    Phone phone = phoneInfo.GetPhone();
}

PhoneInfo.ascx的代码后台

protected void Page_Load(object sender, EventArgs e)
{
}

public void InitControl(Phone phone)
{
    if (phone != null)
    {
        ViewState["PhoneId"] = phone.Id;
        txt_areaCode.Text = SafeConvert.ToString(phone.AreaCode);
        txt_number.Text =  SafeConvert.ToString(phone.Number);
        ddl_type.SelectedValue = SafeConvert.ToString((int)phone.Type);
    }
}

public Phone GetPhone()
{
    Phone phone = new Phone();
    if ((int)ViewState["PhoneId"] >= 0)
        phone.Id = (int)ViewState["PhoneId"];
        phone.AreaCode = SafeConvert.ToInt(txt_areaCode.Text);
        phone.Number = SafeConvert.ToInt(txt_number.Text);
        phone.Type = (PhoneType)Enum.ToObject(typeof(PhoneType),       SafeConvert.ToInt(ddl_type.SelectedValue));
        return phone;
    }
}
4个回答

1
我曾经像回答者一样认为,这个问题是因为我在SaveViewState期间设置ViewState变量太晚了而无法保存。然而,我添加了一个SaveViewState事件的重写,确实在ViewState被保存之前很久就设置了我的ViewState变量。结果证明,时间太长了。
显然,在初始化后,.net运行TrackViewState(),它实际上开始监听您添加的任何viewState变量。由于我在TrackViewState()运行之前设置了这个viewState值,所以一切看起来都很好,但实际上在SaveViewState期间并没有添加值。我在设置变量之前明确调用了TrackViewState(),一切都按预期工作了。
我的最终解决方案是通过初始化分配一个私有属性来保存ID值,然后从属性值在SaveViewState期间设置viewState变量。这样,我可以确保.net已经运行了TrackViewState,而不必显式调用它并搞乱事情的流程。我在page_load中重新加载viewState值,并使用它来设置属性的值,然后在GetPhone函数中使用它。
这篇文章讨论了enableViewState。
private int _id = -1;

protected void Page_Load(object sender, EventArgs e)
{
    if (ViewState["PhoneId"] != null)
    _id = SafeConvert.ToInt(ViewState["PhoneId"]);
}

protected override object SaveViewState()
{
    ViewState["PhoneId"] = _id;
    return base.SaveViewState();
}

public Phone GetPhone()
{
    Phone phone = new Phone();
    phone.Id = _id;
    phone.AreaCode = SafeConvert.ToInt(txt_areaCode.Text);
    phone.Number = SafeConvert.ToInt(txt_number.Text);
    return phone;
}

1

ViewState 在 Init 和 Load 事件之间进行管理。当您关心维护 postbacks 的值时,应该在 Page_Init 处理程序中使用代码或从中调用的函数,而不是在 Page_Load 中。

请参阅此链接以获取有关 ASP.NET 页面生命周期的更多信息。

http://msdn.microsoft.com/en-us/library/ms178472.aspx


我理解生命周期的这一部分,并同意将其放在page_init上是最好的选择。但是,如果用户输入的值是通过viewstate保存的postback,为什么它们会传递到我的GetPhone函数呢?难道那些不应该也返回为空吗? - caltrop

0

要做你正在做的事情,你必须在页面生命周期的Init部分加载控件。

然而,为了更方便地实现它,为什么不将其添加到一个<asp:ContentPlaceHolder>中呢?我认为这会使它更容易。


谢谢您提供的占位符建议,但由于我留下的代码复杂性,占位符在这里并不理想。=)我了解生命周期足够了解将其放入Init中将导致ViewState正确保存和加载...我猜想我困惑的是为什么用户输入的文本可以正常工作,考虑到它也通过ViewState。 - caltrop

0

根据评论回复,我在这里添加另一个答案,解释页面生命周期的一部分以及它对发布和视图状态数据的意义。以下是页面生命周期开始部分的更多英文(针对开发人员)和更少关于事件的版本:

  1. 用户请求页面(请求1)
  2. ASP.NET使用字段中的默认值构建页面(请求1)
  3. 在Page_Load中,您添加了带有默认值的特殊控件(请求1)
  4. 您将此页面发送给用户(请求1)
  5. 用户向页面发布数据(请求2)
  6. ASP.NET从您的ASPX文件构建页面(请求2)
  7. ASP.NET将ViewState数据添加到页面中(此时没有)(请求2)
  8. ASP.NET将Post数据添加到页面中(请求2)
  9. ASP.NET运行到页面生命周期的Load步骤,并添加具有默认值的特殊控件(请求2)
  10. 您现在处于您所看到的问题状态。
所以问题在于,当页面添加POST数据时,您动态加载的控件不存在。 当您添加它时,将默认值放入其中。 ASP.NET不再有机会将其填充为正确的数据。

说句实话,在这个概述中,“Init”事件位于步骤6和7之间。更具体地说,步骤6是“PreInit”事件。 - Jaxidian
这很有道理。我希望我的实际代码能让我简单地将创建代码移动到Page_init,但事实并非如此。我必须想办法调整一下。感谢您的帮助! - caltrop

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