views:

603

answers:

4

I have a BindingSource that I'm using in winforms data binding and I'd like to have some sort of prompt for when the user attempts to close the form after they've made changes to the data. A sort of "Are you sure you want to exit without saving changes?"

I'm aware that I can do this via the BindingSource's CurrencyManager.ItemChanged event by just flipping a "has changed" boolean.

However, I want a more robust functionality. I'd like to know when the current data is different from the original data. The event just tells me if somethings changed. A user could still change a property, hit undo, and I would still think that there is a change in the data to save.

I want to mimic this similar functionality of notepad

  • open notepad
  • type something
  • delete everything (essentially undoing what you did)
  • close notepad, notepad closes, no prompt to save changes because it knows the end state == the initial state

If this is not possible, then should I go with the ItemChanged event handler as outlined above or is there a better way?

For the record, I'm looking for something along the lines of

bool HasChanged()
{
    return this.currentState != this.initialState;
}

not this

bool HasChanged()
{
    // this._hasChanged is set to true via event handlers
    return this._hasChanged;
}

I'd just rather not have to manage the current state and the initial state myself, I'm looking for a way to grab that info from the BindingSource If I can get this functionality from the BindingSource its way more ideal since I will be able to use the functionality on many different data sources, regardless of type, etc.

+1  A: 

Instead of flipping a bit, you could check the state against a snapshot of your initial state.

Brett Veenstra
yes yes yes, this is what i want to do, but how do i do it? lol I dont want to do it manually, surely the `currencymanager` or the `bindingsource` has this built in. That or maybe it keeps track of the initial and current states
Allen
+1  A: 

You'll have to implement the INotifyPropertyChanged interface from within your object classes, then catch whenever a change occurs through proper event handlers for your type class within your DataSource BindingSource property.

The one object offering what you require is the DataSet, containing both the Original and Current (changed) state of an persistent entity. Then, when one cancels, all you need to call is the Rollback() method. When one accepts the changes, then a call to the AcceptChanges() method will do.

Besides an the DataSet, perhaps considering an ORM like NHibernate will do the job for you, plus allowing you to use custom defined objects, instead of a DataSet. Keeping the ISession API alive while in your form will allow the ISession to keep track of your changes whatever it may be to whatever object it is, as long as it is know by NHibernate.

Another solution implementing the INotifyPropertyChanged interface, is at the property setter, you could stock the Original value within a private field or for every property of an object. You could simple have an abstract class with the HasChanges property return whether each property is as its Original state, then return true or false accordingly.

I have a question regarding our interesting initial discussion. I just want to make sure of one thing. Let's call it language barrier if we like. But publishing the PropertyChanged event through the INotifyPropertyChanged interface will also somehow "rollback" an object to its original state. The only detail you had to take care is that if the user says he doesn't want to keep the changes, then reload the this CurrentItem from the underlying database via the BackgroundWorker class and its done! No lagging from your GUI, your user has canceled the changes, and you resetted the object to its default/original state!

Well, I guess here's enough details to make yourself an idea, plus all of the other good answers provided by the others. I am confident you will find your way to accomplish what you want.

Best of success! =)

Will Marcouiller
In what way is this different than flipping a bit with CurrencyManager.ItemChanged? It doesnt compare the origional state with the current state
Allen
Your object only should know whether it has changed or not. Implementing it directly into your object, you won't bother playing around with the CurrencyManager. Anyway, I don't get really what Brett means by "Instead of flipping a bit", though I get the idea of comparing the snapshot against the initial state. In fact, the real question is, do you want to be able to compare if whether there was a change compared to your initial state, or do you want to publish there was a change, so you may validate whether the user wants to save changes or not while closing the form?
Will Marcouiller
Brett means that instead of doing `bool hasChanged() { return this._hasChanged; }`, I would do `bool hasChanged() { return currentValues != initialValues; }`
Allen
Thanks for enlightening my lantern. =) This might be a better approach for your situation, I can't say, only you know. Despite, I use the approach suggested regularly and works fine. Doing Brett's way means you have to have two sets of your objects in memory. But that is fine, really! =)
Will Marcouiller
@Will, I've read elsewhere that the BindingSource already does this, which is why I'm hesitant to go in and implement it myself. I'd like to know where / how it keeps the current and initial states, so I can compare those, rather than keeping track of it myself
Allen
@Allen: I guess you'll have to clone the CurrentItem to be edited into another DataSource, then compare the changes against your primary object from the initial DataSource. Otherwise, I think CurrencyManager.CurrentItemChanged can do it, even though this is something you would like to avoid. I'm sorry, I can't help more than that even if I would have liked to. I don't get the point of really comparing between an initial and current object state. If a property publishes that it has changed, than its older value is already overwritten. For the behaviour of FormClosing, PropertyChanged is fine.
Will Marcouiller
@Allen: I hope this won't get too hard to maintain for the next newbie programmer that will come in and work with it. You must know what you're doing, for sure! =)
Will Marcouiller
@will, the point of all this is explained in the question, I think we're running into a language barrier. What if they change the value and then undo their change. At that point, you will think that they have changed something when they really haven't. Unfortunately, its looking like it will be impossible to accomplish this without alot of type specific code, which I was trying to avoid and wont be interested in implementing.
Allen
@Allen: You may be right at some point about the language barrier. =) See my EDIT, please.
Will Marcouiller
+1  A: 

When you open your detail, you could make a clone of the entity that you are going to modify.

Then, when the user attempts to close the form, you could compare the clone (the entity in its original state) with the modified (or not) entity. If the clone and the entity are not equals, you could prompt the user.

Javier Morillo
I was under the opinion that the CurrencyManager or the BindingSource did this already, internally.
Allen
Aha, yeah..., it has all the sense. I´ll wait for an answer too your question, too.
Javier Morillo
+1  A: 

Will is right, you should implement INotifyPropertyChanged, ideally in conjunction with IDataInfoError to get visisble information for your users.

For your Objects to get a state and a notification on Editing, try using the IEditableObject interface.

All three interfaces are used by default from WinForms and help make the programmers life easier.

BeowulfOF
I may be missing something, but like I said, in what way is this different than the event I outlined in my question. Its STILL just telling me when a user had made a change, its not comparing the initial state to current state.
Allen
+1 Thanks for this BeowulfOF
Will Marcouiller
@Allen: It just depends on your implementation if it holds an initial state internally. The reference implementation for IEditableObject shows a clean way to make your objects editable and undoable.
BeowulfOF