利用 ASP.NET 创建多页自定义报表

利用 ASP.NET 创建多页自定义报表
发布日期 : 4/1/2004 | 更新日期 : 4/1/2004

Chandra Kamalakantha

EDS Corporation

Marius Rochon

Microsoft Corporation

2002 年 9 月

适用于:

Microsoft ASP.NET

Microsoft Visual Studio .NET

摘要:讲述一种利用现有的 ASP.NET 基础结构(设计器、数据绑定和其他运行时功能)来生成简单的和复杂的多页 HTML 报表的方法。 (7 页打印页)

下载 Reporttool.exe

本页内容

简介
我们的方法
利用数据绑定创建报表
运行时
小结
致谢
关于作者

简介

本文讲述一种利用现有的 ASP.NET 基础结构 — 设计器、数据绑定和其他运行时功能 — 来生成简单的和复杂的报表的方法。 报表由包含多页数据的 HTML 构成。 在通常情况下,Microsoft ASP.NET 用于处理单页的窗体,其中带有一组 UI 控件。 出于进行报告的目的,我们需要让 ASP.NET 产生相同 HTML 输出的重复序列,直到用尽所有报表数据为止。 为了达到这一目的,我们扩展了 System.Web.UI.Page类的功能。 具体来讲,我们重写了 Render 方法并增加了另外一些其他设计时属性。 利用这个新类,开发人员可以使用标准的 Microsoft Visual Studio .NET 窗体设计器来设定报表页面的布局,设置其属性以及所包含的各个控件的属性,并执行该项目以便生成绑定到传入数据的各个连续组成部分的、具有相同布局的一个 HTTP 响应流。

此报表所生成的一个输出示例包含于 Report.htm 文件中。

我们扩展了该新类的功能以便允许多个输入数据流,在同一报表中使用多个可选的页面布局(例如,第一页,报表总计页,明细页,等等),并能够逐页浏览整个报表(而不用反复访问服务器)。 所有相关的代码都包含在 ReportClass 项目中。 MSDNArticleTest 项目中包含了利用此框架的示例报表定义。

我们的方法

ASP.NET 以立即可用的方式支持一次一页 (HTML Form) 的呈现方式。 因此,可以使用它根据用户对报表的每个页面的请求而作出响应,从而呈现该页面。 然而,这需要用户干预才能生成整个报表,并且需要在对服务器的不同访问之间保持游标状态,如果要整体生成该报表,则这可能是一种比较昂贵的需要。

然而,ASP.NET 还提供了设计和运行时可扩展框架,这就使得我们可以更改其默认行为。 在此方法中,我们已经使用了一个新类 (ReportPage) 来重写 System.Web.UI.Page 类(默认情况下,ASP.NET 窗体是从此类派生出来的)的默认窗体处理方式。

Report 类的基本操作体现在其 Render 方法中,该方法重写(并替换)了 System.Web.UI.Page 中的 Render 方法:

首先,跳过与 Form 标记相关的 HTML 生成过程 — 这是不必要的,并且实际上会干扰其他控件 ID 的重复使用。加载一个以足够数据填充报表页的数据集(利用 DataGrid 的页面大小)。 接下来,调用 DataBind以便将数据与页面上的所有控件进行关联。 利用各个控件的标准 Render 方法对其进行呈现;首先加载数据集,重复此操作直到没有要加载的数据为止。 最后,调用结束标记的 Render 方法。

利用数据绑定创建报表

我们将要创建的示例报表(如下所述)将提供该报表的下列特性:

  • 一个多页报表,用以查看所有作者(步骤 1 到 13)

  • 当作者状态改变时,进行分页(步骤 14)

  • 根据邮政编码跟踪总数(步骤 15)

  • 列出所有可被重写的函数

  • 显示如何在报表中添加页面导航(步骤 17)

