[1] [2] [3] [4] [5]

Additional direct FRX adjustment

GENREPOX performs its work by actually creating a copy of your FRX, with adjusted entries depending on your special GENREPOX instructions. As I said earlier, some of these adjustments depend on capabilities inherent in the FRX format that aren’t available to us in the Designer interface.

There are some other changes that we can make by playing with the FRX as a table.  Most notably, we can adjust the printer instructions in the Expr, Tag, and Tag2 fields of the FRX header record.  As you can see in the next figure, the Expr field uses quite a legible format that you can edit with sensible values at runtime, if necessary.  The Tag and Tag2 fields, by contrast, which were the only means of handling printer data in Fox 2.x, are a mishmash you shouldn’t edit directly, but you’ll find that you may benefit if you clear out their contents entirely.

Here’s why: all three fields receive information when you edit the report, even if you have chosen „default printer“ rather than „specific printer“ for this report.  That’s because the Report Writer has to have some basis on which to form its sense of page definition and printer capabilities – and it uses your design-time default printer for this idea (unless you give it an alternative idea with „specific printer“).

Even though „default printer“ instructions will allow your users to print your report on their default printer at runtime, it is possible for the embedded development instructions to cause Windows to wonder why your particular printer driver isn’t available on the runtime machine, among other problems. You can REPLACE TAG WITH „“, TAG2 WITH „“ to avoid this problem. You can also sometimes benefit by removing a lot of the information in the Expr field if you think that it involves printer attributes your users’ printers may not have.

You can also add information to the Expr field, as I’ve indicated.  Try changing the Copies value for an instant feeling of power over reports! It’s true that we can still use PRINTJOB/ENDPRINTJOB and _PCOPIES in VFP.  But the old fashioned way causes VFP to emit the report extra times, while the number of copies simply tells the printer to churn out extra copies of the same pages, without causing any calculations, UDFs, or view-building to be re-done.

I’ll return to the subject of printer management towards the end of this paper. The balance of this section will deal with some of the common questions that people ask, regarding adjustments they wish to make in report layouts.

The contents of printing-related FRX header fields

The one about multiple detail bands

Reports that have a driving parent table in multiple one-to-many relationships often face this problem: how can they show the two or more sets of multiple children? For example, suppose I have a report of transactions grouped by customer, but the transactions are kept in „debit“ and „credit“ tables that have no direct relationship with each other. I would like to generate statements that show all debits on the account for the month, followed by all credits.

The reason this is called a „multiple detail band“ report is that it requires two separate detail scans, which may have different formats , different numbers of records in them, etc. But Fox only allows the possibility of one detail band. What’s a reporter to do?

There are actually many answers to this problem, boiling down to two types:

The child table through which you did not SCAN becomes the „real“ child table for the purposes of allowing FRX its normal path through records for each customer group. You can make sure that the special detail object for the „fake“ band prints only for the first  or last detail record of the group by placing this object in the group header or footer, rather than in the detail band as you might expect.

