![]() |
Reporting with dBASE Plus:
|
Conditional Page Breaks |
The goals for this Session are:
When you need to force a new page in your report, you must use the beginNewFrame() method of the streamSource. For example, if you have a grouped report and you want to start each group on its own page, you can call the beginNewFrame() method in the onRender event handler of the group footerBand.
You can also determine the amount of space remaining on your page with the renderOffset property. This is a property of the groupHeaderBand, groupFooterBand, or detailBand. The renderOffset property returns the distance from the top of the streamFrame to the bottom of the band. This measurement, subtracted from the height of the streamFrame, is the distance to the bottom of the printable page.
The combination of the beginNewFrame() method and the renderOffset property give us the tools to force new pages.
Lets consider the first page break report. Assume we need to force a new page based on a condition of the data that we are printing. Perhaps we have an inventory table and we want all the parts whose part number begins with "AAA" to print on one page and those beginning with "BBB" to print on a new page.
Open Paging1.rep in the report designer. This report lists the litter date, the Dam and the Sire from the litters.dbf table. How can we begin each month on a new page?
Lets look at forcing a new page for each month in the Litter date. (The litter dates in this report are restricted to one year. Because of this we don't need to worry about the year.)
Start by creating a calculated that returns the month of the litter date.
function litters1_onOpen local oField oField=new Field() oField.FieldName := "CalcMonth" this.rowset.fields.add(oField) oField.BeforeGetValue := ; {; Return month(this.parent["lit_date"].value) } return
Next, we need to initialize a custom property to keep track of the current month. We will do this in the onOpen event handler of the Text field that displays the date. The custom property is going to be assigned to the streamSource1 object.
Function KMFIELDTEXT1_onOpen this.parent.parent.curMonth = 1 return
This event will fire once -- when the report object is created -- and will sets the custom property. When the report renders, and begins looping through the data, we will compare the next row of data with the custom property.
To do this we need an onRender event handler for the detailBand. In the code
the row pointer will skip down one row in the rowset, compare the month in that
record with the current month, and if they are not the same, we will force a
new page. Here is the code for this procedure.
Function DETAILBAND_onRender local oStream, nNextMonth oStream = this.parent // move row pointer one row down oStream.rowset.next(1) // store next month value for next row nNextMonth = oStream.rowset.fields["calcMonth"].value if nNextMonth # oStream.curMonth and not oStream.rowset.endOfSet // reset the property this.parent.curMonth = nNextMonth // force a new page oStream.beginNewFrame() endif // move row pointer back one row oStream.rowset.next(-1) return
You can run this report and see that each month will print on it's own page.
The next report is going to force a new page if the child records within a group will not fit on the remaining space of the current page. To do this we need to know how many children records need to be printed and compare that with the amount of space on the page.
Open Paging2.rep in the report designer. This report displays parent and child data, and is grouped by the parent record. If you run this report and skip through the pages, you will notice that the groups are not always kept together on the same page. To change the report we need to add some code to the groupFooterBand's onRender event handler.
In the report designers add this event handler and then enter the following code:
function FOOTERBAND_onRender local oStream, oChild local nChildCount, nSpaceRemaining, nSpaceNeeded // create some reference shortcuts oStream = this.parent.parent oChild = this.parent.parent.parent.litters1 // set a bookmark on the current parent row oStream.bookmark = oStream.rowset.bookmark() // skip down to the next parent row oStream.rowset.next(1) // set range on the child rowset oChild.rowset.setRange( oStream.rowset.fields["REG_NO"].value ) // count the number of child records nChildCount = oChild.rowset.count() // remove the range restriction oChild.rowset.clearRange() // return the row point to the bookmark oStream.rowset.goto(oStream.bookmark) // determine the amount of space remaining on the page nSpaceRemaining = this.streamFrame.height - this.renderOffset // determine the about of space needed to print the bands // headerBand + detailBand + footerBand nSpaceNeeded = 0.9 + (nChildCount * 0.3) + 0.3 // uncomment the following for debugging /* ? '------------------------------------' ? ' child. num.: ' + nChildCount ? ' rest: ' + nSpaceRemaining ? ' needed: ' + nSpaceNeeded */ // determine if there is enough space to print the next group // if not, force a new page if nSpaceRemaining < nSpaceNeeded oStream.beginNewFrame() // ? '***New Page***' endif return
Although the inline comments explain what each line is doing, a few comments are needed. To determine the space needed, we need to sum the height of the groupHeaderBand, the groupFooterBand, and the detailBand for each child record in the group (nChildCount). It's a bit tricky to determine the values needed for these heights. Sometime the height of a band is fixed, so the value is known. However, the height property defaults to zero, which allows the band to adjust so that each individual row is as big as necessary. In the current report it's a good idea to specify the height all the time. Also make sure you set the expandable property for each band to false.
The Legal Stuff: This document is part of the dBASE Reporting Tutorial
created by Michael Nuwer. This material is copyright © 2002, by Michael
Nuwer. dBASE, dBASE Plus, dBASE SE, and dB2K are copyrighted, trademarked, etc.,
by dBASE, Inc., the BDE (Borland Database Engine) and BDE Administrator are
copyrighted, trademarked and all that by Borland, International. This document
may not be posted elsewhere without the explicit permission of the author, who
retains all rights to the document.