Paradox Community
Search:

 Welcome |  What is Paradox |  Paradox Folk |  Paradox Solutions |
 Interactive Paradox |  Paradox Programming |  Internet/Intranet Development |
 Support Options |  Classified Ads |  Wish List |  Submissions 


Paradox Programming Articles  |  Beyond Help Articles  |  Tips & Tricks Articles  


Impossible ObjectPAL Techniques
Programming Reports
© 1994 - 2002 Mark Pauker

Important Note From The Editor

Recently, this article was graciously forwarded to the community (via Vladimir Menkin) by Mark Pauker. However, the article was written back in 1994 and the download includes files dated from 1993 through 1996. It's quite possible that the things you read below will not work under later versions of Paradox and/or with later versions of the BDE and Windows. Please test the concepts below in test environments and not on real/live data or code!

Thanks Mark for everything you've contributed to the Paradox community!


Programming Reports

It may seem odd to see a section about reports in a paper on ObjectPAL, since Paradox doesn't support ObjectPAL within reports. Still, there are ways to "program" reports in order to attain functionality that is beyond what is generally thought to be provided.

Conditional Objects in Reports

One common need within reporting is a conditional field. A conditional field is one that prints only if a certain set of criteria is satisfied. Paradox supports this type of field quite well with calculated fields and the iif keyword. A report displaying debits might have a calculated field defined as follows:
iif([Answer.Balance] <= 0, -[Answer.Balance], "")
While this is quite straightforward, it deals only with the text itself. What if the task we wanted to accomplish was a bit more complex? What if we wanted to cause an entire object (a box, for example) to be printed conditionally? The easiest way to implement a "conditional object" is by using a blotter.

A blotter is an opaque white object that is placed on top of an object that you don't want displayed. The trick in creating a "conditional object" is in first creating a "conditional blotter". Fortunately, Paradox makes this very easy. The blotter that we will be using is simply a field object whose color is opaque white, and whose display type is unlabeled. We must also turn off the blotter's runtime/fit width and runtime/fit height properties so it will stay a fixed size. (Otherwise it might shrink and not block out the object in question.) When we completely cover an object with this blotter, that object will not be displayed on the report.

Now that we have created a blotter, we need to make it conditional. This can be accomplished by turning the blotter's runtime/fit width property back on, and creatively using a calculated field with iif. To understand how this will work, let's first consider the ramifications of turning runtime/fit width on. Since the field is undefined, its value will be blank (and its width will be zero). When Paradox is asked to display the report, the blotter's width will shrink down to zero and thus will disappear. This will leave the object underneath the blotter visible. If, on the other hand, the blotter field contained some value, then it would take on the width of that value and cover whatever was underneath it. Therefore the blotter field should be a calculated field with a formula such as:
iif([Answer.ShowBox] = "Yes", "", fill(" ", 255))
where the second argument of the fill procedure makes the field large enough to cover the entire width of the object you wish to blot out. Note: Make sure that objects to the right of the blotter that you do not want to be effected by it are pinned horizontally, and are placed physically above the blotter in the z-order of the report.

Analyzing the above calculated field, if the ShowBox field is set to "Yes" then the value of the blotter field will be blank, it will shrink down to have zero width, and the object underneath it would be visible. Otherwise the blotter will be filled up with spaces, and the object underneath it would not be visible. There is one caveat to this approach: Text will bleed through a blotter when the report is sent to the printer. (all other objects work, and text works when sent to the screen.) For printed reports you will need to add iifs to all textual fields underneath the blotter in addition to using the blotter itself.

Attaching Methods to Reports

Being able to add conditional objects to reports is not too difficult, but what about changing colors? How about altering sort order within a tableframe? Wouldn't it be nice if we could use ObjectPAL within reports? The fact is... you can! (I would like to acknowledge Ulrich Zindler, who I believe initially discovered the capability of placing ObjectPAL in reports.)

Before continuing, I would be remiss if I didn't provide the following warning: The information contained within this section describes Paradox functionality that is not stable in all areas, and thus is specifically not supported. If you apply it to your applications, you are doing so at your own risk. Borland will not answer any questions about this topic, nor will they support any problems that arise out of its use.

The above warning notwithstanding, ObjectPAL is available within reports, and I have found it to be extraordinarily useful in certain circumstances. There are a few ways of adding ObjectPAL to a report, but the easiest way is as follows:
  1. Open the report you wish to add code to in design mode.
  2. Open a form (any form) in design mode, and bring up the "methods" dialog box.
  3. Pin down the "methods" dialog by clicking on the icon in the upper-right corner. (This step is optional.)
  4. Click on the report object you wish to add code to.
  5. The "methods" dialog should now be displaying a list of methods available for that report object. You can now open ObjectPAL editor windows as if you were working on a form.
