Thoughts on expanding n-level undo

Thoughts on expanding n-level undo

Old forum URL: forums.lhotka.net/forums/t/3210.aspx


JonM posted on Monday, July 16, 2007

I've been thinking a lot about how to design my interfaces so they are easier to use.  Most of my applications allow a user to search for an item (a customer, supplier, invoice, and so on) with a variety of different search parameters.  When the results appear the user can sort them by any column and edit the item from the properties screen (accessed by double-clicking on the item).   Nearly all of my property windows contain various text and combo boxes that a user can edit.  The window also contains a 'Save' button, an 'Undo' button, and a 'Close' button.  If the user changes a property and then clicks 'Close' without saving, I prompt them with a 'Are you sure want to discard changes?' dialog.  Here lies the problem.  If a user took the time to change something wouldn't they just expect it to be saved?  It seems like forcing the user to constantly click 'Save' then 'Close' is a real hassle.  Here is the model I want to go to.  I want to remove the 'Save' button off of all my screens.  When a user clicks 'Close' the changes will be automatically changed.  Also, the 'Close' button should not be clickable if the user has made a change that has broken error rules.  Now here comes the tricky part.  I want the undo button to be able to undo let's say the last few sets of changes, but I wan't them to be undoable even if several hours or days have passed.  I want them to be undoable even if the application has been closed.  Basically, I want a way to keep the last few sets of changes to an object stored in the database.  I'm not talking about a complete audit, I don't want to waste tons of space keeping track of everything that has ever happened just the last few changes.  Then, when a user clicks undo I want the last set of changes to come back.  I'm not sure how the undo interface should work, should it just update all the fields with last version or should I implement a common undo box that shows the differences and allows the user to pick which properties they want to revert?  I'm reminded of the 'Previous Versions' tab in Windows Server 2003 and Vista that let's you easily recover old versions of documents.  So basically I'm talking about extending n-level undo so that at least some of its data is saved into the database with the object and that it is retrieved again with the object.   Does this sound doable? or just plain crazy?

ajj3085 replied on Tuesday, July 17, 2007

Well, you can certainly do this, although I don't think it'd integrate with n-level undo, it would be a seperate mechenism.  You need to consider though, do the users really want this, and do they expect your application to work like this?  If they make a change by accident (it happens), but just want to close the window their changes will be saved. 

Also, if the user comes back on a Monday morning, why would they hit Undo to undo the changes made from Thursday?  Would they even remember what their last change to the record was?  I doubt it.

I'd think your best be is to implement this as a kind of audit; that's basically what the Previous versions is.  Every change is saved.  In your case, you'd clear out some older records as changes are made, but you are talking about an audit (with the ability to restore the old version). 

Added this to n-level undo would make Undoable base much more complicated and harder to maintain... plus you don't really need much except to have your insert or updates audited, and a bit of code to restore the previous version.

JonM replied on Tuesday, July 17, 2007

After some more thought I think you are correct. Integrating this into n-level undo would really increase the complexity and maintainability.  Not to mention it would make it hard to upgrade to new CSLA versions.  I already have an audit system written in at the database level.  So here is what I am thinking now.  First, the various textboxes and comboboxes need to indicate that they have been changed since the form has been opened with a different font or background color.  Second, I still want to eliminate the 'Save' button and have the Save functionality automatically run when they press the 'Close' button.  Third, the 'Undo' button should just undo changes since they've open the form.  Finally, the undo button should actually have another smaller button pushed right up against it, it should look like a down arrow or something.  When the user presses this button a common dialog needs to pop up the shows a list of changes (sort of like the previous versions tab) as well as what has changed.  Then the user should be able to select one of them and press Ok.  The dialog should close and all of the old values should be loaded in place of the current values.  Then the user can make any final changes and click Close (saving the changes).   How does this sound?  I really want to eliminate the 'Save' button.  I want users to be able to bounce between screens without constantly having to be hassled with committing the data.

ajj3085 replied on Tuesday, July 17, 2007

Sounds fine, although I don't think I'd get rid of the save button.  You can remove the prompt on closing and autosave, nothing wrong with that.  Just think about the user though that leaves the screen open, to go to a meeting perhaps, and then there's a power failure or crash.  Changes lost.  I would think most users are used to saving before leaving their desktop (I know I always save everything before walking away), and losing the ablitity to do that may upset them.

Just something to think about, I'm not trying to talk you out of this.  Also, is this something your users have requested?  If they like how its currently working, I think you'd be hard pressed to justify changing it.  Again, just some food for thought.

JonM replied on Tuesday, July 17, 2007