利用数据绑定创建报表

  • 1.创建一个新的解决方案,将其命名为 "CustomReporting"。

  • 2.创建一个类库类型的新 C# 项目,将其命名为 "ReportClass"。

  • 3.删除 Class1.cs 模块。

  • 4.将所附带的 ReportPage.cs 模块下载到计算机。

  • 5.单击该 ReportClass 项目,单击 Add Existing Item,然后将 ReportPage.cs 添加到该 ReportClass 项目。

  • 6.在 ReportClass 项目中添加对 System.Data 和 System.Web 的引用。

  • 7.生成类库以准备好利用数据绑定来创建报表。

  • 8.向 CustomReporting 解决方案中添加一个新的 ASP.NET Web 项目,并将其命名为 "ReportWriterTest"。

  • 9.在 ReportWriterTest 下添加一个到 ReportClass 的引用。 (单击 References,在 Projects 选项卡上,选择 ReportClass 项目。) 包含 ReportClass.dll(显示于 Selected components 部分)。

  • 10.通过添加 Reportclass,编辑 WebForm1.aspx。

  • 11.将

     public class WebForm1 : System.Web.UI.Page
    
        

    更改为

     public class TestReport : ReportClass.ReportPage
    
        

    这样就会强制 Web 页面从 ReportClass.ReportPage 进行继承,这样一来测试 Web 页面就能够利用 ReportPage类(ReportClass 类库的一部分)所公开的属性和方法了。

  • 12.生成 ReportWriterTest 项目。

  • 13.在设计器中编辑 WebForm1.aspx,并设计报表布局。 在本示例中,我们将根据 authors 表(Microsoft SQL Server? 中所驻留的 pubs 数据库的一部分)生成报表。

    编辑 WebForm1.aspx 并设计报表布局

    • a.添加一个包括两行四列的 HTML 表格。

    • b.编辑 HTML 并删除该表格的 style 属性。

    • c.将 cellSpacingcellPaddingborder 设置为 0。

    • 在该表格的第一行中,添加两个标签控件。 第一个标签用于 Page 标记,而第二个标签将显示页号;因此它将被命名为 "lblPage" 而文本应被设置为空格。

    • e.在该表格的第二行中,添加两个标签控件。 第一个标签控件将被用于标签标记状态。 第二个标签控件将被命名为 "lblState",其文本将被设置为 empty spaces

    • f.在第三行上,删除四列中的三列,仅剩余一列。 将该列的宽度设置为与表格宽度相同。

    • g.在 HTML 表格中,把一个数据绑定网格控件拖到第三行。

    • h.编辑该数据绑定网格列集合,并设置四列;分别用于作者的姓、名、地址和城市。 利用 Bound Column 并根据邮政编码统计页面总数。 请注意: 在运行时,请取消选中 Create columns automatically

    • i.在第四行上,添加另两个标签。 第一个标签的文本将被设置为 total,而第二个标签的文本被设置为 empty spaces且将被命名为 "lblTotal"。

    • j.将整个表格嵌入一个面板中,并给该面板指定 ID 为 Page1。 确保该面板被设置为在服务器上运行。

    • k.将一个非类型化数据集拖到窗体上。

    • l.导航至 Properties 选项卡并选择 WebForm1.aspx 页。 将 dataset属性设置为该页面上的数据集。 将 data-bound 网格设置为页面上的数据绑定网格。

    • m.在该 Web 页面的 Page_Load 事件中填充该数据集。

    • n.包含以下代码,以便填充数据集:

      private void Page_Load(object sender, System.EventArgs e)
      
              {
      
              if ( IsPostBack) return;
      
              string sConnectionString = "Provider=SQLOLEDB;data source=.;
      
              initial catalog=pubs; User ID=sa; Password=****";
      
              string sSQL = "select au_lname, au_fname, address, city, state,
      
              zip from authors order by state";
      
              OleDbConnection dbCon = new OleDbConnection(sConnectionString);
      
              OleDbCommand dbCmd;
      
              OleDbDataAdapter dbAdapt = new OleDbDataAdapter();
      
              try
      
              {
      
              dbCon.Open();
      
              dbCmd = new OleDbCommand(sSQL, dbCon);
      
              dbAdapt.SelectCommand = dbCmd;
      
              dbAdapt.Fill(dataSet1);
      
              dbAdapt.Dispose();
      
              dbCmd.Dispose();
      
              }
      
              catch (Exception e1)
      
              {
      
              Response.Write("Database error   " + e1.Message);
      
              }
      
              finally
      
              {
      
              dbCon.Close();
      
              dbCon.Dispose();
      
              }
      
              }
      
              
  • 14.根据列设置页面组/组分隔符:

    base.TrackColumnBreaks("one", "state");
    
        This will provide an override function and exposes the row that accounted for column/page break.
    
        protected override void ProcessPageBreak()
    
        {
    
        lblState.Text  = CurrentDataRow["state"].ToString();
    
        }
    
        
  • 15.设置并跟踪总数:

    base.TrackTotals("zip",ReportClass.TotalsScopeEnum.Page);
    
        base.TrackTotals("zip",ReportClass.TotalsScopeEnum.Report);
    
        
  • 16.其他可重写的函数包括:ProcessPageBreakPostProcessPageBreakProcessGroupBreakPostProcessGroupBreakProcessReportEndProcessCurrentRow。 建议不要重写 ProcessCurrentRow。 开发人员可根据具体情况来重写这些函数以重置或打印内容。

  • 17.要在报表中添加导航条,请将 Pagenav.js 包含到项目中,并将其包括到每个报表页上。 将 ViewReport.aspx、Reportdetail.htm 和 Reportheader.htm 包括到该项目中。 ShowReport.aspx 中包含一个示例,用以说明如何调用 Viewreport.aspx 进行页面导航。 这种导航功能所完成的唯一动作就是在各个链接(例如,http://localhost/MSDNTest/WebForm1.aspx#Page[1-n])之间移动。

运行时

当您执行此报表时,会发生什么情况? Report 类的实际操作都在其 Render 方法中,该方法重写了标准 Page 类中的同一方法。

开始 HTML 标记是通过调用 Controls[0].render 方法而呈现的。 在某个阶段,Control.Render将调用 Page.VerifyRenderingInServerForm。 此函数的默认实现方法将引发一个异常;因此,在 Report 类中重写此函数是为了不进行任何操作。

ReportClass 循环处理数据集 tables[0] 以便创建一个多页报表。 调用 LoadPage方法以便将足够数据加载(首先清除)到 DataSet 中,从而生成下一页报表。 LoadPage方法是可重写的,因此您可以在此处提供自己的逻辑。 此外,LoadPage 方法会调用虚拟函数 OnNextRow,您可以重写此虚拟函数以便进行一些特殊的处理,例如,检测数据分隔,计算小计(您可以将其保存在数据集的一个表中)等等。如果您确实要重写这些方法,并想要强制进行分页,则应将 Report.m_PageFull 设为 True。 要完全停止进一步的输出,请将 MoreData 设为 TrueLoadPage 会调用 DataBind 方法。

Page.Render 方法插入 Html 以便强制进行分页。 调用 AllControls.RenderControls方法以便强制呈现页面上的各个控件。 重复进行以上步骤,直到所指定的数据集中数据行耗尽为止。 结束 HTML 标记 (Controls[2].RenderControl) 被调用,数据库连接被关闭,Render 完成。

小结

本示例说明了如何能够利用相对很少的几行代码将 ASP.NET 的基本范例从呈现 HTML 窗体更改为生成多页报表。 现有 ASP.NET 基础结构的绝大部分都原封未动 — 所有的工具箱控件都是可用的,可通过属性窗口设置这些控件的呈现属性,且真正的呈现过程仍然由现有代码执行。 然而,所生成的输出却可用作一个明显不同的用途。

致谢

在此,我们想感谢以下人士:

Microsoft 咨询服务部门的 David Powell 和 Scott Beaudreau 帮助我们开始使用 .NET 并指导小组完成了本项目的初始阶段

Mark Wadsworth、Frank Degise 和 Linda Sutton 不断提供支持和鼓励,才使我们得以出版这篇文章。

Tushar Patwardhan 和 Neeraj Pathania 帮助测试和修复了所有问题,我们还要感谢 Chandra 在 EDS Corporation 的小组同事。

你可能感兴趣的:(asp.net)