How To…Create a Spread Sheet Style Report

Question

“My report columns will not all fit on one page.  How do I extend them to the next page similar to a spread sheet?”

Solution

This can be done using multiple subreports connected to a single dataset.  Each subreport will contain different columns of data, the second and so on containing the overflow columns.  They can then be generated to memory and rearranged to give a spread sheet style look.

Download:  SpreadSheetStyle.zip

Delphi Code Description and Examples:

The first step is to print the report to cache (memory) using a generic TppDevice object.  This will allow us to access each page and rearrange them in the correct order.  Below is an example of a routine that will print a report to cache.

procedure TForm1.PrintToCache;
var
  lDevice: TppDevice;
begin

  lDevice := TppDevice.Create(Self);
  lDevice.PageSetting := psLastPage;
  lDevice.Publisher := ppReport1.Publisher;

  ppReport1.CachePages := True;
  ppReport1.PrintToDevices;

  lDevice.Free;

end;

Next we need to reorder the pages so we have a page layout similar to the following…

Subreport1: Page 1
Subreport2: Page 1
Subreport1: Page 2
Subreport2: Page 2… and so on.

To do this we first need to create a list of all the pages currently saved in memory.

lPages := TList.Create;

  lPublisher := ppReport1.Publisher;

  // clone all pages
  for liIndex := 0 to lPublisher.PageCount-1 do
    begin
      lPage := lPublisher.Pages[liIndex];
      lClonePage := TppPage.Create(nil);
      lClonePage.Assign(lPage);
      lPages.Add(lClonePage);
    end;

Once we have a list of pages to work with we can begin reordering them.  Each page object contains a PageNo property that needs to first be updated.

liIndex := 0;
  liPageNo := 1;

  while (liIndex < FFirstReportPageCount) and (FSecondReportPageCount > 0) do
    begin
      lFirstReportPage := lPages[liIndex];
      lFirstReportPage.AbsolutePageNo := liPageNo;
      lFirstReportPage.PageNo := liPageNo;

      Inc(liPageNo);


      lSecondReportPage := lPages[FFirstReportPageCount + liIndex];
      lSecondReportPage.AbsolutePageNo := liPageNo;
      lSecondReportPage.PageNo := liPageNo;

      Inc(liPageNo);


      Inc(liIndex);
      Dec(FSecondReportPageCount);
    end;

After the above has finished, we still may have extra pages left for the second subreport and we need to define the last page of the report.  The following code processes any extra pages and assigns the LastPage property to the last TppPage object.

  liCollatedPages := liIndex;

  if (FSecondReportPageCount > 0) then
    for liIndex := 0 to FSecondReportPageCount - 1 do
      begin
        lSecondReportPage := lPages[FFirstReportPageCount + liCollatedPages + liIndex];
        lSecondReportPage.AbsolutePageNo := liPageNo;
        lSecondReportPage.PageNo :=  liPageNo;

        Inc(liPageNo);

      end

  else if (liCollatedPages < FFirstReportPageCount) then
    begin
      if (liCollatedPages > 0) then
        TppPage(lPages[lPages.Count - 1]).LastPage := False;

      for liIndex := liCollatedPages to FFirstReportPageCount - 1 do
        begin
          lFirstReportPage := lPages[liIndex];
          lFirstReportPage.AbsolutePageNo := liPageNo;
          lFirstReportPage.PageNo := liPageNo;

          if (liPageNo = FFirstReportPageCount - 1) then
            lFirstReportPage.LastPage := True;
 
          Inc(liPageNo);

        end;
    end

Now we have successfully reordered the pages.  The final step is to send each page back to the cache, free up any used objects to prevent memory leaks, and let the publisher know how many pages the report will be printing.  Making a call to the TppReport.Print method will print the report with the newly ordered pages.

  {update page cache}
  for liIndex := 0 to lPages.Count - 1 do
    begin
      lPage := TppPage(lPages[liIndex]);

      lPublisher.ReceivePage(lPage);
    end;

  {free old pages}
  for liIndex := 0 to lPages.Count - 1 do
    TppPage(lPages[liIndex]).Free;

  lPages.Free;

  {update page number of new pages}
  for liIndex := 0 to lPublisher.PageCount - 1 do
    lPublisher.Pages[liIndex].Update(nil);