No. I have not received specific requests for this solution.  However, while observing users I constantly see them forget to click Save.  They are then confronted with an 'Are you sure?' box.  In this specific application a user will open one window edit only a couple of fields.  Then, close that window and do the same to another.   It just occurred to me that if I could remove the save buttons and just implicitly save everything that it would really cut down the number of clicks required over the course of a work day.  Then I started thinking about how a more advanced undo would be needed because my users make mistakes all the time.  I don't like the idea of keeping the 'Save' button.  I think that it may be difficult to get users to realize they don't need to hit it.  Perhaps I should autosave changes on a regular interval just in-case they leave the window open and walk away for a while.

Justin replied on Tuesday, July 17, 2007

Sounds like 3 distinct problems, here are some ideas based on similar issues we are dealing with:

1. Save and Close as two separate UI actions less effiecent than a single action. As a simple solution in our application we have a "Save & Close" along with "Save" and "Cancel" (X being a "Cancel"). Users are trained and get used to Save&Close pretty quickly and is thier primary choice.

2. Users saving but with errors that should be rolled back. We have this issue as well, and being legal in nature our next verison we are going to most likely move to a fully versioned data model, where by all updates are really copy-inserts and primary keys are an ID and a version number toghether. Yes this impacts space and performance, but for our needs this solves many issues, such as this one and things like concurrent edits and permanent records for linked entities. Think source control, we as developers can't live without it, well our users really need it for thier business data.

3. Possible data loss by leaving the client unattended and have some sort of system failure during that time. Again we have this issue and are looking at different options, perhaps saving a temp file like Word or VS does, and if you re-open the client it sees the temp file and says hey do you want to recover, or perhaps storing these "intermediate" versions in the db as well, similar to shelving it Team Foundation but automatic, yet not fully "checked-in".

 

Justin

tetranz replied on Tuesday, July 17, 2007

For what its worth ... here's what I've done with form close etc.

My edit forms nearly all have four standard buttons.
OK which saves and closes.
Apply which saves but doesn't close.
Cancel which does a CancelEdit on the bindingSource (Undo seems like a better name)
Close which closes the form.

Button enables are set via the bindingsource CurrentItemChanged event.

The Close button simply does this.Close() and then everything else happens in the FormClosing event. That way the Close button and the little X at the top right behave the same.

If the form is closed while BO is dirty, the user gets one of two dialogs triggered by the FormClosing event.
If the BO is valid then it says "Save Changes?  Yes, No Cancel" which does the (I think) obvious things. Yes saves and closes, No doesn't save but still closes, Cancel cancels the form close.

If the BO is not valid then its a bit more messy but says "Data not saved but cannot be saved without further editing. Return to Editing?  Yes, No". Yes is effectively the same as Cancel above and No is the same as No above.

A refinement is that if the form is closed when the BO IsNew but no property has been changed then Close closes without prompt even if the BO is not valid. This assumes that if you hit the "Add New" button (which usually means a new and dirty BO) and immediately try to close then you've changed your mind and don't really want to create a new object. This logic uses a flag which is set false in the BO create new factory method and true on PropertyChanged.

All this adds up to what I see as similar to what MS Word and Excel do if you close a document without saving. Setting the form's CancelButton property to the Close button helps smooths things along too. Someone can open an edit form, hit Escape and then hit Enter which means Yes on the first dialog above.

Cheers
Ross

Justin replied on Tuesday, July 17, 2007

This is a somewhat philisophical point that has been debated and explored in the past with computer systems. Our current computing model is really based on Unix(processes with isolated memory, hirearchal file system, etc). There have been other models that use such things as orthogonal persistence. This is where you really don't have "save", that is the programmer or the user really need not be exposed to such physical constructs as committing data from ram to disk. There where systems that you could yank the plug, and when plugged back in came back to the exact state they were. No real need for a file system you just had objects that once created where persistent until destroyed, ram was just a cache to the real memory your hard disk.

Why should my grandmother have to remeber "save" her document why can't it just be created edited, rolledback, destroyed? But I digress.

With CSLA you will have to implement this orthogonal persistence yourself, such that every property set might have to trigger a update to a persistent store, or perhaps do this in batches on timed intervals if your not worried about each and every property change being committed in lieu of performance. This could be stored in a DB although if it's just short term non roaming then perhaps the local filesystem would be a better place to serialize the objects.

Bottom line current persistent storage is many orders of magnituded slower than ram, and making your objects commit every change to a persistent store can be difficult while still mantaining performance.

Justin

 

Copyright (c) Marimer LLC