Using ASP.NET to Create Multi-Page Custom Reports


Chandra Kamalakantha
EDS Corporation

Marius Rochon
Microsoft Corporation

September 2002

Applies to:
   Microsoft® ASP.NET
   Microsoft® Visual Studio® .NET

Summary: Describes a way to use the existing Microsoft ASP.NET infrastructure of designers, data binding and other runtime functionality to generate simple and complex multi-page HTML reports. (7 printed pages)

Download Reporttool.exe.


Our Approach
Creating Reports Using Data Binding
About the Authors


This article describes a way to use the existing ASP.NET infrastructure—designers, data binding and other runtime functionality—to generate simple and complex reports. The reports consist of HTML containing multiple pages of data. In general, Microsoft ASP.NET is used to handle single-page forms, with one set of UI controls. For reporting purposes, we needed to get ASP.NET to produce repetitive sequences of the same HTML output until all report data was exhausted. To accomplish that, we extended the functionality of the System.Web.UI.Page class. In particular, the Render method was overridden and additional design-time properties were added. Using the new class, the developer can use the standard Microsoft® Visual Studio® .NET Form Designer to lay out a report page, set its properties and the properties of the controls it contains, and execute the project to generate one HTTP Response stream with the same layout, bound to successive portions of incoming data.

An example of the output produced by this report is contained in the Report.htm file.

We expanded the functionality of the new class to allow for multiple streams of input data, use of several alternative page layouts in the same report (e.g., First Page, Report Totals page, Detail Page, etc.), and ability to browse the entire page-by-page (with no round trips to the server). All the relevant code is included in the ReportClass project. The MSDNArticleTest project contains sample report definitions using this framework.

Our Approach

Out of the box, ASP.NET supports page-at-a-time (HTML Form) rendering. One can therefore use it to render each page of a report in response to the user's request for the page. However, this requires user intervention to produce an entire report and it requires that cursor state be maintained between trips to the server, a potentially expensive need if the report needs to be produced in its entirety anyway.

However, ASP.NET also provides both a design and runtime extensibility framework, which allows us to change its default behavior. In this approach, we have used a new class (ReportPage) to override the default form handling of the System.Web.UI.Page class, from which ASP.NET forms are derived by default.

The basic operation of the Report class is embodied in its Render method, which overrides (and replaces) the Render method from System.Web.UI.Page:

First, generation of HTML related to the Form tag is skipped—it is not needed and in fact interferes with repetitive use of other control ID's. A dataset is loaded with sufficient data to populate a report page (using the DataGrid's page size). Next, DataBind is called to associate the data with all the controls on the page. Controls are rendered using their standard Render method; beginning with loading the dataset, the operation is repeated until there is no more data to be loaded. Finally, the Render methods of the closing tags are called.

Creating Reports Using Data Binding

The example report that we are going to create (and described below) will provide the following features for the report:

  • A multi-page report for viewing all the authors (Steps 1 through 13)
  • Page breaks when authors state changes (Step 14)
  • Track totals on zip code (Step 15)
  • List all the functions that can be overridden
  • Show how to add page navigation to the report (Step 17)

