This is with CSLA 3.0.4:
I have a BusinessListBase with 6 objects. My "Undo" routine looks like this and seems to be what is required. It unbinds the object, calls CancelEdit, then BeginEdit to commence editing again, and then rebinds the object.
public static void UndoChanges(System.Windows.Forms.BindingSource source, Csla.Core.ISupportUndo bo)
{
try
{
source.RaiseListChangedEvents = false;
source.DataSource = null; // Unbind
bo.CancelEdit();
bo.BeginEdit();
source.DataSource = bo;
}
finally
{
source.RaiseListChangedEvents = true;
}
}
This works great the first time I call this routine, but when I call it the second time (even when making no changes), the EditLevel is wrong for the first entry in the list (shown in bold below). I'm stumped. Any thoughts on how to troubleshoot this? I could just disable IEditableObject, but I'd rather make it work.
I'm not doing anything here but clicking on my Undo button twice, so I'm baffled as to what is incrementing the EditLevel of the first object in my list (especially when it was correct after the initial binding).
Edit Level before undo call #1
Depth 0, Edit Level is 1 for List UserTableInfoList[ of 6]
Depth 1, Edit Level is 1 for UserTableInfo[0]
Depth 1, Edit Level is 1 for UserTableInfo[1]
Depth 1, Edit Level is 1 for UserTableInfo[2]
Depth 1, Edit Level is 1 for UserTableInfo[3]
Depth 1, Edit Level is 1 for UserTableInfo[4]
Depth 1, Edit Level is 1 for UserTableInfo[5]
Edit Level after undo call #1
Depth 0, Edit Level is 1 for List UserTableInfoList[ of 6]
Depth 1, Edit Level is 1 for UserTableInfo[0]
Depth 1, Edit Level is 1 for UserTableInfo[1]
Depth 1, Edit Level is 1 for UserTableInfo[2]
Depth 1, Edit Level is 1 for UserTableInfo[3]
Depth 1, Edit Level is 1 for UserTableInfo[4]
Depth 1, Edit Level is 1 for UserTableInfo[5]
Edit Level before undo #2
Depth 0, Edit Level is 1 for List UserTableInfoList[ of 6]
Depth 1, Edit Level is 2 for UserTableInfo[0]
Depth 1, Edit Level is 1 for UserTableInfo[1]
Depth 1, Edit Level is 1 for UserTableInfo[2]
Depth 1, Edit Level is 1 for UserTableInfo[3]
Depth 1, Edit Level is 1 for UserTableInfo[4]
Depth 1, Edit Level is 1 for UserTableInfo[5]
Ah, that explains why I could make this work by inserting this call before unbinding, which I suppose indirectly does exactly what you describe:
source.CurrencyManager.CancelCurrentEdit();
I reviewed ProjectEdit.cs, and there really do appear to two calls to CancelEdit -- one on the interface and a direct call (which answered my other question -- do you need to call CancelEdit on both the interface and the object itself).
Uh oh. :(
So, I integrated the code from UnbindBindingSource() into my binding helper class, but I find when I call it in my use case above that the .Current property is null (or even throws an exception in some cases), so I can't call IEditableObject.CancelEdit(). However, if I use my call to the CurrencyManager above as a fallback in the event the Current can't be retrieved, it works.
public static class CslaBindingHelper
{
static void UnbindBindingSource(BindingSource source, bool apply, bool isRoot)
{
System.ComponentModel.IEditableObject current = null;
try
{
current = source.DataSource as System.ComponentModel.IEditableObject;
}
catch (Exception)
{
current = null; // Ignore
}
// Workaround:
// If can't retrieve the current, use Currency Manager to be sure edit applied or cancelled?
if (current == null)
{
if (apply)
source.CurrencyManager.EndCurrentEdit();
else
source.CurrencyManager.CancelCurrentEdit();
}
if (isRoot)
source.DataSource = null;
if (current != null)
{
if (apply)
current.EndEdit();
else
current.CancelEdit();
}
}
Copyright (c) Marimer LLC