This one is for you VFP reporting folks, although the subject comes to mind by way of a SQL Server reporting conundrum in the forums recently.
The problem-to-solve is one of those Reporting Convergence things you'll see no matter where, and how, you report:
How do you handle stretching content that is longer than a single page in length?
The interesting point, in this case, is not how similar things are, but how the resolution in RS will contrast with what you can do in VFP in similar scenarios.
The original layout in the recent thread looked like this:
Page Header |
Table Header |
="Group Header: " & Fields!Field1.Value |
="Value of another field:" & Fields!Field2.Value |
=Fields!LongField.Value |
="Group Footer: " & Fields!Field1.Value |
Table Footer |
Page Footer |
The long text field you see in this detail band always moved to the next page, leaving a large block of empty space in the layout where you would have expected the long text to start displaying. After some experimenting with why this was causing a page break on every page, I determined that the rules in RS are something like this:
When a text field requires more height than is left on the page, it is pushed to the next page. However, when a text field requires more height than the full-available height on a page (ie it is the first thing on that page), it is not pushed to the next page, because this would happen infinitely. The thing would never appear and the report would never finish.
Therefore, the resolution in RS looks something like this (you can look at the code, which isn't a big deal, in that thread). You split the long field into a section which has room to appear on the "proper" page and move the rest of the content to a separate element. Because this separate element is now the first thing on the next page, it is properly rendered, no matter how many pages it may take to do so, on subsequent pages. The "continued" message you see here, between the two portions of the long field contents, is optional:
Page Header |
Table Header |
="Group Header: " & Fields!Field1.Value |
="Value of another field:" & Fields!Field2.Value |
=Code.SplitLongField(Fields!LongField.Value,1) |
=Code.GetContinuedMessage(Fields!LongField.Value) |
=Code.SplitLongField(Fields!LongField.Value,2) |
="Group Footer: " & Fields!Field1.Value |
Table Footer |
Page Footer |
Contrast with the VFP 9 Reporting System
If you use VFP reporting, you know that we don't need to worry about this for stretching text. The code splitting the text into "what will fit on the current page" and "what's left" is handled internally by the VFP report engine. It might not be handled perfectly all the time, but it's handled less simplistically.
In fact in writing the code for this resolution, I basically did in VB code what I thought the RS renderers could potentially do themselves. Maybe they don't because it would cause even more havoc and differences between renderers, as each output format struggled with its own page layout requirements.
But there is an interesting parallel, with instructive differences, between the scenario above and the ReportListener's AdjustObjectSize feature for stretching images and shapes.
As you probably know, in the ReportListener.AdjustObjectSize event you can consult the MaxHeightAvailable member of the properties object to see how much usable space there is on the rest of the page. By "usable" I mean that this value does not include space in the remaining page length that must be substracted/reserved for footer elements. (I'm not going to repeat the docs information here, because it's expressed properly there already; this post does not constitute any sort of correction.)
This feature effectively gives us a chance to decide what to do when you want to render dynamic content that is not going to fit on the page. Just as I could write some limited VB code to do what I thought the RS engine might do, you have the ability to make pretty much the same calculation — with whatever rules suit you, in Xbase code — that report engines all have to make in this situation internally, and that report engines often hide from you with almost-as-often annoying results.
With great power comes great responsibility
Remember: The RS engine chooses what to do to avoid a possible infinite push-down loop. Its choice is as follows: push down once if an item does not fit. Then, on the next page, if the item still won't fit even though it has the full usable page to deal with, unilaterally choose a pagination method for the item's content to break it up between pages.
In VFP, it's up to you. You get MaxHeightAvailable to help you make your decision about what Height should be used. You can set it too high for the remaining page space, forcing a page break if you want.
Alternatively, if you set Height lower, you can make the thing fit on the current page. You can then use the Reattempt member value of the properties object, on the next page, to decide what you want to do there. If you do not pay attention to Reattempt, and again set Height to a value that still won't fit, you can in fact cause an infinite push-down loop to occur.
When you think about what you might do with this capability, remember that the shape or image layout element might just be a placeholder for other custom content (RTF, anyone?) that you are shoehorning into this report.
Rather than post an example here, I'm going to ask you to check the code that gets generated for an image or rectangle when you set Dynamic conditions for its Height or Width in SP2.
Press the Script… button from the Dynamics tab in the ReportBuilder Properties dialog, and don't forget that you can Code Zoom from there for a closer look. You may have to change your preferences for .TXT files to get syntax coloring in the Code Zoom dialog.
It's worthwhile getting used to doing this, and besides, you'll see that the code there observes other niceties, such as the overall max allowable values, which you'll also find listed in FOXPRO_REPORTING.H:
#DEFINE FRX_RUNTIME_LAYOUT_DIMENSION_LIMIT 64000
There's also helpful information in editboxes within the ReportBuilder dialogs.
And there you have it.
You can hunt your own game and bring it to the table, or you can shoot yourself in the foot.
Isn't that, yea verily, the essence of the VFP way?