My framework has the ability to "check each record", which means go to the record that is supposedly modified and use GetFldState to see if it's truly modified (I wish I could remember why I added this feature - I think it was just a way for me to check which field got unintentionally modified in order to correct a bug).This is what I had to say on the subject in a conference session that I did called "It Seemed Like a Good Idea at the Time":
When SetFldState() Doesn’t Work as Expected (Example: SetFldStateProblem.scx and FixFldStateProblem.scx)In the previous section, I discussed how using default values can be the source of problems when using views. They can also be the source of some minor irritation to our end users if our application does not handle them properly. Take, for example, the case where the user clicks on the “Add” button in a form and then changes his mind. He may decide to navigate to a new record or to close the form. If the underlying data uses default values, and you have a routine that checks for pending changes in order to prompt the user to save them, this routine will see the newly appended record as having changes and display the prompt. This is, understandably, confusing because the user did not make any changes. This is also a problem any time we force a bound combo box to display a specific value with a command like this:
Thisform.cboCountries.ListIndex = 1
The problem is that this makes a change to the underlying data and could then trigger a ‘do you want to save changes’ message. So what can be done about it? Fortunately, we can use SETFLDSTATE() to reset the field state flag so that the changes made by inserting default values are no longer seen as changes. Or can we?
Suppose our form has a data entry grid on it and we want to either commit or discard all of the changes made to the data in the grid. We could put code like this in our “Add” method to ensure that the default value inserted for the primary key is not later seen as a change:
APPEND BLANK IN Clients
SETFLDSTATE( 'iClientPK', 3, 'Clients' )
We could then use code like this in our method that checks for pending changes and prompt the user to save if any are found:
LOCAL lnModified, lnAnswer
lnModified = GETNEXTMODIFIED( 0, 'Clients' )
IF lnModified # 0
GO lnModified IN Clients
lnAnswer = MESSAGEBOX( 'Do You Want to Save Your Changes?', 3, 'Pending Changes' )
IF lnAnswer = 6
Thisform.DoSave()
ELSE
IF lnAnswer = 7
TABLEREVERT( .F., 'Clients' )
ELSE
RETURN .F.
ENDIF
ENDIF
ELSE
TABLEREVERT( .F., 'Clients' )
ENDIF
There is just one small technical problem here. You cannot use GETNEXTMODIFIED() to get the job done because it still thinks that record has pending changes! You can prove this to yourself by checking for
GETFLDSTATE( -1, ‘Clients’ )
Immediately after the line
GO lnModified IN Clients
GETFLDSTATE() returns a string of 3’s.
So, if you have a process that needs to find all of the pending changes in a table or a cursor, and you have used SETFLDSTATE() to reset the field state flags on some records that you do not want to process, you cannot rely on GETNEXTMODFIED() to do the job. Instead, you have to scan the table and use GETFLDSTATE() to check each record for pending changes.