tags:

views:

153

answers:

6

Talking .NET WinForms here:

If you have an application where someone can change items using some properties dialog for example, how do you determine if a user has changed something when he clicks on cancel (to decide whether to display a cancel confirmation message box, or not).

I can think of 2 methods:

  1. Defining a dirty bit and handling all "changed" events to set this bit to true.
  2. Clone the original object and write some comparison code to determine if the modified object really was modified compared with the original

Is there any more elegant and/or more generic way to do that?

A: 

My DAL does what you mention in 2. so I can blindly assign all values back to my DAO that it will take care of submitting only the values that were actually changed.

Otávio Décio
I was more talking about, when a user clicks on cancel. I want the dialog to just disappear when nothing was changed but if something has changed I want to get confirmation on the cancel command.
Stefan Koell
Same concept. I can assign the values and ask the DAO if/which values changed.
Otávio Décio
But in my case it's a bunch of objects persisted to XML with serialization...
Stefan Koell
Even so, your objects should be able to tell you if changes occurred or not. And consider what Gamecat said about changing a value and reverting back to the original value.
Otávio Décio
+1  A: 

Both options are valid.

However consider the following: a user changes a value, and then changes it back to the original.

According to the dirty bit method it is changed. But according to the clone and compare method, it is not. So this method is prefered.

Gamecat
If your object is large then doing the comparison might be expensive. Using the dirty bit is a cheap way of seeing whether you need to do the expensive test.
ChrisF
I see, both points are valid and considerable. If nothing better is coming up I prefer option 2.
Stefan Koell
@Chris, you are right, but the alternative is annoying the user with a remark that the data is changed while it isn't. Its always a tradeoff.
Gamecat
Wouldn't the object have to be simply immense before comparison becomes expensive? Since this is a dialog you only need a response in human times which means hundreds of milliseconds. You can do a lot of comparing in 100 milliseconds.
Bryan Oakley
A: 

Actually, in a dialog, clicking cancel should just close the dialog. Clicking OK/Save should then save any changes.

So, both methods are slightly flawed in that they assume you are changing the model before the user clicks OK!

I would do following: Attach a pseudo-model object to the dialog. Let changes be reflected in this object, then scrap it on Cancel or pass it on to your DAL on Save. If you only create the pseudo-model on the first change, why then you have your bit for the cancel confirmation dialog.

Daren Thomas
A: 

If you wish to know if anything changed (uspecific to what exactly changed) then on loading of the form, GetHashCode on your object(s) then on close, call GetHashCode again, and see if the results are different.

you will need to override the GetHashcode function in your objects however:

Public Class Person


    Private _firstName as string = string.empty
    Private _lastName as string = string.empty
    Private _dob as Datetime = Date.MinVal
    private _weight as single = 0.0

    ...

    Public Overrides GetHashCode as Integer

        Dim sb as new System.Text.StringBuilder

        sb.append(_firstName)
        sb.append(_lastName)
        sb.append(_dob)
        ...
        sb.append(_weight)

        Return sb.ToString.GetHashCode

    End Function

End Class

this works well for our objects and DAL :)

HTH

Pondidum
This is interesting. So basically you are suggesting "option 2" and also deliver a way to compare the values. I think I like that and will try to implement your suggestion tonight...
Stefan Koell
It is very possible that a change is not reflected in the hash code. After all, there are only 2^32 possible hash codes, and most objects have more possible states than this.
erikkallen
Hmm, shouldn't a hashcode of an object always return the same value, even if the object has changed ? What if you put your objects in a hashtable ?
Frederik Gheysels
We use guids as our unique keys on our objects, so overriding the has codes is fine. Also as you only ever test if Person1.GetHashCode = Person2.GetHashCode are equal, the likelyhood of a clash is minimal. (we have never had one, and we have over 1 million objects of one type to compare)
Pondidum
A: 

Rethink the design where you want to display a confirmation dialog box. Confirmation boxes are largely useless because users become conditioned to always press "yes, that's what I meant". Instead, if a user cancels after having made a change, provide a mechanism for the user to recover that data. This is particularly important if the dialog in the data is difficult or time consuming for the user to reproduce, not so important if the user could redo their work in a matter of seconds.

To answer your specific question though, I recommend choice (2) where you have a clone of the object. Comparing the original to the new is the surest way to know "has the data changed?". If you use the dirty bit concept, make sure you handle the case where the user changed a value then changed it back. Usually to do that though, you have to keep a copy of the original version anyway so you're back to keeping a copy of the original object. With a copy of the original object in hand it's probably easier to just compare the two objects than to manage tracking a dirty bit.

Bryan Oakley
A: 

Personally I'd go with Option 2. The cost of creating the cloned "original values" object would be minimal, unless you've got a very large number of fields, or unless some contain large data objects (large text blocks, images, etc.).

A key benefit is that you wouldn't get a "false positive" when the user changes a value then changes it back.

Also, this would let you implement a Reset function so the user can discard all of their changes without having to Cancel and restart.

If you don't want to maintain a full "clone", consider just saving old values for the fields that change. If the user changes one back, remove it from your OldValues structure. If you have a lot of fields this would make your Reset faster, since you'd only have to restore the fields that exist in OldValues.

Clayton