To create a report using data binding

  1. Create a new solution and name it "CustomReporting".
  2. Create a new C# project of type Class Library and name it "ReportClass".
  3. Delete the Class1.cs module.
  4. Download the attached ReportPage.cs module to your machine.
  5. Click the ReportClass project, click Add Existing Item, and add ReportPage.cs to the ReportClass project.
  6. Add references to System.Data and System.Web to the ReportClass project.
  7. Build the class library to set the stage to create reports using data binding.
  8. Add a new ASP.NET Web project to the CustomReporting solution and name it "ReportWriterTest".
  9. Add a reference to ReportClass under ReportWriterTest. (Click References, and on the Projects tab, select the ReportClass project.) Include the ReportClass.dll (shown in the Selected components section).
  10. Edit WebForm1.aspx by adding Reportclass.
  11. Change public class WebForm1 : System.Web.UI.Page to public class TestReport : ReportClass.ReportPage. This forces the Web page to inherit from ReportClass.ReportPage so that the test Web page can utilize the properties and methods exposed by the ReportPage class that is part of ReportClass class library.
  12. Build the ReportWriterTest project.
  13. Edit WebForm1.aspx in the designer and design the report layout. In this example, we will be reporting on authors table that is part of pubs database that resides on Microsoft® SQL Server™.

    To edit WebForm1.aspx and design the report layout

    1. Add an HTML table composed of two rows and four columns.
    2. Edit the HTML and delete the style property for the table.
    3. Set cellSpacing, cellPadding, and border to 0.
    4. On row one of the table, add two label controls. The first label will be for the Page tag, and the second one will display the page number; hence, it will be called "lblPage" and the text should be set to empty spaces.
    5. On row two of the table, add two label controls. The first label control will be utilized for label tag state. The second label control will be called "lblState" and the text will be set to empty spaces.
    6. On row three, delete three of the four columns so you will be left with just one column. Set the width of the column to be same size as the table.
    7. In the HTML table, drag a data-bound grid control onto row three.
    8. Edit the data-bound grip column collection and set up four columns; one each for the author's first name, last name, address, and city. Use Bound Column and perform page totals on zip codes. Please note: clear Create columns automatically at runtime.
    9. On row four, add two more labels. The first label's text will be set to total and the second label's text would be set to empty spaces and will be named "lblTotal".
    10. Embed the entire table inside a panel and give the panel an ID of Page1. Make sure the panel will be set to run on server.
    11. Drag an untyped dataset to the form.
    12. Navigate to the Properties tab and select the WebForm1.aspx page. Set the dataset property to the dataset on the page. Set the data-bound grid to the data-bound grid on the page.
    13. Populate the dataset on the Page_Load event of the Web page.
    14. Include code to populate the dataset:
      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();
         dbCmd = new OleDbCommand(sSQL, dbCon);
         dbAdapt.SelectCommand = dbCmd;
      catch (Exception e1)
         Response.Write("Database error   " + e1.Message);
  14. Set Page group/group break based on columns:
    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. Set and track totals:
  16. Other functions that one could override are ProcessPageBreak, PostProcessPageBreak, ProcessGroupBreak, PostProcessGroupBreak, ProcessReportEnd, and ProcessCurrentRow. Overriding ProcessCurrentRow is not recommended. One can override these functions to reset and print contents based on specific conditions.
  17. To add a navigation bar to the report, include Pagenav.js to the project and include it on every report page. Include ViewReport.aspx, Reportdetail.htm and Reportheader.htm to the project. ShowReport.aspx has an example of how Viewreport.aspx is called for page navigation. The only thing this navigation is doing is moving between the links, e.g., http://localhost/MSDNTest/WebForm1.aspx#Page[1-n].


What happens when you execute this report? The guts of the Report class are in its Render method, which overrides the same method in standard Page class.

The opening HTML tags are rendered by calling the Controls[0].render method. At some stage Control.Render will call Page.VerifyRenderingInServerForm. The default implementation of this function would raise an exception; therefore it is overridden in the Report class to do nothing.

ReportClass iterates thru the dataset tables[0] to create a multi-page report. The LoadPage method is called to load (first Clear) the DataSet with enough data to produce the next page of the report. The LoadPage method is overridable so you can provide your own logic here. Also, the LoadPage method calls a virtual function OnNextRow, which you can override to do some special processing, e.g. detect data break, calculate sub-totals (which you would store in a table in the dataset), etc. If you do override these methods and want to force a page break, set the Report.m_PageFull to True. To stop further output altogether, set MoreData to True. LoadPage calls the DataBind method.

The Page.Render method inserts Html to force a page break. The AllControls.RenderControls method is called to force rendering of controls on the page. The steps are repeated until the data rows are exhausted in specified dataset. The closing HTML tags are called (Controls[2].RenderControl), the database connection is closed and Render completes.


This sample shows how, with relatively few lines of code, one can change the basic paradigm of ASP.NET from rendering HTML Forms to producing multi-page reports. Most of the existing ASP.NET infrastructure remained intact—all the toolbox controls are available, their rendering properties can be set through the property window and their actual rendering is still executed by existing code. Yet, the resulting output serves a significantly different purpose.


We would like to thank the following people:

David Powell and Scott Beaudreau from Microsoft Consulting Services for helping us get started with .NET and guiding the team through initial stages of the project

Mark Wadsworth, Frank Degise, and Linda Sutton for their continued support and encouragement to move forward with the publication.

Tushar Patwardhan and Neeraj Pathania for helping to test and fix all the bugs, and Chandra's teammates from EDS Corporation.

About the Authors

Chandra Kamalakantha works for EDS Corporation, the leading global information technology (IT) services company. Send questions and comments to Chandra at

Marius Rochon is a Technical Evangelist at Microsoft Corporation.