772 ObjectPAL: How to reference fields in tableframes and MRO objects
VERSIONS: Paradox 7 and later

SUBJECT: How to reference fields in tableframes and MRO objects.

BY: BERTIL ISBERG

Two topics are covered in this document.
  • Where to put the code
  • How to reference fields in a tableFrame / MRO.
A general recommendation for where to place your code is: as close to the object using the code as possible. This seems rather elementary for simple objects as buttons, but what about a tableframe containing a header and a record object which contains fields? Below, I'll show an example. The example will also cover how to reference the field objects in a tableframe. Something I never had thought very much about as the field objects get unique names by Paradox.

Let's set up a form based on Customer and Orders in the Sample directory. Tables are linked as 1-M from Customer to Orders giving a single record for Customer and a detail tableframe for Orders. Let the tableframe show 6 or 7 records at a time.

All I want to do is: When the user press the F7 button, I want to scroll to last record in the restricted view of the detail table and tell me Order No. (The basic problem was to find specific values in a column, but I have simplified the code.) To call another method, doing exactly the same, but bound to the tableframe, F6 is to be used.

Add this code to the Total Invoice field in the Orders' tableFrame.
method keyPhysical(var eventInfo KeyEvent)
var
   stKp string
endvar
stkp=eventInfo.vchar()
switch
   case stKp="VK_F7" :
      disableDefault
      uDoitField()
   case stKp="VK_F6" :
      disableDefault
      uDoitTableFrame()
endSwitch
endMethod
Method uDoitField() is bound to Total Invoice
method uDoitField()
; Loop to last record in the restricted view and 
; tell us the Order No of last record.

active.home()
while True
      if not active.nextRecord() then
         quitloop
      endif
endWhile
msgInfo(Order_No.string(),"")
endMethod
Method uDoitTableFrame() is bound the Orders table frame. The code is identical to uDoitField(), so I won't show it.

Run the form (after saving and compiling it). Move to Total Invoice and press F7 and F6 and watch the result. You may select a customer with more orders than what's shown in the tableFrame to see what I mean. First customer should do this if you have 6-7 records in the tableframe.

You could expect those two methods to show the same Order No, but they don't. Code bound to the field object is incorrect. I won't try to explain the Order No presented when code is bound to the field. There is a pattern, but it's irrelevant.

In this case when the code is scrolling the table, code should be bound to the tableframe.

If you want to have the code in the field, the Order No should be referenced as Orders.Order_No. Now you get the same result from both methods.

Let's take a closer look at what could happen before we change the reference, though. Let's say we want to do something inside the loop.

Add two lines after endif to log each Order No visited.
while True
      if not active.nextRecord() then
         quitloop
      endif
      ; Log the Order No to a txt file	
      ; Log the Order No to a txt file	
      tsLog.writeline(Order_No.string())
      tsLog.commit()
endWhile
You also have to add code for opening and closing the tsLog textstream file

Form level Var window
var
   tsLog textstream
endvar
Page open
method open(var eventInfo Event)
  tslog.open("log.txt","nw")
endMethod
Page close
method close(var eventInfo Event)
  tslog.close()
endMethod
Run the form, but expect to get into a never ending loop. Use Ctrl+Break to break out of it.

active.nextRecord() is revoked by the Order_No reference!

Here's an excerpt from the log file:
1001
1001
1001
1001
1001
....
1001

Note this problem does only occur when code is bound to the field object. Changing the Order_No reference to Orders.Order_No will also remove the problem.

Trying to summarize:
Bind code for scrolling tableframes and MROs to the tableFrame / MRO, not to fields contained in the record object.

Consider using tableframe.fieldname as a default when referencing fields bound to a containing object like tableframe / MRO.


COMMENTS BY : LANCE LEONARD

Each record object in a table frame or MRO is a discrete, distinct object at run time, whether named or not. When you refer to an object with a name that does not refer to the complete containership path, Paradox has to resolve that name with respect to the scope of the current object. In this case, Paradox is resolving the msgInfo() call with respect to the third record object in the table frame, the one that was active when you pressed [F1].

Why is this important? Well, Total_Invoice doesn't contain any objects called Order_No. However, it's container (the record object). Because ObjectPAL lets you make "lateral" object references inside a table frame, you have to make sure that your code refers to the correct record object.

So why does the code work "correctly" when you simply move it to the table frame? Again, this stems from Paradox having to figure out where Order_No is. When your custom method is triggered from the table frame, self refers to the TableFrame.

Because self is not a specific record object, Paradox then assumes you mean the active record object, so it grabs the Order_No from that record--providing the desired correct value.

When self is the Total_Invoice field object of the third record object, you get the Order_No value from the thrid record object, whether or not it's active. That's just the way Paradox determines exactly _which_ Order_No field value you want.

If, for some reason, you absolutely _must_ take the UIObject navigation approach, then be sure to pay particular attention to the scoping you're working with. Remember that there isn't one record object at run time, there are as many as you've got rows. Also, remember that there isn't a single set of field objects, there one set for each record object. Thus, you need to make sure that you're referring to the field object you really need. If you use custom methods to perform this stuff, they're probably best defined at the table frame (or MRO) level because that provides the most flexibility in the implied object references.

To index