在 Page_Load 事件处理程序中绑定报表时的持久性限制

在 ASP.NET Web 应用程序中,通常的做法是将页面的所有启动代码放置在 Page_Load 事件处理程序中,而该事件处理程序将被 Page.Load 事件调用。

具体来说,控件数据绑定代码通常存储在 Page_Load 事件处理程序中。但是,将绑定代码放置在这个事件处理程序中会给 ViewState 造成问题。问题和典型的解决方案如下所述:

  • ViewState 用于在重新加载页面时保持两项内容:绑定到控件的数据和在控件上执行的鼠标单击事件。
  • ViewState 是一个字符串。所以,数据和单击事件都必须序列化。
  • 在重新加载页面时,数据和单击事件都从 ViewState 恢复。
  • Page.Load 事件发生在 ViewState 恢复之后。如果 Page_Load 事件处理程序包含控件绑定代码,则在重新加载页面时,此绑定代码将覆盖 ViewState,使得原始的数据和单击事件丢失。
  • 当重新加载页面时,如果控件忘记了鼠标单击操作(例如,DropDownList 选择),则通常会遇到此问题。
  • 若要防止数据和鼠标单击事件被覆盖,请将 Page_Load 事件处理程序中的任何绑定代码放置在 Not IsPostBack 条件块中,这可以防止在回发期间调用绑定代码。

此解决方案进行了重要假设:数据和鼠标单击事件都可以序列化到 ViewState 中。但是,CrystalReportViewer 控件可能会绑定到不能序列化的对象(明确地讲,包括 ReportDocument 类、ReportClientDocument 类或 InfoObject 类)。

Note注意

有一个例外:当 CrystalReportViewer 控件通过其文件目录路径绑定到某个报表时,该路径字符串可以保持到 ViewState中。只有在此方案中,才可以将 CrystalReportViewer 控件放置在 Not IsPostBack 条件块中。然而,与上面列举的绑定到报表类相比,此报表绑定方案功能较弱,也不很常用。

由于只有 CrystalReportViewer 控件的鼠标单击事件才可以序列化到 ViewState 中,所以,绑定到不能序列化的报表类会在重新加载页面时产生无法解决的问题:

  • 如果将报表绑定代码放置在 Not IsPostBack 条件块中,则 ViewState 的鼠标单击事件将被保留。但不会进行报表绑定。因此将引发异常。
  • 如果将报表绑定代码放置在条件块之外,则会正确绑定报表。但在此进程中 ViewState 会被覆盖。因此,鼠标单击事件会丢失。
    Note注意

    当在 CrystalReportViewer 控件中的多页面报表中单击时,通常会出现此问题。报表会不断返回到第一页。

推荐的解决方案:将 CrystalReportViewer 控件的绑定代码移动到 Init 事件中

对 CrystalReportViewer 控件的解决方案是将报表绑定代码移动到在恢复 ViewState 之前发生的 Init 事件中。

此解决方案会造成一种复杂情况。与 Load 事件相比,由于不常对 Init 事件进行编码,所以它更难以访问。在 Visual Studio .NET 2002 或 2003 的 Web 或 Windows 项目中,Init 事件处理代码位于 Web 窗体设计器生成的代码区域中,该区域通常是为生成的代码保留的隐藏区域。

若要解决此问题,建议使用以下方法:

  • 将所有 CrystalReportViewer 绑定和配置代码提取到名为 ConfigureCrystalReports() 的私有帮助器方法中。
  • 在 Web 窗体设计器生成的代码区域内,在 Page_Init() 事件处理程序或 OnInit() 事件引发方法中放置单行代码:调用 ConfigureCrystalReports() 帮助器方法。

有关创建和填充 ConfigureCrystalReports() 帮助器方法的说明,请参见“项目设置”

教程

若想全面了解如何保持报表的持久性,您可以学习以下教程。

使用 ReportDocument 对象模型进行编码时,可使用以下教程: