VFP9 Report Overview
VFP9 Report Overview
VFP 9
Doug Hennig
Stonefield Software Inc.
1112 Winnipeg Street, Suite 200
Regina, SK Canada S4R 1J6
Voice: 306-586-3341
Fax: 306-586-5080
Email: dhennig@stonefield.com
Web: www.stonefield.com
Web: www.stonefieldquery.com
Overview
The major focus of VFP 9 is improving the reporting system. The list of new and
improved features is enormous: multiple detail bands, protection of objects in the Report
Designer, design-time events, ability to position objects absolutely, more zoom levels,
better menus, etc. This document explores these enhancements in detail, demonstrates
some types of reports you couldn’t do in earlier versions of VFP, and provides an
introduction to the “Extending the VFP 9 Reporting System” documents.
w w w . D e v T e a c h . c o m
Introduction
One of the biggest changes in VFP 9 is the incredible improvements made in the
reporting system. There are three aspects to this: an enhanced Report Designer and
other design-time improvements, enhanced run-time capabilities including output to
HTML and XML, and new engine capabilities such as support for multiple detail bands.
This document looks at each of these areas.
For the first point, the VFP team worked very hard to ensure that existing reports would
work exactly the same in VFP 9 as they did in earlier versions. In addition, the FRX
structure hasn’t changed (although some previously-unused fields are now used in VFP
9 and there’s now support for user-defined fields).
The keys to the second point are two things: a new system variable called
_REPORTBUILDER and report events. The Report Designer can now call an Xbase
application when events occur at design-time (such as adding a field or invoking a
dialog). This application is specified in _REPORTBUILDER, which by default points to
ReportBuilder.APP in the VFP home directory. You can use your own application if you
wish by setting _REPORTBUILDER to point to a different application. This topic is
discussed in detail in my “Extending the VFP 9 Reporting System, Part I: Design-Time”
document.
w w w . D e v T e a c h . c o m
In VFP 9, there are only a few dialogs, because all of the properties for an object are
now in one place. For example, the Properties dialog for all object types uses a tabbed
interface, so all possible properties for the object can be edited in one dialog and without
launching additional modal dialogs.
w w w . D e v T e a c h . c o m
The new Report Properties dialog combines the features of
the Page Setup, Title/Summary, Data Grouping, Variables,
and Set Grid Scale dialogs, plus adds new features as well.
These dialogs aren’t actually part of the product but are provided by ReportBuilder.APP.
That’s right, they’re Xbase dialogs! Since the source code for ReportBuilder.APP comes
with VFP (unzip XSource.ZIP in the Tools\XSource subdirectory of the VFP home
directory, then look in the ReportBuilder folder), you could customize or subclass the
various dialogs and behaviors to suit your needs.
The Report menu and the shortcut menu for a report have been reorganized and
have additional items (including Properties, which invokes the Report Properties
dialog) to make it easier to work with reports.
The mouse cursor now changes to provide a visual cue when an object can be
resized.
The Report Designer toolbar includes Page Setup and Font Properties buttons. Also,
the View menu includes an item for the Report Designer toolbar. Previously, you had
to choose Toolbars from the View menu and turn on the Report Designer toolbar in
the resulting dialog.
The Reports tab of the Tools, Options dialog has been reorganized and has a couple
of new options: how the Expression Builder should deal with aliases for fields and
whether the default run-time behavior is backward-compatible (the equivalent of
using the new SET REPORTBEHAVIOR 80 command; see the “New Reporting
w w w . D e v T e a c h . c o m
Syntax” section later in this document) or object-assisted
(the same as SET REPORTBEHAVIOR 90). Run-time
behavior is discussed later in this document.
Protection
If you allow users to modify reports in the Report Designer in your run-time applications,
you may have wished you could protect them from themselves. The Report Designer
has a lot of features that can get an unsuspecting user in trouble, such as the
DataEnvironment. If you only want the user to make simple changes, such as moving
fields around or adding a company logo, all of the features available in the Report
Designer are overkill.
You can control whether an object can be moved or resized, edited (that is, whether the
Properties dialog can be invoked), or deleted. You can even control whether the object
can be selected at all, or whether it appears or not.
For bands, you can control whether they can be edited or resized.
w w w . D e v T e a c h . c o m
At the report level, you can control which tabs of the Report Properties dialog and which
menu items are available.
For example, here’s what the Report Properties dialog looks like when MODIFY
REPORT … PROTECTED is used for a report with the Optional Bands, Data Grouping,
and Data Environment tabs turned off:
w w w . D e v T e a c h . c o m
Design-Time Captions
You may have noticed that the Protection tab for a field object has an additional setting:
design-time caption. This allows you to indicate what appears in place of the field
expression when a report is modified in protected mode.
For example, compare these two Report Designer sessions. This one used MODIFY
REPORT without the PROTECTED keyword:
This one used the PROTECTED keyword. Notice that the fields show descriptive names
such as Employee Name rather than the actual expression such as
ALLTR(Employees.firstname)+" "+ Employees.lastname. This allows you to shield your
users from seeing the real expressions used for fields and display meaningful descriptive
w w w . D e v T e a c h . c o m
names instead. Note that if they bring up the Properties
dialog, they’ll see the real expression because that’s the
value they’d have to edit rather than the descriptive name.
Design-Time Tooltips
In addition to design-time captions, you can also specify design-time tooltips for report
objects (all object types, not just fields). Set the desired tooltip text in the Tooltip setting
on the Other page of the properties dialog for an object.
w w w . D e v T e a c h . c o m
Absolute Positioning
One thing I’ve wanted for a long time is the ability to specify
the exact size and position of an object by typing the top, left, height, and width values
rather than having to carefully move or resize it one pixel at a time to get it to the correct
place and shape. The General tab of the object properties dialogs now allow you to do
just that.
Default trimming: an ellipsis is added to the end of the text indicating that there is
more data that can’t be seen. Notice in the following report that several company
names have an ellipsis at the end.
Trim to nearest character: this is similar to the truncation that earlier versions of VFP
use.
w w w . D e v T e a c h . c o m
Trim to nearest word: this cuts off the text at the last
whole word.
Filespec, show inner path as ellipsis: this has the same effect as the
DISPLAYPATH() function; characters at the start and end of the expression appear
but middle characters are replaced by an ellipsis.
DataEnvironment Handling
There are two changes dealing with the DataEnvironment of a report: you can now save
the DataEnvironment as a class and you can load the DataEnvironment from another
report or from a DataEnvironment class.
VFP 8 added the ability to visually subclass a DataEnvironment. In VFP 9, to save the
DataEnvironment of a report as a class, open the DataEnvironment window and choose
Save As Class from the File menu.
International Support
The Windows Font dialog includes a Script setting that allows a user to select the
desired language script. Values include Western, Cyrillic, Japanese, Hebrew, and
Arabic. VFP objects support this with their FontCharSet property. Unfortunately, earlier
versions of VFP didn’t store the selected script in a report, either for report objects or for
the default font for the report. In VFP 9, this value is now saved so full support is
provided.
In addition, the VFP team ensured that alignment works better in both left-to-right and
right-to-left languages.
w w w . D e v T e a c h . c o m
Multiple Detail Bands
Crystal Reports is one of the most popular reporting tools in
the world. One of the main reasons many VFP developers, who have a report writer built
into the product, use Crystal is because it supports sub-reports. A sub-report is a report
that runs within a report. The most common use for a sub-report is to report on multiple
children for a parent table.
For example, suppose you have a customers table, an invoices table, and a credit notes
table. You may want to show customers, their invoices, and credit notes on a report. The
complication here is that the report has three tables it needs to go through, and while
invoices and credit notes are related to customers, they aren’t related to each other. The
way you’d resolve this in Crystal is to create a report showing customers and their
invoices and then add a sub-report to it showing the credit notes for the current
customer.
Unfortunately, until now, there wasn’t a good way to do this in VFP. One commonly used
workaround is to create a cursor combining invoices and credit notes, with a “record
type” field distinguishing which records come from which table, and then have the report
include fields from both types of records in the detail band with Print When expressions
on the fields so only certain fields print for each type of record. This makes for a very
ugly report to maintain!
Fortunately, VFP 9 solves this problem nicely with a new feature: multiple detail bands.
Record Processing
Before we look at multiple detail bands, let’s discuss how VFP moves through records in
a report. A report has a single “driving” cursor. VFP moves through this cursor in a single
pass; that is, the cursor is processed only once. The processing of these records is
paused by group breaks; the report engine takes whatever action is specified (for
example, printing a group footer for the former group and a group header for the new
one) and then continues processing the cursor. The set of records processed without
interruption can be referred to as a “detail scope”. If there are any groups for the report,
the detail scope will be those records inside the innermost group. If not, it’ll be the entire
report scope.
In VFP 9, there can now be multiple detail scopes (up to 20). The records for a particular
detail scope can be from related records in child tables or they can be from the driving
alias, which means it can be processed multiple times. The Report Designer presents
these multiple detail scopes as multiple detail bands. An important thing to note is that
detail scopes are consecutive, not nested like group breaks are.
Calculated fields and report variables can now be scoped to a particular detail band.
Interestingly, variables will retain their values until the band they’re scoped to is
processed again. This means you can use these variables in later detail bands if
necessary. The Variables page of the Report Properties dialog now uses “reset based
on” rather than “reset at” as the prompt for a variable’s scope to reinforce this idea.
w w w . D e v T e a c h . c o m
Creating Multiple Detail Bands
Every report has at least one detail band. To create
additional detail bands, choose Optional Bands from the Report menu or the report
shortcut menu, or bring up the Report Properties dialog and choose the Optional Bands
page. The Add button adds a new detail band and the Remove button removes the
selected detail band. You can rearrange the order of the bands in the list.
In the properties dialog for a detail band, you can specify whether the band has header
and footer bands and what the target alias expression is for the detail scope. Ordinarily,
the report engine processes a single record in the driving cursor before moving to the
next detail band. However, if you specify a child cursor as the target alias, the report
engine will process all child records of the current driving cursor record before moving to
the next band. Note that you enter the target alias as an expression; to use a hard-coded
name, surround it with quotes. However, since this is an expression, you could enter the
name of a variable that contains the target alias or even call a UDF. This may lead to
some very interesting types of reports!
w w w . D e v T e a c h . c o m
The target alias expression can evaluate to one of three values:
The alias of a child table means that the report engine will process all child records of
the current driving cursor record before moving to the next band.
The driving alias, which is valid in the first detail band only, means the report engine
will process all driving alias records until it encounters a group break or the end of
the report scope before moving to the next detail band.
w w w . D e v T e a c h . c o m
The report has a group expression on Employees.EmployeeID and the desired fields
from the Employees table appears in the group header band. There are two detail
bands, one with a target alias of EmployeeTerritories and the other with a target alias of
Orders; the appropriate fields appear in each band. Here’s what the report looks like in
the Report Designer:
w w w . D e v T e a c h . c o m
Example 2: Pre-calculation of Totals
The next example is similar to the first one, but doesn’t show two child tables; instead, it
runs through the same child twice. The idea here is that we want to calculate the number
and total amount of the orders for each employee, but we want to show these
calculations before displaying the actual orders. In addition, we want to show the amount
of each invoice as a percentage of the total amount, meaning we have to pre-calculate
the total.
In previous versions of VFP, this would require doing the calculations prior to running the
report, and using the results of those calculations in the report. In VFP 9, this simply
means having one detail band that does the calculations and another that displays the
results. In the case of this example, EmployeesMD2.FRX, both detail bands have the
Orders table as the target alias. Here’s what the report looks like in the Report Designer;
notice that there are no objects in the Detail 1 band:
w w w . D e v T e a c h . c o m
There are two variables defined: OrdersCount, which is a
“count” variable that’s reset based on Detail 1, and
OrdersTotal, which sums Order_Subtotals.Subtotal and is
also reset based on Detail 1. Order_Subtotals is a view in the Northwind database that
calculates the subtotal for each order, so summing the Subtotal field from that view
(which is related to the Orders table, so it’s positioned to the correct record) gives the
total of the desired orders. So, all the Detail 1 band is used for is to process all order
records for the current employee and calculate the proper values in the OrdersCount
and OrdersTotal variables. Then, the order records are processed a second time in
Detail 2. The number of orders and total amount is shown in the header for Detail 2 and
the orders and the percentage that each is of the total amount is shown in the detail
band.
Handling more types of report output than just printing and previewing.
Using GDI+ for report output. This provides many significant improvements, such as
more accurate rendering, smooth scaling up and down of images and fonts, and
additional capabilities such as text rotation.
Before VFP 9, the report engine was rather monolithic; it handled everything and with a
few exceptions (UDFs, expressions for OnEntry and OnExit of bands, etc.), you couldn’t
interact with it during a report run.
w w w . D e v T e a c h . c o m
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. Because report listeners
are classes, we can now interact with the reporting process in ways we could only dream
of before.
VFP 9 includes both the old report engine and the new one, so you can run reports
under either engine as you see fit. However, once you see the benefits of the new report
engine, you won’t want to go back to old-style reporting unless it’s absolutely necessary.
w w w . D e v T e a c h . c o m
A report listener is an object that provides new-style
reporting behavior. Report listeners are based on a new
base class in VFP 9, ReportListener. 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:
The defined types are 0 for outputting to a printer, 1 for previewing, 4 for XML output,
and 5 for HTML output. Other types can also be used.
When you run a report this way, the application specified in the new _REPORTOUTPUT
system variable (by default, ReportOutput.APP in the VFP home directory) is called to
figure out which listener class to instantiate for the specified type.
You’re probably thinking “But I’ve got tons of reports in my application. Do I have to find
and modify every REPORT command in the entire application?” Fortunately, there’s an
easier way: SET REPORTBEHAVIOR 90 turns on object-assisted reporting by default.
This means the REPORT command behaves as if you’ve specified OBJECT TYPE 0
when you use the TO PRINT clause or OBJECT TYPE 1 when you use the PREVIEW
clause. SET REPORTBEHAVIOR 80 reverts to VFP 8 and earlier behavior. If most or all
of the reports in your application work just fine in object-assisted mode, use SET
REPORTBEHAVIOR 90 at application startup. Because there are rendering differences
between new and old-style reporting, some reports may need to be tweaked to work
properly with new-style reporting, so either tweak them or use SET REPORTBEHAVIOR
80 to run just those reports.
ReportOutput.APP and report listeners are discussed in detail in my “Extending the VFP
9 Reporting System, Part II: Run-Time” document.
w w w . D e v T e a c h . c o m
Notice there are new buttons allowing you to specify the number of pages displayed at a
time. Here’s what 4 pages at a time looks like:
w w w . D e v T e a c h . c o m
Also notice you can now scale the report above 100%: 200, 300, and 500% are
supported (this works in both the new and old preview windows). Another new feature is
that a shortcut menu is now provided.
That’s right: the preview window is an Xbase form rather than a native window. That
means you have full control over its appearance, something that was painful to do in
previous versions.
w w w . D e v T e a c h . c o m
Progress Feedback
When a long report runs, you may wish there was some way
of showing the user that something’s happening. The UpdateListener class built into
ReportOutput.APP does this; it displays the progress as the report is processed, and
also gives the ability to cancel. You can disable this feedback if you want to, or provide
your own using a report listener. An example of this is shown in my “Extending the VFP
9 Reporting System, Part II: Run-Time” document.
Listener type 5 specifies HTML output and type 4 is for XML output, so you could just
use the following command to output to HTML:
However, this doesn’t give you any control over the name of the file to create or other
settings. Instead, we’ll call ReportOutput.APP to give us a reference to the desired
listener, set some properties, and then tell the REPORT command to use that listener.
The following code (taken from HTMLOutput.PRG) creates an HTML file called
MyReport.html from the first six pages of the EmployeesMD report. When you specify
type 5, ReportOutput.APP uses its built-in HTMLListener class to provide output.
loListener = .NULL.
do (_reportoutput) with 5, loListener
loListener.TargetFileName = 'MyReport.html'
loListener.QuietMode = .T.
report form EmployeesMD object loListener range 1, 6
w w w . D e v T e a c h . c o m
The following code (taken from XMLOutput.PRG) creates an XML file called
MyReport.xml from the first six pages of the EmployeesMD report, containing only the
data. In this case, the XMLListener class in ReportOutput.APP is used.
loListener = .NULL.
do (_reportoutput) with 4, loListener
loListener.TargetFileName = 'MyReport.xml'
loListener.QuietMode = .T.
loListener.XMLMode = 0 && 0 = data only, 1 = layout only, 2 = both
report form EmployeesMD object loListener range 1, 6
w w w . D e v T e a c h . c o m
HTML output actually uses the XML listener to produce XML
and then uses XSLT to produce the HTML end-result.
Here’s some simple code, taken from GraphicOutput.PRG, that outputs a single page of
the EmployeesMD report to a GIF file.
loListener = createobject('ReportListener')
loListener.ListenerType = 3
report form EmployeesMD object loListener range 1, 1
loListener.OutputPage(1, 'MyReport.gif', 104)
w w w . D e v T e a c h . c o m
See my “Extending the VFP 9 Reporting System, Part II: Run-Time” document for details
on this topic.
Use a VFP-specific third-party tool that supports PDF output, such as Mind’s Eye
Report Engine, XFRX, or FRX2Any.
In VFP 9, you can output to a TIFF file and then use the freeware GhostScript utility
to convert it to a PDF file.
Because this was written early in the VFP 9 beta, this is still up in the air. Stay tuned,
though–I can guarantee there will be some great solutions available soon.
w w w . D e v T e a c h . c o m
Other Printing Enhancements
There are a number of other general printing enhancements
in VFP 9.
SYS(1037), which displays the Page Setup dialog, has some new capabilities. First, the
dialog looks different. Here’s the VFP 8 version:
w w w . D e v T e a c h . c o m
Second, it now has a return value indicating whether the user took any action (“1”) or not
(“0”).
The biggest change, though, is that you can pass it a parameter to tell it what to do.
Passing 0 or no parameter displays the default Page Setup dialog. To display and
possibly change the Page Setup settings for a particular report, open the report as a
table (that is, USE MyReport.FRX) and then call SYS(1037, 1). You can save and
restore the current default printer settings using SYS(1037, 2) (which writes the default
printer settings to an FRX) and SYS(1037, 3) (which sets the default printer settings to
those in the FRX); neither of these displays the Page Setup dialog. This is typically used
to push and pop printer settings.
APRINTERS() can now accept a new optional argument of 1, in which case the resulting
array will have three new columns showing the driver, comment, and location.
The Print dialog displayed when you use PROMPT keyword in the REPORT command
has a more modern appearance. Here’s the VFP 8 version:
w w w . D e v T e a c h . c o m
Here’s what it looks like in VFP 9:
Summary
VFP 9 has an incredible number of changes in the reporting engine. These changes
make it easier to work with the Report Designer, make it possible to create types of
w w w . D e v T e a c h . c o m
reports we either couldn’t do before or were hard to do,
provide new types of output, and allow us to extend the
capabilities of the reporting engine in both design-time and
run-time environments. We can expect a lot of new uses for VFP reporting, and many
new discoveries about what its capabilities are in the years to come.
Note: since this document was written during the first beta phase of VFP 9, many of the
details described in this document may be different in the release version. Be sure to
check my Web site (www.stonefield.com) for updates to this document and the
accompanying samples.
Biography
Doug Hennig is a partner with Stonefield Systems Group Inc. and Stonefield Software
Inc. He is the author of the award-winning Stonefield Database Toolkit (SDT), the award-
winning Stonefield Query, and the CursorAdapter and DataEnvironment builders that
come with Microsoft Visual FoxPro. Doug is co-author of “What’s New in Visual FoxPro
8.0”, “The Hacker’s Guide to Visual FoxPro 7.0”, and “What’s New in Visual FoxPro 7.0”.
He was the technical editor of “The Hacker’s Guide to Visual FoxPro 6.0” and “The
Fundamentals”. All of these books are from Hentzenwerke Publishing
(http://www.hentzenwerke.com). Doug writes the monthly “Reusable Tools” column in
FoxTalk. He has spoken at every Microsoft FoxPro Developers Conference (DevCon)
since 1997 and at user groups and developer conferences all over North America. He
has been a Microsoft Most Valuable Professional (MVP) since 1996.
w w w . D e v T e a c h . c o m