EditLevel mismatch - can't figure this one out?

EditLevel mismatch - can't figure this one out?

Old forum URL: forums.lhotka.net/forums/t/5192.aspx


rsbaker0 posted on Monday, August 04, 2008

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]

RockfordLhotka replied on Tuesday, August 05, 2008

You should follow the pattern from PTWin. You are close, but you aren't calling CancelEdit() on the IEditableObject interface after you unbind from the bindingsource. That is NOT the same as calling CancelEdit() on the object directly - you really need to use the interface for that one call to complete the unbinding.

rsbaker0 replied on Tuesday, August 05, 2008

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).

 

rsbaker0 replied on Tuesday, August 05, 2008

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