Tek-Tips is the largest IT community on the Internet today!

Members share and learn making Tek-Tips Forums the best source of peer-reviewed technical information on the Internet!

  • Congratulations SkipVought on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

Enable / disable "Save" button 2

Status
Not open for further replies.

Eugen Fintina

Programmer
Apr 27, 2005
29
0
1
RO
I have a grid based on a cursor.
I want to enable/disable the "Save" button if the user modified or revert to the modifications regards to any cursor's records.
I tried to use SYS(2017) function but without any success.
 
Sys(2017) wouldn't have anything to do with what you say you want to do.

You probably want GetFldState() and Getnextmodified() instead if you're just trying to determine if a cursor is modified.

Can you clarify your question a bit? It's unclear why you need a checksum of the record to determine if something has changed when VFP will tell you that directly.



 
Rather than having a single button that boths saves and reverts, it would make much more sense to have separate Save and Revert buttons. This makes programming easier and - more importantly - will be easier for the user to understand.

The Revert button merely needs to call TABLEREVERT(). The Save button calls TABLEUPDATE(). This assumes that your cursor is buffered. Both buttons would start out disabled. You would also have an Edit button. When the user clicks Edit, the other two buttons become enabled and the Edit button becomes disabled. When the click the other two buttons, the reverse happens.

Actually, if the grid's underlying data is a cursor rather than a physical table, things are a bit different. You don't need to worry about buffering. The Cancel button would simply close the cursor, so all the edits would disappear. The Save button would copy the contents of the cursor to a physical table. But I can't see any reason for using a cursor in that way.

Mike

__________________________________
Mike Lewis (Edinburgh, Scotland)

Visual FoxPro articles, tips and downloads
 
Hi,
If the very good advise here given by Mike is still not workable for you, you can also consider to add the class wizbtns.txtbtns or the class wizbtns.picbtns (make sure you have all the pictures available) to your form.
The Wizbtns class can be found in the Home()+Wizards directory.
The use of this class will save you lots of coding and errors, just drop either one on your form.
In general the use of the different wizard classes are not recommended, some have serious errors, but this one is a good exception.
And once you are familiar with it and you use the wizbtns.picbtns you can change the pictures to a more modern UI.
Regards,
Koen
 
Mike,

I see a good reason for using a cursor, you can query just a few records to edit and the grid scrollbar is fine, in comparison with a table recordsource and a filter.
The cursor could be a view curor, which means TABLEUPDATE() and TABLEREVERT would work, you'd have the typical connection you also have with a DBF directly. And the grid works fine with a view REQUERY.

Bye, Olaf.

Olaf Doschke Software Engineering
 
I wouldn't even know how that would work. Once you disable a button it has no function. So there is no way a save button becoes a revert button when disabled, it won't make sense anyway. In the situation without changes, you don't have anything to save or revert, in situations with changes you have to save or revert. So there is no way to toggle between save and revert. You either need none of these options or both options.

I think the question was more general about how to see a buffer has changes, GetNextModified(0) will tell you, but will also move there. If the return value is 0, there is no modification, though. There is no event about this, that would trigger the buttons to enabled once a modification starts, simply keep them active anyway, that's the easiest solution. Feedback from saving/reverting data, instead of disabling the save or revert buttons.

If you subclassed all controls an easy way to notice changes is to set a changed flag in any interactive/programmatic change event. in these events you may then also cause the buttons to change their state. You can have a property "ismodified" and create an assign method ismodified_assign. You change event code will always set this .T., your save/revert buttons will set this .F., whatever it is can then also become the state of enabled, set by this assign method, so that assign method does not only store the ismodified state, but sets buttons in the correct model. That's a possible solution. Once I thought triggers would be nice for cursors, but then DBC stored procedures run outside of the scope of the current form, you wouldn't cause forms to adapt to the new situation anyway. It's likely for that reason that was never introduced.

Introducing an edit mode, as Mike also suggested, would also introduce a moment to toggle save and revert button active once edit mode begins and let save or revert end this mode, toggling button states.

Bye, Olaf.

Olaf Doschke Software Engineering
 
Dear friends,

