Undo/Redo functions

BountyBob
10-20-2004, 06:44 AM
Hi guys,

I have a lookup form which allows a database table to be updated. What I'm trying to implement is an Undo/Redo feature which would allow a user, before the record is saved, to Undo or Redo edits to the current record.

The way I have tried implementing this is by maintaining a collection of Edits. The collection elements (Edits) hold the Control that was edited, the Pre-Update value and the Post-Update value.

When a user enters a control (ie. textbox/listbox) the GotFocus event is called which initialises a collection element with the control name and the pre-update value.

When a user leaves a control, the LostFocus event is called which inserts the post-update value.

An integer pointer is maintained which points to the current collection element. All the while the user is making changes, this points to the last element in the collection.

When a user "undoes" an operation, the pointer replaces the Post-Update value with the Pre-Update value and decrements the pointer.

When a user "redoes" an operation, the pointer replaces the Pre-Update value with the Post-Update value and increments the pointer.

This all works fine until I click on another form. At this point, the Lost_Focus event for the current control is executed, incrementing the pointer. If I keep switching between forms, the pointer is executed each time and screws up my undo collection.

Can anyone suggest a better way of doing this?

Cheers in advance,

Rob Small

Diurnal
10-20-2004, 07:48 AM
You could keep the values in a stack. Each time the entry is changed, add the entry to the stack with the current control index. If the user wants to Undo or Redo the action, the information is in the stack. See the Data Structures (http://www.visualbasicforum.com/showthread.php?t=18746&highlight=stack) tutorial in the knowledge base. You can use this to build a class module to do the work of keeping track of the stack and implement it with a Undo/Redo menu function and/or command buttons. If you hold the values in a form level variable, the data will not be lost if the form loses focus.

BountyBob
10-20-2004, 09:28 AM
Cheers for the response. In essence, that is what I've been doing. The problem I've been experiencing was due to the Lost_Focus event. Every time I flipped between forms, the Lost_Focus event was being executed and screwing up the pointer to the current record and adding a new record to the stack (or collection in this case). I have managed to resolve the problem, though this is almost certainly not perfect. However, as a first-cut version, this is how I've managed it ...

BountyBob
10-22-2004, 05:52 AM
Here's some slightly better undo/redo code...

loquin
10-22-2004, 10:04 AM
When working with ADO recordsets, you may want to think about using a few additional properties of the field.

rs.Field(n).OriginalValue
rs.Field(n).UnderlyingValue

Rob Macdonald, in his Serious ADO book (http://www.xtremevbtalk.com/showpost.php?p=802983&postcount=8), says:
OriginalValue contains the value that the field held prior to any edits within this recordset. UnderlyingValue contains the value currently held in the data source the recordset was built from, including changes made by other users since we opened the recordset. (my italics) In a single-user app, you wouldn't need to worry about the UnderlyingValue property, but in a multiple-user environment, it will probably be important to you to know if someone changed the data before you did...

A third property that may be useful to you is the .DataChanged property of many controls, including the textbox. Whenever you make any changes to the control, this property is set to True, even if you immediately change it back.

A generic undo function might look like this for a multi-user app(pseudo code)
for each fld in rs.fields
If fld.Value <> fld.OriginalValue then
If fld.OriginalValue <> fld.UnderlyingValue then
msgbox "Field: " & fld.Name & " has changed from: " & fld.originalValue & " to: " & fld.UnderlyingValue
' Ask what to do, and do it.
Else
' Update the field
Endif
Else
' Check fld value against fld underlying value.
' If different, ask what to do. & do it.
End If
Next fld

Note that IF you're using a true relational database server, like SQL Server/MSDE or Oracle, you can have triggers in the database to automatically record the time/user who has modified a record, so you could report this data to the user as well, if the underlying data has been changed...

Ref: http://www.visualbasicforum.com/showpost.php?p=873850&postcount=4

BountyBob
10-25-2004, 02:29 AM
Cheers loquin,

That's the first time I've heard about .OriginalValue and .UnderlyingValue. Since I'm writing a multi-user app at the moment I'm sure they'll come in handy (certainly cut down on the number of temporary variables I use).

Rob

EZ Archive Ads Plugin for vBulletin Copyright 2006 Computer Help Forum