Using ObjectPAL within reports is quite different from the way you would use it within a form. The most significant difference is that you shouldn't add code to built-in methods, because built-in methods are not called in a report. The place to put your code is in custom methods, and in the Uses, Var, Type, Const, and Proc windows. The next issue is figuring out how and when your code can be called. As with the "conditional objects" approach described above, the use of ObjectPAL within reports is predicated on the creative use of calculated fields.

Changing an Object's Color

To invoke a custom method on a report, place a calculated field on the report which calls the method. Each time Paradox attempts to display that field, the calculation will be performed and thus the custom method will be called. To illustrate, consider an example where you want an object to appear red if a given field is negative, white if it is positive, and green if it is above 1000. The code required to do this is as follows:
method setColor() string
  var
    siFieldVal smallInt
  endVar
  siFieldVal = numField'value
  switch
    case siFieldVal < 0:
      box'color = Red
    case siFieldVal > 1000:
      box'color = Green
    otherwise:
      box'color = White
  endSwitch
  return ""
endMethod
This code assumes that the field whose value we are interested in has already been placed on the report, and that the name of that field object is numField. It is responsible for changing the color of the object: box (another object on the report). The method returns a value because it is going to be called by a calculated field, and all methods called within calculated fields must return values. By returning a blank, we know that the value will not be displayed anywhere on the report.

The only thing left to do is to determine where the code should be placed, and how it will be called. To simplify matters, let's create an unlabeled field object and attach the setColor method to that object. In addition, let's make that field a calculated field and define the calculation as follows:
setColor()
Now, whenever Paradox attempts to print this field, the setColor method will be called. If we place the field in the record band above and to the left of the object named box, then the color of box will be changed each time before it is printed. If the field is placed in the record band below and to the right of box, then the color of box will be changed each time after it is printed. If the field is placed at the top of the report band, then box will only be effected once at the beginning of the report. Thus the "event model" in reports is based on where an object is placed in relation to other objects.

Sorting Without Groups

More sophisticated examples of using ObjectPAL in reports can be developed using libraries, tCursors, etc. In our next example, we will be using ObjectPAL to sort the records in a tableframe. This can be valuable because the normal method of sorting a report (placing group bands) will cause the tableframe to be broken up into individual tableframes, where each tableframe houses the records belonging to a particular group. Our technique will allow all of the sorted records to be displayed within a single tableframe. There are other ways to accomplish this task, such as sorting the table first and then reporting on the table without grouping. Still, as the task you want to accomplish becomes more complex (let's say you want to sort a detail rather than the master), you will find that the following technique becomes the easiest, if not the only way to solve the problem.

Our task is to generate a report where the table appears sorted in descending index order. To do this we must first define a tCursor that will be visible everywhere on the report. If you open the report's object tree, you will notice that the object called #Report1 is visible to all objects on the form. Thus we will define a tCursor in its Var window:
var
  tc tCursor
endVar
The next thing we have to do is to take care of the opening and initialization of the tCursor. A good place to do this is in the report header, because that code will execute only once, and at the beginning of the report. We will place a calculated field in the header containing the expression:
initTC()
and the custom method:
method initTC() string
  tc.open("Table", "Index")
  tc.end()
  return ""
endmethod
This code will open the tCursor with the appropriate index, and position the tCursor at the bottom of the index. The only other code you will need is in a field that should be placed at the right-most location inside the record object of the tableframe. (Refer to the code disk if you are unclear as to its exact location.) This field will contain the expression:
nextRec()
and the custom method:
method nextRec() string
  tc.skip(-1)
  return ""
endmethod
The code in this field will make sure that after every record is printed, the tCursor will skip to the next (or previous in this case) record to be printed. The only thing left to do is to make the fields being displayed in the table come from the tCursor rather than from the report's data model. To do this, simply change all of the fields within the table frame to calculated fields defined as:
tc."fieldName"
where "fieldName" is the name of the field that you want to have displayed. This last step is critical, or else the tCursor will visit all of the records, but none of the values from the tCursor will be displayed. (The original records in the data model would be displayed in their original order.)

Final Words Regarding ObjectPAL in Reports

As you can see, using ObjectPAL in reports can open the doors to some very interesting functionality. If you choose to experiment with it, make sure that you backup your work regularly, and don't invoke the ObjectPAL debugger. (That's asking for trouble!) I have found several things that don't work in reports, but have only found a couple of crash problems (which all seem to be pretty repeatable). In general, if something works once, it seem to continue to work.


Multitasking, Hard to Call DLLs and Status Messages


Discussion of this article


 Feedback |  Paradox Day |  Who Uses Paradox |  I Use Paradox |  Downloads 


 The information provided on this Web site is not in any way sponsored or endorsed by Corel Corporation.
 Paradox is a registered trademark of Corel Corporation.


 Modified: 15 May 2003
 Terms of Use / Legal Disclaimer


 Copyright © 2001- 2003 Paradox Community. All rights reserved. 
 Company and product names are trademarks or registered trademarks of their respective companies. 
 Authors hold the copyrights to their own works. Please contact the author of any article for details.