If these methods don’t thrill you, I’ll admit they are kludges.  You might be better off using Crystal Reports or another third-party reporting tool.  (In version 5, Crystal allows both nested subreports and multiple detail bands – check out http://www.img.seagatesoftware.com or http://www.vb5.com/crystal6.htm for more information about the new Version 6, which seems to have greatly improved features for applications developers.)

The one about summary bands

Have you heard the one about the summary band that was supposed to lie quietly at the bottom of the last page of the report but kept bouncing all over the page? (Just how do you sedate a report band, anyway?)

As with the fake detail object above, you get the results you’re after by putting something in a different band in which it would ordinarily „travel“. If you want the summary to be in a fixed position, the information must be in the page footer, rather than in a real summary band (unless you want it to be on a „separate page“, which you can format completely differently from the body of the report).

Now you have to make sure that this information prints only on the last page. To do that, you’ll use a simple trick: an outer group on an expression that will never change, such as .T., and a report variable initialed to .F. that stores its own name to itself, so it doesn’t change throughout the body of the report. In a group footer band event (it doesn’t matter whether you do it on entry or exit), set this report variable’s value to .T., signaling that this is the last page. Now put your summary information in the page footer with Print When expressions that only print these objects when the report variable is .T.

The one about multiple pages per detail band

In the DOS/Unix character-based Fox report writer, you could define a detail band to be as big as you wanted (you could also have any size page you wanted, but never mind that here). If the detail band was bigger than the physical page size, your detail band could have a some fields that didn’t appear until page two, and All Was Good.

In the GUI report writer, however, you are more rigidly tied to physical pages to define the attributes of the report.  Your detail band cannot be longer than a page and just slop over to page 2.  If you want more than one page’s worth of objects for a single detail band, you have to overlay objects within the band. You can use Print When expressions that indicate something like _PAGENO % 2 = 0 (for objects that print only on page 2) and _PAGENO%2 = 1 (for objects that print only on page 1).

Unless you know you have exactly the right number of objects to fit on the two pages, with no excess white space at the bottom of page 2, you will probably also want a group on RECNO() for this report, with a new page on group. This will force your first objects for the next detail band to Page 3 and the report will function as you expect. You can make your Print When and overlaying strategy more complex to handle more than two pages per detail band, of course.

REPORT FORM execution and event processing

The actual execution of a REPORT FORM command is a complex ballet. As the reporting engine moves the record pointer through detail after detail instance, it evaluates objects in a left-to-right, top-to-bottom sequence for each band, checking group breaks, page conditions, et cetera. In VFP we were given some major help understanding or at least intervening reliably in this event sequence, because the new report band events  can be counted on to be evaluated at predictable points during this sequence.

Since FoxPro 2, we have also had a powerful aid to runtime processing in report variables, which can be calculated and mixed up with each other, UDFs, IIF()s, and so on, in a bewildering number of ways.

In this section I’ll present an example report (T_O_C_1.FRX) that uses report variables and events, plus some help from the DE and some additional understanding of the way the REPORT FORM command executes, to take care of a number of common report design requirements:

First, let’s think about what we mean by „summary“ information, and why I thought it was important enough to serve as a key example here. It is true that the REPORT FORM command can use the SUMMARY keyword to present a high-level view of a report’s contents (SUMMARY omits the detail band). But this type of summary is not very flexible, and is not the only way you can extract helpful information from a report that will help the user get more out of the report’s data.

This example will show you how you can collect summary information during the report run, for display after the run, in the format of your choice. It creates a cursor of group headers and page numbers on which those groups began, suitable for a table of contents. I’ve prepared a second FRX (T_O_C_2.FRX) to output the table of contents after the first report runs, and a wrapper program (T_O_C.PRG) to execute the two reports in sequence.

The data in this example is a series of tokens and keywords, which you’ll find in rhe VFP installation files in the FDKEYWRD.DBF table. I chose it because it had a sufficient number of records, and because the nature of its data allowed me to create a completely arbitrary set of rules (report specifications) for its display.

I decided that I would group on the first letter of the token (something like a phone book), and that I wanted these group breaks to start a new column. But some of the tokens start with non-alphabetic characters. I didn’t want groups between these characters; I wanted them all to be treated as one group visually (because each group would have only a few detail instances and the columns would be mostly empty).

Notice I said that I wanted to treat these initial symbol-prefixed tokens as one group visually.  From the point of view of the FRX, each time the first character changes a new group still starts. In this simple example, I’m sure I could have designed a more complex group break expression than LEFT(token,1), so that all the symbol-prefixed tokens became one group. However, any calculations done by group would also have treated these tokens as one group, in that case.  It’s quite possible to have grouping requirements from the logical or calculation point of view that have are not the same as your visual requirements.  It’s also possible to have a need for a visual break that as literally nothing to do with your grouping.  For example, you could decide arbitrarily that you wanted a visual break every 10 detail items, should a group span more than 10 items.  You could force a visual break every time you encountered a memofield that was over a certain number of characters. The technique I’m showing you here can help in all these situations.

Keep in mind, as we go through the solution to this problem, that „new page conditionally on group“ would work the same way as „new column“ does here. The width of data for each record is limited, so I added columns to this report to give a better chance to see what is going on; with three columns you have the chance of seeing three groups per page.

The visual report break

This trick requires another outer group, which will do the „visual break“ (here, on column) for us, but we will control exactly when the group break occurs. We’ll break use a report variable as the outer group’s control expression this time.

Once again, we’ll use the report variable’s own name as its „value to store“, so that VFP never changes its value in the usual detail band way. However, we’ll choose to re-evaluate the report variable’s „initial value“ on the real group break (LEFT(token,1)).  This will give us a chance to decide whether this new group needs a visual break or not.  If we decide the new group requires a visual break, we increment the report variable, which forces a group break on the outer level, requiring a new column (or page).

We could do this work in a UDF or DE method, but in fact incrementing the report variable can be done in a single IIF() (in-line IF expression), like this:

IIF(ISALPHA(token) OR LEFT(token,1) = „_“,
    rptGroupBreak+1,rptGroupBreak)

As you can see in the figure, I’ve placed this IIF() directly in the initial value that the report variable receives every time the LEFT(token,1) group break appears.  Because a report variable’s initial value can reference itself, as it does here, I can accomplish the trick without additional code.

A group break on a report variable enables us to break conditionally, if we set up the report variable’s value with an IIF().

When you example T_O_C_1.FRX’s layout more closely in the samples on your source code disk, you will notice something odd: although the column header is LEFT(token,1) to match the inner group’s requirements, I’ve chosen to place this heading in the outer group’s group header band instead.  You may think that this was arbitrary, but in fact it reflects the event-order of the REPORT FORM command processing in a significant way. Had I put the header expression in the „proper“ group, the header would appear at the end of the previous group rather than where it belonged above the appropriate group.  The inner group break would occur, causing the header to print, before the variables associated with this group had a chance to re-evaluate their initial values (and, therefore, before the report variable we’re using for the outer group break has a chance to be incremented).

Putting this header in the outer group band also prevents it appearing when we don’t want the visual break. If we had the expression in the inner, logical group we’d have to use a Print When expression such as

ISALPHA(Token) OR LEFT(token,1) = „_“

,and we’d to indicate to the Report Designer that we wanted this line removed if blank.

Although this is a rather trivial example, conditional group breaks can get quite complicated, and they can be accomplished for logical as well as visual reasons. The technique is always pretty much the same, except that sometimes you put the conditional break inside the „regular“ group breaks, depending on the order of execution of report events that suits your needs.

[1] [2] [3] [4] [5]