Chapter 7
Extending the Reporting System at Run Time
In addition to the design-time extensibility of VFP 9’s reporting system discussed in Chapter 6, “Extending the Reporting System at Design Time,” VFP 9 also provides the ability to extend the behavior of the reporting system when reports are run. In this chapter, you will learn about VFP 9’s report listener concept, how it receives events as a report is run, and how you can create your own listeners to provide different types of output besides the traditional print and preview.
As discussed in the “Enhanced run-time capabilities” section of Chapter 5, “Enhancements in the Reporting System,” the new reporting engine in VFP 9 splits responsibility for reporting between the report engine, which now just deals with data-handling and object positioning, and a new object known as a report listener, which handles rendering and output. Report listeners are based on a new base class in VFP 9, ReportListener.
During the run of a report, VFP raises events in a report listener as they happen. For example, the LoadReport event of a report listener fires when the report is loaded before being run. When an object is drawn on the report page, the Render method fires. The ReportListener base class has some native behavior, but extensibility really kicks in when you create and use your own subclasses. For example, a subclass of ReportListener could dynamically format a field, so under some conditions it prints with red text and under other conditions it prints in black.
This chapter starts with a discussion of how report listeners work, and then moves on to examining the properties, events, and methods (PEMs) of the ReportListener base class. After that, we discuss some of the subclasses of ReportListener that come with VFP. The rest of the chapter focuses on some cool uses of report listeners to create special effects you can’t do in earlier versions of VFP, including drawing charts without using ActiveX controls and creating your own report previewer.
Report listener basics
Report listeners produce output in two ways. “Page-at-a-time” mode renders a page and outputs it, renders the next page and outputs it, and so forth until the report is done. This mode is typically used when printing a report. In “all-pages-at-once” mode, the report listener renders all the pages and caches them in memory. It then outputs these rendered pages on demand, such as when the user clicks on the next page button in the preview window. This mode is typically used when previewing a report.
Report listeners can be used in a couple of ways. One is by specifying the OBJECT clause of the REPORT command. OBJECT supports two ways of using it: by specifying a report listener and by specifying a report type.
To tell VFP to use a specific listener for a report,
instantiate the listener class, and
then specify the object’s name in the OBJECT clause of the REPORT command.
Here’s
an example:
loListener = createobject('MyReportListener')
report form MyReport object loListener
If you’d rather not instantiate a listener manually, you can have VFP do it for you automatically by specifying a report type:
report form MyReport object type 1
The defined types are 0 for outputting to a printer, 1 for previewing, 2 for “page-at-a-time” mode without sending the output to a printer, 3 for “all-pages-at-once” mode without invoking the preview window, 4 for XML output, and 5 for HTML output. Other user-defined types can also be used; this is discussed in the “Registering listeners” section of this chapter.
When you run a report this way, the application specified in the new _REPORTOUTPUT system variable (ReportOutput.APP in the VFP home directory by default) is called to figure out which listener class to instantiate for the specified type. ReportOutput.APP looks for the specified listener type in a listener registry table. If it finds the desired class, it instantiates the class and gives the reporting engine a reference to the listener object. ReportOutput.APP is primarily an object factory, but it also includes some listeners that provide XML and HTML output and other utility functions.
Another way to use report listeners is with the new SET REPORTBEHAVIOR 90 command. This command turns on “object-assisted” reporting so the REPORT command behaves as if you specified OBJECT TYPE 0 when you use the TO PRINT clause or OBJECT TYPE 1 when you use the PREVIEW clause.
ReportListener
The next sections in this chapter examine the PEMs of ReportListener to understand its capabilities. One thing to note about ReportListener is that the unit of measure (for example, the values returned by the GetPageWidth method, and the size parameters passed to the Render method) is 960th of an inch.
Properties
Table 1 shows the properties of ReportListener.
Table 1. The properties of the ReportListener base class.
|
Property |
Type |
Description |
|
AllowModalMessages |
L |
If .T, allows modal messages showing the progress of the
report (the default is .F.). |
|
CommandClauses |
O |
An object based on the Empty base class with properties indicating the clauses used in the REPORT or LABEL command. See Table 2 for the properties of this object. |
|
CurrentDataSession |
N |
The data session ID for the report’s data. |
|
CurrentPass |
N |
Indicates the current pass through the report. A report with _PageTotal or TwoPassProcess set to .T. requires two passes; others only require one. 0 indicates the first pass of a two-pass report or that only one pass is required, while 1 indicates the second pass. |
|
DynamicLineHeight |
L |
.T. (the default) to use GDI+ line spacing, which varies according to font characteristics, or .F. to use old-style fixed line spacing. |
|
FRXDataSession |
N |
The data session ID for the FRX cursor (a read-only copy of the report file the reporting engine is running opened for a ReportListener’s use). |
|
GDIPlusGraphics |
N |
The handle for the GDI+ graphics object used for rendering. Read-only. |
|
ListenerType |
N |
The type of report output the listener produces. The default is -1, which specifies no output, so you’ll need to change this to a more reasonable value. See the discussion of the OutputPage method for a list of values. |
|
OutputPageCount |
N |
The number of pages rendered. Read-only. |
|
OutputType |
N |
The output type as specified in the OBJECT TYPE clause of the REPORT or LABEL command. |
|
PageNo |
N |
The current page number being rendered. Read-only. |
|
PageTotal |
N |
The total number of pages in the report. Read-only. |
|
PreviewContainer |
O |
A reference to the display surface the report will be output to for previewing. |
|
PrintJobName |
C |
The name of the print job as it appears in the Windows Print Queue dialog. |
|
QuietMode |
L |
.T. (the default is .F.) to suppress progress information. |
|
SendGDIPlusImage |
N |
1 or higher (the default is 0) to send a handle to an image for a General field to the Render method. This is numeric rather than logical to allow the possibility for subclasses to treat images differently if desired. |
|
TwoPassProcess |
L |
Indicates whether two passes will be used for the report. Set this to .T. to force a prepass even if _PageTotal isn’t used somewhere in the report. |
The CommandClauses property contains a reference to an Empty object with properties representing the various clauses of the REPORT or LABEL command, plus a few other goodies. Table 2 lists these properties.
Table 2. The properties of the CommandClauses object.
|
Property |
Type |
Description |
|
ASCII |
L |
.T. if the ASCII keyword was specified when outputting to
a file. |
|
DE_Name |
C |
The name of the DataEnvironment object for the report. The name specified with the NAME clause or the name of the report if not specified. |
|
Environment |
L |
.T. if the ENVIRONMENT keyword was specified. |
|
File |
C |
The name of the report to run. |
|
Heading |
C |
The heading specified with the HEADING keyword. |
|
InScreen |
L |
.T. if the IN SCREEN keyword was specified. |
|
InWindow |
C |
The name of the window specified with the IN WINDOW keyword. |
|
IsDesignerLoaded |
L |
.T. if the report is run from within the Report Designer. |
|
IsDesignerProtected |
L |
.T. if the PROTECTED keyword was specified. |
|
IsReport |
L |
.T. if this is a report or .F. if it’s a label. |
|
NoConsole |
L |
.T. if the NOCONSOLE keyword was specified. |
|
NoDialog |
L |
.T. if the NODIALOG keyword was specified. |
|
NoEject |
L |
.T. if the NOEJECT keyword was specified. |
|
NoPageEject |
L |
.T. if the NOPAGEEJECT keyword was specified. |
|
NoReset |
L |
.T. if the NORESET keyword was specified. |
|
NoWait |
L |
.T. if the NOWAIT keyword was specified with the PREVIEW keyword. |
|
Off |
L |
.T. if the OFF keyword was specified. |
|
OutputTo |
N |
The type of output specified in the TO clause: 0 = no TO clause was specified, 1 = printer, 2 = file |
|
PDSetup |
L |
.T. if the PDSETUP keyword was specified. |
|
Plain |
L |
.T. if the PLAIN keyword was specified. |
|
Preview |
L |
.T. if the PREVIEW keyword was specified. |
|
PrintPageCurrent |
N |
Defaults to 0. However, a preview window can set this to the page currently displayed when it calls the listener’s OnPreviewClose method. The listener could use this to enable the “Print current page” option in a Print dialog. |
|
PrintRangeFrom |
N |
Defaults to 1. However, a listener could set it to the starting page number to print from when printing after preview. |
|
PrintRangeTo |
N |
Defaults to -1. However, a listener could set it to the ending page number to print to when printing after preview. |
|
Prompt |
L |
.T. if the PROMPT keyword was specified. |
|
RangeFrom |
N |
The starting page specified in the RANGE clause; 1 if not specified. |
|
RangeTo |
N |
The ending page specified in the RANGE clause; -1 if not specified. |
|
RecordTotal |
N |
The total number of records being reported on in the main cursor. |
|
Sample |
L |
.T. if the SAMPLE keyword was specified with the LABEL command. |
|
StartDataSession |
N |
The data session that the REPORT or LABEL command was issued from. |
|
Summary |
L |
.T. if the SUMMARY keyword was specified with the REPORT command. |
|
ToFile |
C |
The name of the file specified with the TO FILE clause. |
|
ToFileAdditive |
L |
.T. if the ADDITIVE keyword was specified when outputting to a file. |
|
Window |
C |
The name of the window specified with the WINDOW keyword. |
A special comment about data session handling is in order. Four data sessions are actually involved during a report run. The first is the data session the ReportListener is instantiated in; SET(‘DATASESSION’) will give you the appropriate value when issued in a ReportListener method. The second is the data session the REPORT or LABEL command was issued from; check the StartDataSession property of the CommandClauses object to determine the data session ID. The third is the data session the FRX cursor is open in. The FRXDataSession property contains the data session ID for this cursor, so use SET DATASESSION TO This.FRXDataSession if you need access to the FRX. The fourth is the data session the report’s data is in. If the report has a private data session, this will be a unique data session; otherwise, it’ll be the data session the REPORT or LABEL command was issued from. The CurrentDataSession property tells you which data session to use, so if a ReportListener needs to access the report’s data, you need to SET DATASESSION TO This.CurrentDataSession. Remember to save the ReportListener’s data session and switch back to it after selecting either the report data or FRX data session.
Examine the code in TestDataSessions.PRG and run it to see how these different data sessions work.
![]()
The Developer Download files for this chapter, available at www.hentzenwerke.com, include TestDataSessions.PRG and two reports it uses: PrivateDS.FRX and DefaultDS.FRX.
Report events
Report events, which fire when something affects the
report as a whole, are shown in
Table 3.
Table 3. Report events of the ReportListener base class.
|
Event |
Parameters |
Description |
|
LoadReport |
None |
Analogous to the Load event of a form in that it’s the
first event fired and returning .F. prevents the report from running. Because
this event fires before the FRX loads and the printer spool opens, this is
the one place where you can change the contents of the FRX on disk or change
the printer environment before the report runs. |
|
UnloadReport |
None |
Like the Unload event of a form, UnloadReport fires after the report runs. This is typically used for clean up tasks. |
|
BeforeReport |
None |
Fires after the FRX loads, but before the report is run. |
|
AfterReport |
None |
Fires after the report runs. |
Band events
Band events fire as a band is processed. These events
are shown in Table 4.
Table 4. Band events of the ReportListener base class.
|
Event |
Parameters |
Description |
|
BeforeBand |
nBandObjCode, |
Fires before a band is processed. The first parameter
represents the value of the OBJCODE field in the FRX for the specified band,
and the second is the record number in the FRX cursor for the band’s record. |
|
AfterBand |
nBandObjCode, |
Fires after a band is processed. Same parameters as BeforeBand. |
Object events
These events fire as a report object is being processed.
EvaluateContents(nFRXRecno, oObjProperties): this event fires for each field (but not label) object just before it’s rendered, and gives the listener the opportunity to change the appearance of the field. The first parameter is the FRX record number for the field object being processed and the second is an object containing properties with information about the field object. The properties this object contains are shown in Table 5. You can change any of these properties to change the appearance of the field in the report. If you do so, set the Reload property of the object to .T. to notify the report engine that you changed one or more of the other properties. Also, return .T. if other listeners can make more changes to the field.
Table 5. Properties of the object parameter passed to EvaluateContents.
|
Property |
Type |
Description |
|
FillAlpha |
N |
The alpha, or transparency, of the fill color. Allows
finer control than simply transparent or opaque. The values range from 0 for
transparent to 255 for opaque. |
|
FillBlue |
N |
The blue portion of an RGB() value for the fill color. |
|
FillGreen |
N |
The green portion of an RGB() value for the fill color. |
|
FillRed |
N |
The red portion of an RGB() value for the fill color. |
|
FontName |
C |
The font name. |
|
FontSize |
N |
The font size. |
|
FontStyle |
N |
A value representing the font style. Additive values of 1 (bold), 2 (italics), 4 (underlined), and 128 (strikethrough). |
|
PenAlpha |
N |
The alpha of the pen color. |
|
PenBlue |
N |
The blue portion of an RGB() value for the pen color. |
|
PenGreen |
N |
The green portion of an RGB() value for the pen color. |
|
PenRed |
N |
The red portion of an RGB() value for the pen color. |
|
Reload |
L |