Check for state change in objects using serialization?
I have a form that is bind to an object, and when the user trying to leave the form, I want to warn them if anything on the form has been changed, and prompt them to save. My question is, is there any wa开发者_JS百科y to achieve this without implementing IComparar for all my classes in the binded object? I was thinking if there is a way I can serialize my object when loading the form, and do a simple comparison against the change object that also get serialized. Something like a string comparison.
Hope that make sense,
Thanks
If you follow the regular pattern that people use when implementing INotifyPropertyChanged, then all it takes is another few lines of code to implement an IsDirty
(or IsChanged) flag on the data object.
First of all, create a base class that implements the basics, and a real data class that derives from it:
public class BaseDataObject : INotifyPropertyChanged
{
public bool IsDirty
{
get { return _isDirty; }
protected set { _isDirty = value; }
}
protected void OnPropertyChanged(string propertyName)
{
_isDirty = true;
if (this.PropertyChanged != null)
this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
#region INotifyPropertyChanged Members
public event PropertyChangedEventHandler PropertyChanged;
#endregion
private bool _isDirty;
}
public class MyRealDataObject : BaseDataObject
{
public int MyIntProperty
{
get { return _myIntProperty; }
set
{
bool changed = _myIntProperty != value;
if (changed)
{
_myIntProperty = value;
OnPropertyChanged("MyIntProperty");
}
}
}
private int _myIntProperty;
}
Now whenever a notifiable property changes, the data object's IsDirty
flag will be set to true. To check for changes, all you have to do is enumerate your collection of objects and check the IsDirty flag. If you are clever you can do it with a LINQ statement:
bool changesMade = MyDataObjectCollection.Any(p => p.IsDirty == true).Count() > 0;
... or ....
bool changesMade = MyDataObjectCollection.Count(p => p.IsDirty == true) > 0;
If your data objects are not in a nice collection, you will just have to iterate them manually. Of course the above pattern could be refactored a few different ways (hey, maybe you aren't even using INotifyPropertyChanged because you aren't data binding to UI elements), but it gives you a good starting example of how to go about implementing what you want.
This was asked before, in the form of comparing 2 object trees through serialization.
The answer was: it is not reliable, there are counter examples of equal objects generating different serialized text/data.
To do what you're trying to do, there is probably a much simpler method: Use a "modified" flag or event.
You can cascade this up many levels of user controls if need be. Just declare the event like this:
public class MyControl : Control
{
public MyControl()
{
InitializeComponent();
textBox1.TextChanged += BubbleModified;
// etc.
}
protected void BubbleModified(object sender, EventArgs e)
{
OnModified(e);
}
protected void OnModified(EventArgs e)
{
var handler = Modified;
if (handler != null)
handler(this, e);
}
[Category("Behavior")]
[Description("Occurs when data on the control is modified.")]
public event EventHandler Modified;
}
Then, at whatever level you need to actually check for modifications, hook all of the events.
public class MainForm : Form
{
private bool isDataModified;
public MainForm()
{
InitializeComponent();
textBox1.TextChanged += DataModified;
textBox2.TextChanged += DataModified;
// etc.
userControl1.Modified += DataModified;
userControl2.Modified += DataModified;
// etc.
}
private void DataModified(object sender, EventArgs e)
{
isDataModified = true;
}
}
Then all you have to do is check (and reset) the isDataModified
flag accordingly.
It's really not a lot of work, certainly easier than ensuring that INotifyPropertyChanged
is implemented for every object in the graph. Remember, this is a form, you don't really care that the object changed, you care if the user made a change, and for that, you want to actually check for changes made through the controls.
Yeah, it's not perfect - you still run into the minor nuisance of reporting that data was changed even when the user changes something and then changes it back. But I don't think I've ever actually heard a complaint about this, and using serialization as a comparison method just isn't reliable. What you really need to do if you want to eliminate the redundant save confirmation is override the Equals
method of every object in the graph and implement an actual value-equality operation. Or, if you don't want to retain a copy of the old graph, use a checksum-generating function (collisions are possible but highly unlikely).
But I would just stick with the flag. Don't try to cheat your way out of writing equality checks. It's actually sort of the same as trying to write an automatic deep-cloning method; you can try, but anything you come up with is going to be broken sometimes.
精彩评论