Thank's for your answer. I really don't waste the time to implement
the IEditableObject in all my classes. The BindingList takes care of
call the CancelEdit internal when ESC is pressed, but since I move to
another control, even to the Cancel button used in the form, the
BindingList call the EndEdit internal and I can't undo the properties
changed to the original value. I think I have to extend the
BindingSource control to make a backup of the current item and if I
cancel the changes restore the original values from that backup. I
think this approach would resolve my problem but I have some
difficulties to restore the data into the bounds controls. Any help or
example would be very appreciate.
[]'s
BD
Problem solved with FieldInfo class.
[]'s
BD
What do you mean? I had to implement IEditable and INotifyPropertyChanged
to capture the changes to the object and be able to roll them back.
Robin S.
I did not intend to implement that in all my objects and I made two
generic function to backup and restore the data using Stack<byte[]>,
HybridDictionary, FieldInfo[], MemoryStream and BinaryFormatter.
private Stack<byte[]> _backup;
private void BackupData()
{
if (_backup == null)
{
_backup = new Stack<byte[]>();
object source = BindingSource.Current;
Type sourceType = source.GetType();
HybridDictionary state = new HybridDictionary();
FieldInfo[] fields;
do
{
// get the list of fields in this type
fields = sourceType.GetFields(
BindingFlags.NonPublic |
BindingFlags.Instance |
BindingFlags.Public);
foreach (FieldInfo field in fields)
{
// make sure we process only our variables
if (field.DeclaringType == sourceType)
{
object value = field.GetValue(source);
if(sourceType.IsAssignableFrom(field.FieldType))
{
// make sure the variable has a value
if (value == null)
{
// variable has no value - store that fact
state.Add(field.DeclaringType.Name + "!" +
field.Name, null);
}
else
{
// this is a child object, cascade
the call
((this)value).BackupData();
}
}
else
{
// this is a normal field, simply trap the value
state.Add(field.DeclaringType.Name +
"!" + field.Name, value);
}
}
}
sourceType = sourceType.BaseType;
} while (sourceType != null);
// serialize the state and stack it
using (MemoryStream buffer = new MemoryStream())
{
BinaryFormatter formatter = new BinaryFormatter();
formatter.Serialize(buffer, state);
_backup.Push(buffer.ToArray());
}
}
}
private void RestoreData()
{
// if we are a child object we might be asked to
// undo below the level where stacked states,
// so just do nothing in that case
if (_backup != null)
{
HybridDictionary state;
using (MemoryStream buffer = new
MemoryStream(_backup.Pop()))
{
buffer.Position = 0;
BinaryFormatter formatter = new BinaryFormatter();
state =
(HybridDictionary)formatter.Deserialize(buffer);
}
object source = BindingSource.Current;
Type sourceType = source.GetType();
FieldInfo[] fields;
do
{
// get the list of fields in this type
fields = sourceType.GetFields(
BindingFlags.NonPublic |
BindingFlags.Instance |
BindingFlags.Public);
foreach (FieldInfo field in fields)
{
// make sure we process only our variables
if (field.DeclaringType == sourceType)
{
// the field is undoable, so restore its
value
object value = field.GetValue(source);
if(sourceType.IsAssignableFrom(field.FieldType))
{
// this is a child object
// see if the previous value was empty
if(state.Contains(field.DeclaringType.Name + "!" + field.Name))
{
// previous value was empty -
restore to empty
field.SetValue(source, null);
}
else
{
// make sure the variable has a
value
if (value != null)
{
// this is a child object,
cascade the call.
((this)value).RestoreData();
}
}
}
else
{
// this is a regular field, restore
its value
field.SetValue(source,
state[field.DeclaringType.Name + "!" + field.Name]);
}
}
}
sourceType = sourceType.BaseType;
} while (sourceType != null);
BindingSource.ResetCurrentItem();
_backup = null;
}
}
[]'s
BD