GetFldState(), SetFldState()
This is an interesting pair of functions. GetFldState() is essential to any sensible strategy for dealing with conflicts when saving data. SetFldState() is strange—both in how it's supposed to behave and in how it actually behaves. They're both relevant only when you're dealing with buffered data.
Usage |
uCodes = GetFldState( cField | nFieldCode [, cAlias | nWorkarea ] ) lSuccess = SetFldState( cField | nFieldCode, nCode [, cAlias | nWorkarea ] ) |
Parameter |
Value |
Meaning |
cField |
Character |
The name of a field whose status is being queried or changed. |
nFieldCode |
-1 |
Query the status for all fields in the current record. |
0 |
Query or change the deleted status of the current record. Maybe this is because the deleted mark can sort of be seen as the "zeroth" field of any table. |
|
Positive |
Query or change the nFieldCode-th field in the current record, based on the table's structure. |
|
nCode |
Numeric |
The status to assign the specified field. See Help for a list. |
cAlias |
Character |
The alias for the table whose status is being queried or changed. |
Omitted |
If nWorkArea is also omitted, query or change the table in the current work area. |
|
nWorkArea |
Numeric |
The work area containing the table to be queried or changed. |
Omitted |
If cAlias is also omitted, query or change the table in the current work area. |
|
uCodes |
Numeric |
Contains the status of a single field or the deleted status of the current record. See Help for meaning of status values. 1 – Untouched field |
Character |
Contains a string where the first character is the deleted status for the current record and each subsequent character is the changed status of a field. Fields are represented in definition order. |
|
.NULL. |
Returned by GetFldState() when the record pointer is at end-of-file. |
|
lSuccess |
.T. |
The field's change status or the record's deletion status was changed. |
.F. |
Doesn't occur. |
The behavior of GetFldState() when you add a new record and some fields have default values varies in different VFP versions and based on which command (INSERT or APPEND BLANK) you use to add the new record. In some combinations, the field state for a field default value is 4; in other combinations (all of which involve adding with INSERT), it's 3. While we generally think 4 is the right value, we can see the logic that says that APPEND BLANK actually changes the field, while INSERT doesn't. The key point is that you need to test to see what behavior your combination of VFP version and insertion mechanism produces, so your code can act accordingly. If your framework checks for changes in order to figure out things like whether certain buttons should be enabled or disabled, you may want to use SetFldState() to change the value for such a field to 3. |
The docs say that SetFldState()changes the status of a field or record. That it does. However, what the docs don't say is that your changes matter only when you're dealing with a view. For tables, it doesn't matter how you set the field state. Changed fields will lead to updated records. For views, you can affect what gets saved back to the base table. |
The original release of VFP 7 has a new bug that makes it hard to take advantage of GetFldState() and SetFldState(). In this version, GetFldState() always returns 4 for a field with a default value, even if you explicitly set it to 3. However, internally, VFP honors your change to the field state. (For views, when a field with a default value has field state set to 4, the default value is saved to the base table. When the field has a field state of 3, the view-level default value is not saved to the base table.) The problem this bug causes is that you can't easily determine whether a record has changes or not. This bug is fixed in VFP 7 Service Pack 1. |
That release also has a bug involving GetFldState(). Under certain circumstances, when you add two records in a row, doing TableUpdate() inside a transaction after each, GetFldState() shows the second record as appended (that is, returns 3's rather than 1's). This bug is also fixed in VFP 7 Service Pack 1. |
Example |
* You can prevent an update from failing by checking for * conflicts ahead of time. Loop through all the fields and check * whether other users have changed any that you changed. * This example assumes you're dealing only with existing records * and handles only a single record. Wrap it with a loop * involving GetNextModified() to handle multiple records. FOR nCnt = 1 TO FCOUNT() * Did you change this field? IF GetFldState(nCnt)<>1 * Did anyone else change it? IF CurVal(FIELD(nCnt))<>OldVal(FIELD(nCnt)) * Conflict. You changed it and so did someone else. * Ask the user what to do. nNowWhat = MessageBox("Someone else changed "+ ; FIELD(nCnt) + ", too. Save anyway?", MB_YESNO+MB_ICONEXCLAMATION, ; "Save Conflict") IF nNowWhat = IDNO * Grab the other user's value. REPLACE (FIELD(nCnt)) WITH CurVal(FIELD(nCnt)) ENDIF ENDIF ENDIF ENDFOR * Now you're ready to actually update the thing. |
See Also |
Buffering, ControlSource, CurVal(), GetNextModified(), OldVal(), TableUpdate(), Valid |