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.
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. |
GETFLDSTATE()
makes a lot of sense. It tells you whether a particular field has changed or if the record has been deleted. It also tells you whether a record is new. Most of the conflict resolution strategies we've seen involve using GETFLDSTATE()
to figure out which fields have changed, so you can see if someone else changed the same fields. (Would that human conflict resolution were so simple.)
One warning here: GETFLDSTATE()
doesn't get changed for a field until you've left the control it's bound to. If you make changes but don't leave the field, causing its Valid to fire, the changes don't get sent to the control's ControlSource, and GETFLDSTATE()
doesn't see them. This is consistent with controls not getting their Value changed until Valid is fired. If you need to force the Valid, try This.SetFocus().
* 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.
Buffering, ControlSource, CurVal(), GetNextModified(), OldVal(), TableUpdate(), Valid