Consultancy and Programming
Advice, tips, techniques and downloads for Visual Foxpro developers.
When you preview a report in Visual FoxPro, the output appears in a window like the one in Figure 1. Many users dislike this preview window, to such an extent that developers often feel compelled to find alternative ways of displaying their reports on the screen.
These are some of the things that users and developers dislike about the default preview window:
In this article, we'll suggest ways in which you can improve the default preview window, or replace it with a custom-built report viewer of your own. Most of the techniques we describe are only available in VFP 9.0, but we'll start with one that's been around since version 5.0.
VFP's REPORT FORM command includes an IN WINDOW clause that lets you host the preview window in your own form. This isn't quite as wonderful as it first sounds, but it does open the way to a few useful enhancements.
To take advantage of this feature, first launch a form and make it visible. Adjust its size, position and other properties as necessary. Then execute the report, passing the name of the form (that is, the value of its Name property) to the IN WINDOW clause:
LOCAL loPreview loPreview = CREATEOBJECT("Form") * At this point, you can customise the form in * certain ways, for example by changing its * size, position, caption or icon. loPreview.Caption = "Report Viewer" loPreview.Name = "MyPreview" loPreview.Visible = .T. REPORT FORM MyReport PREVIEW IN WINDOW MyPreview
This example uses the built-in form class as the basis for the preview form, but you can use your own custom class if you prefer. By default, the preview will be modal (regardless of the form's WindowType property) and also non-resizable. The preview will fill the form, so any other controls on the form will be obscured.
You can change that behaviour by adding NOWAIT to the REPORT FORM command. This will make the form modeless and resizable (in fact, you can resize the preview area independently of the outer form). But keep in mind that when the variable holding the form object (loPreview in this example) goes out of scope, the form will disappear.
This technique gives you a little more control over the preview window. But, because you don't have an object reference to the preview itself, you have very little scope for customising it.
VFP 9.0's default preview window suffers from the same limitations as in earlier versions (apart from a couple of minor improvements, such as the increased number of zoom levels). But did you know that it's now possible to invoke an alternative window that overcomes some of those earlier problems?
The alternative window is based on a report listener. But you don't need to know anything about report listeners to take advantage of it. In fact, you only need to make one small change to your code. Instead of this:
REPORT FORM MyReport PREVIEW
REPORT FORM MyReport OBJECT TYPE 1
Figure 2 shows the result.
The new preview window is broadly similar to the old version, but with these useful improvements:
As before, the preview is modal by default. But, as before, you can use NOWAIT to make it modeless.
If you have many REPORT FORM commands scattered around your application, you won't need to modify each one separately. Just execute this command near the start:
SET REPORTBEHAVIOR 90
Once you've done that, all REPORT FORM commands containing the old PREVIEW clause will now invoke the new preview window. (SET REPORTBEHAVIOR is not scoped to the current data session.)
Because this alternative preview window is based on a report listener, you can do a certain amount of customisation just by adjusting the listener's properties. Consider the following code:
LOCAL loPreview loPreview = NULL DO (_REPORTPREVIEW) WITH loPreview
The _REPORTPREVIEW system variable points to a report previewing application. By default, that application is ReportPreview.APP. Given enough time and know-how, you could create your own report previewing application to use in place of ReportPreview.APP. We'll discuss that possibility later in the article. For now, let's keep things simple.
The third line in the above code calls ReportPreview.APP (or any replacement for it), passing a NULL object parameter by reference. ReportPreview.APP's job is to create a container object that will hold the preview. It uses the parameter to return a reference to the container.
Having obtained the reference (loPreview in this example), you can access the container's properties, for example:
loPreview.CanvasCount = 2 loPreview.ZoomLevel = 4 && 75% loPreview.Width = 800 loPreview.ToolbarIsVisible = .F.
Here, we're setting the initial canvas count (the number of pages displayed at one time) to two, the initial zoom level to 75% (the fourth item on the zoom menu) and the width of the preview to 800 pixels; we are also hiding the toolbar.
At this stage, there's nothing visible on the screen. In fact, the report hasn't even been rendered yet. So the next step is to instantiate a report listener object and to point it to our preview container:
loListener = CREATEOBJECT("ReportListener") loListener.PreviewContainer = loPreview lolistener.ListenerType = 1
We're using the built-in ReportListener class here, but we could just as well have used a subclass of it. Many developers prefer to use the _ReportListener foundation class (note the underscore at the start of the name), which offers a number of additional features.
Setting the listener's ListenerType property to 1 is important. This tells the listener to render the pages and to pass them to the preview object. If you left this property at its default setting of -1, the listener would produce no output.
The final step is to run the report, specifying the report listener as the destination:
REPORT FORM MyReport OBJECT loListener
The end product is basically the same preview window that we saw earlier (Figure 2), but subject to whatever changes you made to the relevant property settings (CanvasCount, ZoomLevel, etc. in this example). You can use this technique to customise the preview in many useful ways.
Going further, it's possible to create your own preview window, with whatever look and feel you care to give it. Essentially, this involves creating your own container, that is, the object that's instantiated by ReportPreview.APP. The container (which is most often a form) has to meet the requirements documented in the VFP 9.0 Help topic, The Preview Container API. Remember, it is this container that will actually hold the preview.
Once you've created the container, you must establish an object reference to it. One way to do that would be to create a substitute version of ReportPreview.APP. This program's main job is to instantiate the preview container. It must receive a NULL object reference as a parameter, and use it to return a reference to the object it instantiates. With this approach, you don't have to change any of the reporting code in your application. You just have to make sure that _REPORTPREVIEW points to your substitute ReportPreview.APP.
Alternatively, you could simply instantiate the container class directly, using CREATEOBJECT() or NEWOBJECT(). You then point the listener's PreviewContainer property to it, and invoke the report:
loPreview = CREATEOBJECT("MyContainer") loListener = CREATEOBJECT("ReportListener") loListener.PreviewContainer = loPreview REPORT FORM MyReport OBJECT loListener
So how do you go about developing the container class? Unfortunately, that's a complex subject, and one that's beyond the scope of this article. However, we can point you to a shortcut. The built-in report preview container (the one that produces the output shown in Figure 2) is written in VFP - and the source is available. Although you're not allowed to distribute the source code itself, you are free to adapt it for your own compiled applications.
To do so, first unzip the XSource.Zip file, which is in the Tools\XSource folder below the VFP9 folder. The source code for the preview window will be in the ReportPreview sub-folder. Although there's no documentation for this code, it's reasonably easy to follow. The key component which you'll want to investigate is the FRXPreviewForm class, in FRXPreview.VCX (Figure 3).
As you will see, this class contains all the methods for rendering, navigating, and generally managing the preview. For example, there are methods called ActionGoFirst, ActionPrint, ActionSetCanvasCount and ActionSetZoom, all of which do what their names suggest. You can use this code as the basis for your own customised report viewer, which you can then embellish in whatever way suits your application.
Figure 4 shows our own generic preview form. This contains code from the class mentioned above as well as a little custom code of our own. Our main aim was to place all the required controls - page navigation, zoom, canvas count, etc. - on the form itself, and thus avoid the user having to rely on the context menu or toolbar. The preview container is in fact the scrolling region that occupies the main part of the form. To create that scrolling region, we used the techniques described in the FoxStuff article, Create a scrolling region within a form. (A significant drawback of that technique is that it will only work if the form is modeless.)
Because most of the code in the form is Microsoft's copyright, we can't make the form available for you to download, nor can we show you the code. However, by using the existing source code as a starting point, you should be able to create your own preview form in much the same way that we have done.
If all this sounds like too much trouble, you have another option - one that involves very little programming. There are at least two third-party products available for purchase that you can use to easily create a custom report viewer. They are:
We don't have any recent experience of FRX2ANY, but we have used XFRX on a recent project, with satisfactory results. Figure 5 shows the report viewer that we created with the help of XFRX.
This form is similar to the preview window shown in Figure 4, but with some additional features. These include a Find button that lets the user search for any text anywhere in the report. With a large report, the search will be slow, but nevertheless this is a feature that users greatly appreciate. There are also buttons for exporting the report to PDF or Microsoft Word format. The search and export features are both provided by XFRX, and require very little additional programming. Although not shown here, XFRX also lets you create hyperlinks and bookmarks, and it supports many additional export formats.
We hope that this article has demonstrated that you don't need to put up with the drawbacks of Visual FoxPro's built-in preview window. Whether you tweak the built-in window, create your own viewer, or use a third-party product, you'll end up with a better way of showing your reports on the screen - something your users are sure to thank you for.
Mike Lewis Consultants Ltd. December 2007.
FoxStuff is maintained by Mike Lewis Consultants Ltd. as a service to the VFP community. Feel free to download and use any code or components, and to pass around copies of the articles (but please do not remove our copyright notices or disclaimers).
The information given on this site has been carefully checked and is believed to be correct, but no legal liability can be accepted for its use. Do not use code, components or techniques unless you are satisfied that they will work correctly in your applications.
© Copyright Mike Lewis Consultants Ltd.