First off all I want to ask you all to excuse me about my silence into last two days. I had a specially event into family (actually was my niece's christening) so I must keep away from my computer.

Second I must tell you that english is not my native language so I am afraid that you didn't understand my problem.
So, I have a form, a grid and a "Save" button on it (initially "Disabled"). In grid I have some columns with CheckBox inside.

The requirements is that when it change the values of one or more checkbox's from one or more records, the state of the "Save" button to be change from "Enabled" to "Disabled" or from "Disabled" to "Enabled".

In other words, if I change the values for Check1 or Check2, the state for "Save" will be "Enabled" and if I change back the values to the initial, the state will be again "Disabled".

So, I mind that for achievement of the requirements, I must calculate a CheckSum for each record and store it. When I click onto one CheckBox, I calculate again the CheckSum and compare it with the initial one.

With my ideas and your help, now, I have the following solution. It works, but I wonder if there is another solution maybe more simple.

In the Init method of the Form:
Code:
lcRecordSource = "Select ... SPACE(32) as CheckSum, .F. as WasModified ";
	+ "FROM ... ";
	+ "INNER Join ... ";
	+ "WHERE ... = ";
	+ "INTO Cursor Temp Readwrite"

Thisform.Grid1.RecordSource = lcRecordSource

If _Tally <> 0
	Select Temp
	Locate
	Do While Not Eof()
		lcCheckSum = Sys(2017, "CheckSum, WasModified", 1, 1)
		Replace Temp.CheckSum With lcCheckSum
		Skip
	Enddo
	Locate
Endif

In the Grid1.Column3.Check1.Click and so on:
Code:
lcCheckSum = Sys(2017, "CheckSum, WasModified", 1, 1)
If lcCheckSum  <> Temp.CheckSum
	Replace Temp.WasModified With .T.
Else
	Replace Temp.WasModified With .F.
Endif

Thisform.Refresh

In the Refresh method of the Form:
Code:
Select WasModified From Temp Where Temp.WasModified = .T. Into Cursor crWasModified

If _Tally = 0
	Thisform.commandgroup1.Save.Enabled = .F.
Else
	Thisform.commandgroup1.Save.Enabled = .T.
Endif

Select Temp
 
Eugen,

First, no need to apologise. It was the weekend, and dealing with VFP issues is not the highest priority.

So, it seems that your problem comes down to this: you need to know if a record has been edited. If so, then calculating the checksum is not the normal way to do that. It is much easier to use the GETNEXTMODIFIED() function. Pass itr zero as the first parameter, and the cursor's alias as the second parameter. If the function returns a value greater than zero, it means the cursor has been edited.

Mike

__________________________________
Mike Lewis (Edinburgh, Scotland)

Visual FoxPro articles, tips and downloads
 
I second Mike if you look into our earlier answers not just GETNEXTMODIFIED but also the concept of buffering changes is mentioned. VFP has its own ways of knowing about the changes, namely the buffer. So it all boils down to using that concept instead of computing checksums.

Of course, that concept works too, but you're reimplementing something already existing.

There's one caveat with the way VFPs own modification detection works, even if you change values back to their old value, that doesn't automatically revert the field state to unmodified. Your checksum mechanism would detect that ignoring the very small risk you can get the same checksum from two changes, checksums or hashes are actually designed to avoid that, but simpler checksums as crc16 only have a low set of checksum values making it possible in the long run.

For that reason alone VFPs own field state mechanism is better, even though it lacks detecting a reverted value, especially that of boolean fields can occur very often.

What you would need to check is [tt]OLDVAL(alias.field)[/tt] vs [tt]alias.field[/tt] value and if that's the same you can use SetFldState to override VFPs evaluation of that as modification. VFP indeed only detects that there was some change and once there was it doesn't automatically revert this to detect that as unchanged in net effect.

Bye, Olaf.

Olaf Doschke Software Engineering
 
A little more detail about the instruments you have at hand, for the extreme cases like two (or more) users editing the same record.

If your user session has loaded some data it memorizes this as OLDVAL() values, another user editing data from the same initial state and already saving his changes earlier can store a value you can read via CURVAL() if the underlying alias is the central DBF file. In case you use the view that's less important to know, as reverting the field state already means VFPs TABLEUPDATE will skip this field when generating its UPDATE SQL statement and not overwrite the already committed change.

But caution, if you store [tt]CURVAL(alias.field)[/tt] into [tt]alias.field[/tt] to load that committed change, that will still mean that field state is changed and saved and causes an update conflict, even though it saves the already saved value. So it's important you just notice no change from OLDVAL(alias.field) to alias.field (which by the way is the buffered changed value). If that's the same but different to CURVAL(alias.field) it will only be saved and revert the saved change, if that field state remains at changed. You can even have a totally different value than OLDVAL() and CURVAL() in the buffer, as soon as you signal to VFP via SETFLDSTATE() this field should be handled as unchanged, it will not be written back.

Overall look into buffering, starting with SET MULTILOCKS ON, CURSORSETPROP("Buffering",...), TABLEUPDATE(), TABLEREVERT(), OLDVAL(), CURVAL(), GETFLDSTATE(), SETFLDSTATE(), GETNEXTMODIFIED() and some more.

Bye, Olaf.

Olaf Doschke Software Engineering
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top