Author’s
note: The scope of this document is limited to providing a review
and example of how I put Ken Mayer’s PREVIEW.WFM report viewing utility
to use. Included is an example of passing parameters to a report either
by directly printing or by using PREVIEW.WFM. It is assumed that you already
know how to create forms and reports. If you are not at least a little
familiar with the Report Designer, queries and rowsets, and object oriented
concepts, please first review the knowledgebase files on these subjects,
located at the dB2K
website. I would also note as a disclaimer that some of the code used
in this document was ‘borrowed’ from others on the dB2K newsgroup. A sincere
thanks to all of the folks there.
dB2K offers a built in “ReportViewer” class which is defined in the online help as “A control to display a report on a form”. It does what it says and will display a report (.REP file) on screen. It is not horribly functional on its own though, and putting it to proper use requires some enhancements. To make life easier for us, Ken Mayer created a form called Preview.wfm.
Preview.wfm uses
a ReportViewer object and allows reports to be viewed on-screen. It also
provides pushbuttons for printing and traversing pages and even allows
for passing parameters to your report.
Preview.wfm is
among the many helpful, useful and downright handy pieces of dB2K code
contained in the dBASE Users’ Function Library Project (dUFLP). The library
is available for download from Ken
Mayer’s web site.
As
a novice dB2K user, I had a difficult time making my reports work with
the PREVIEW utility when I wanted to pass parameters to the report. Then,
one day, I finally “got it”. Since others have had many of the same questions
as I, it seemed worthwhile to provide an example here in the dBulletin.
Please keep in mind that I am a novice. This is my way of doing it, but
better ways may exist. I hope you find the examples helpful.
What
I wanted to accomplish
I
am re-writing an old DOS application designed to keep track of video tapes
for a video rental store. The data and the reporting needs are fairly simple.
I wanted the user to have the following options:
The
user dialog for making the selections is started by way of a menu selection
and looks like this:
To
see a sample PREVIEW form (from clicking the “Screen” button), click
here.
To handle the pushbuttons, I only had to create four methods for this form.
PBCancel_onClick()
The
PBCancel_onClick() method
doesn’t leave much to talk about. Since all it is supposed to do is close
the form, I typed form.close()
in
the method. ’Nuff said.
Function PBCancel_onClick form.close() return |
|
PBPrint_onClick()
The
PBPrint_onClick() method
is straightforward. It establishes an instance of the report, gets the
parameter array, sends the array to the report by creating a report property
called PARAMS,
sets the report to go to the printer and then prints it. Comments in the
code explain what each step does.
Function PBPrint_onClick // method of RepDialog.WFM local r, aParam // instantiate report for sending to printer
// get the parameter array to pass to the report
// pass aParams
array to report
// setting the
output property to 1 directs the report to printer
// print report
return |
|
PBPreview_onClick()
Like
PBPrint_onClick(),
the PBPreview_onClick() method
gets a parameter array from ParamSetup().
However, it then passes the array along to the
Preview.wfm form
(rather than the report itself) and
Preview.wfm takes
over from there.
Function PBPreview_onClick // Method of RepDialog.wfm // Revised 01/28/2001 // get the parameter
array to pass to the report
// That's all
we have to do. Preview.wfm takes care of the rest
// Note: I don't
know why but calling PreviewForm()
// so instead,
call it this way
// The parameters
above are:
return |
|
ParamSetup()
I allow the user to send the report directly to the printer or to view the report using Preview.wfm. Since both PBPrint_onClick() and PBPreview_onClick() require the same array setup, I’m using ParamSetup() to fill the bill. If you were offering only a single choice (just the ‘Screen’ pushbutton for example), you wouldn’t need a separate setup method. You could instead just include the ParamSetup() steps within the onClick() method for the ‘Screen’ pushbutton.
First, ParamSetup() establishes an array to contain the parameters. (Please note: The ReportViewer base class uses an associative array by default. For compatibility, ParamSetup() also uses an associative array.)
Next, ParamSetup() checks the group of radio buttons on the form to determine how the user wants to sort the report data.
Then, it changes the text of the report title based on this information.
Last,
it looks at the date entered by the user (if any). If available, it sets
the value of the RecDateStart array
element to use as a filter). Easy to understand, yes? I hope so. If not,
please drop me an email to let
me know what you don’t understand. I’ll try to explain it more thoroughly
and I’ll use your comments to refine this page.
Function ParamSetup() // Method of RepDialog.wfm // this sets up the parameter array and passes it back to // the calling method [PBPrint_onClick() or PBPreview_onClick()] // setup associative
array for passing to tapelist.rep
// Set value
of array's "SortChoice" element
// Set title
according to above choice
// Set date
filter element in aParams
return aParams |
|
Ok, we’re almost done. The last thing I need to talk about is the report itself and how it uses the parameter array to change the report to your liking. Report objects have a built-in method called Render(). This method, when called, simply prints the report by default. It doesn’t check for any kind of parameters passed by the user. So, in order to make this parameter array work, we need to create our own version of Render().Our version of Render() does check for parameters and changes report properties accordingly.
What I want to do is to get my version of Render() to run just once, when the report starts up. This allows me to set and/or change properties of the report. After all the report properties are set to my liking, my version of Render() no longer need to execute though the default/built-in Render() does. We accomplish this by writing code that allows my version of Render() to execute only once while allowing the default Render() to execute as many times as is necessary.
I
began by overriding the default Render()
method. In brief, to do this, open the
report in design mode and click on the Inspector. Make sure the report
itself is selected in the Inspector and not one of the elements of the
report. Then select the Methods tab. There, you should see the default
Render() method in the list of methods.
Select this method and click on the wrench to the right of it. This should
open the source editor and allow you to type your own custom
Form_Render() method. Here is what my
Form_Render() method looks like. Don’t
forget that this method is in the REPORT (.rep),
not in the report dialog form like the previous methods.
Function Form_Render // method of TapeList.rep // overridden render method // We only want this code to run once (just for the filter setup, etc). // Note that except for the call to super::render() at the bottom, // this code only runs if hasn't been called previously // Check for
existence of the runOnce logical property
// Check for existence of a ReportViewer object
endif
// if we have a params array, we need to set parameters for the report
if this.params ["RecDateStart"] # null
// Add subtitle listing presence of filter
if this.cSort # null
endif // if params SortChoice # null
return super::render() |
|
This is a somewhat long method but really not too difficult to understand if we break it down into separate parts. Once you get past the this.runOnce part, here is what happens:
This is my first attempt at writing any kind of technical document. So, if you have any questions in reference to this document or if you wish to offer constructive criticism as to how I could make it better, please drop me an email. I would like to hear from you.
Please note that I cannot supply any kind of technical support for dB2K or any other software product. Nor can I offer general programming assistance except for that which I might post in the dB2K newsgroups.
Steve
Hawkins
SeHawk
Services
dBASE
Plus - the only way to fly