Back

Originally published in FoxTALK March 1999


 

Whatís the current work area?

Visual FoxPro has 32,767 available work areasÖ for each data session.  Since every form or formset, toolbar, and report can have its own data session, the total number of work areas available is astronomical.  Add into this object orientation with classes and event driven interfaces where the developer has very little control over what code runs and when, and you have the makings of a nightmare. How can you keep track of the current work area in this scenario?

Never depend on which work area is current

Whether you are designing a class and writing code in one of its event-methods or methods, or you are writing code in an event-method or method of an object in a form, do not depend on any particular work area being currently selected. Always, explicitly, select the work area your code needs.  For example, if in the click of a command button you need to move the record pointer in the Customer table you would write the code this way;

SELECT Customer

IF NOT EOF()

  SKIP

ELSE

  GO BOTTOM

ENDIF

This code makes no assumptions as to what work area is current.  Why is this important? Hereís an expanded scenario.  You designed this form to be the customer edit form and the only controls in it were some textboxes for editing the fields in the customer table. The buttonís click code had no explicit select in it because there was only one table in the formís data environment.

However, and users are great at creating howevers, the user later asked that the same form be used to display the contacts at a customer.  You add a grid to the form to display records from the contacts table.  Then the trouble started.  If the user clicked the button from one of the textboxes everything was fine, however if they clicked the button while focus was in the grid the record pointer was being moved in the contacts table instead of the customer table.

By using the explicit selection technique above this problem never occurs.

Always put things back when you are done

The code example above is fine for insuring that the button moves in the correct work area, but what about all of the other events that may fire?  Donít they also need to occur in a known environment?  Yes they do.

There is a simple maxim that is extremely important here, never assume anything about the environment in which your code will execute and always put the environment back to what you found when the code started.  The code example above would be expanded to the example below to accomplish this.

LOCAL lcAlias

lcAlias = ALIAS()

SELECT Customer

IF NOT EOF()

  SKIP

ELSE

  GO BOTTOM

ENDIF

IF EMPTY( lcAlias )

  SELECT 0

ELSE

  SELECT ( lcAlias )

ENDIF

What extra work is this code doing? First it declares a local variable, lcAlias.  Why local?  Because there is always a possibility that this code may call other code that is also using the same variable and we donít want that variable to get stepped on.

Next the code saves the name of the currently selected work area in this local variable.  At the end the code restores the current work area to what it was when the code started.

What about the other issues of the data environment?

Ok, so the code can handle the current work area issue, but what about other things?  What if some code needs a specific index order set or it needs the record pointer moved temporarily?

These things can also be handled through the same approach.  The code below shows the process of saving record pointer, index order, and work area and restoring them at the end.

LOCAL lcAlias, lcOrder, lnRecno

* Save work area

lcAlias = ALIAS()

SELECT Customer

* Save current index order

lcOrder = ORDER()

SET ORDER TO Name

* Save current record number

lnRecno = RECNO()

SEEK ďJonesĒ

 

* Do some stuff

 

* Now restore the settings

* Restore the index order for Customer

IF EMPTY( lcOrder )

  SET ORDER TO 0

ELSE

  SET ORDER TO &lcOrder

ENDIF

* Restore record pointer

IF lnRecNo <> 0 AND NOT lnRecNo > RECCOUNT()

  GOTO lnRecNo

ENDIF

* Restore work area

IF EMPTY( lcAlias )

  SELECT 0

ELSE

  SELECT ( lcAlias )

ENDIF

Whatís the point of this?

By coding every method, event-method, and procedure this way you will never again care about what the current environment is. Any piece of code will create the environment it requires and restore the environment it found when it started.  This approach will make your coding much easier in that it will not be effected by the sequence of events at runtime. Subroutines will not be dependent on what called them and calling routines will not be effected by what the called routines do to the environment.

Thatís all well and good, but what does this have to do with how Visual FoxPro works inside? Visual FoxPro is an event driven system.  Things happen and code executes based on those things happening.  Users interact with controls and forms and their interaction fires event-methods. There is really no good way for a developer to know exactly what environment will exist when a particular piece of code executes. By designing your event-methods, methods, and other code this way you remove the data environment from being an issue.

These techniques can be extended to other issues like the SET commands.  The SET() function allows you to derive the current setting for these commands which you can save to a variable and restore after your code has changed the setting and finished its work.