Problem: Using EditableRootListBase with WPF IEditableCollectionView

Problem: Using EditableRootListBase with WPF IEditableCollectionView

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


cclark posted on Thursday, October 16, 2008

Symptom
When using an EditableRootListBase (ERLB) with the .net 3.5 SP1 IEditableCollectionView, calling SaveItem() to save an item in the collection causes the item in the collection to be replaced with the result of the save, however System.Windows.Data.BindingListCollectionView keeps a cached reference to the item before the save operation. Subsequent uses of the IEditableCollectionView then point to the incorrect object.

Description:
The call to SaveItem on the ERLB causes a BindingList<>.ListChanged even to be raised with the ItemChanged flag. The IEditableCollectionCiew handles this event with the following code (from reflector since they STILL don't have 3.5 SP1 up on source servers) from private void OnListChanged:

case ListChangedType.ItemChanged:
            if (!this._itemsRaisePropertyChanged.HasValue)
            {
                changedItem = this.InternalList[args.NewIndex];
                this._itemsRaisePropertyChanged = new bool?(changedItem is INotifyPropertyChanged);
            }
            if (this._itemsRaisePropertyChanged.Value)
            {
                goto Label_032D;
            }
            goto Label_02E9;

        default:
            goto Label_032D;
    }
    args2 = new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, changedItem, newIndex + num);
    goto Label_032D;
Label_02E9:
    if (this.IsEditingItem)
    {
        this.ImplicitlyCancelEdit();
    }
    if (this.IsAddingNew)
    {
        this._newItemIndex = this.InternalList.IndexOf(this._newItem);
        if (this._newItemIndex < 0)
        {
            this.EndAddNew(true);
        }
    }
    base.RefreshOrDefer();
Label_032D:
    if (args2 != null)
    {
        base.OnCollectionChanged(sender, args2);
    }

Other flags actually cause the internal cache to be updated, such as ItemMoved:

 this._cachedList.RemoveAt(args.OldIndex);
 this._cachedList.Insert(args.NewIndex, changedItem);

Ergo, the cache is NOT updated when the SaveItem is called on the ERLB, and references to CurrentItem, etc. are pointing to the incorrect item.

Let me know if there is something I am missing, or how to work around this. Thanks in advance!

RockfordLhotka replied on Friday, October 17, 2008

It sounds like they may have a bug then? It isn't clear what ERLB could do differently - though if you have some idea (that wouldn't break everything that works now) I am interested to hear.

vschuhma replied on Friday, October 17, 2008

Hi,

this sounds like the issue I am having with the new DataGrid. I think it uses a IEditableCollectionView in the background. I also seem to get the same object back from the cache when updating a cell  a 2nd time. Just saw this post after firing off  a question  on the DataGrid and ERLB.

Volker

cclark replied on Friday, October 17, 2008

Please see the post in this thread for a possible partial fix for the issue.

http://forums.lhotka.net/forums/27348/ShowThread.aspx#27348

markell replied on Tuesday, April 14, 2009

I have exactly the same problem.
I am using ERLB as the ItemsSource for a TreeViewItem control.
When an item is saved it is properly replaced within ERLB, which triggers the ListChanged event with the ListChangedType.ItemChanged type.
This event is then caught by the BindingListCollectionView instance wrapping the ERLB.
Inspecting the code of BindingListCollectionView.OnListChanged method in Reflector reveals interesting implementation detail - it swallows the ListChanged events with the ListChangedType.ItemChanged type!!!
I hardly believed my eyes when I saw it. I will be very pleased if anyone proves me wrong.
In the meantime I use an ugly workaround of my devise:
1. I derive from ListCollectionView (because BindingListCollectionView is sealed - thank you very much). Let's call it MyBindingListCollectionView.
2. Subscribe for the ListChanged event of the provided IBindingList - my ERLB instance.
3. Inside the ListChanged event handler I convert the given ListChangedEventArgs object into the respective NotifyCollectionChangedEventArgs object in the best way I can.
4. Call the protected OnCollectionChanged method inherited from the base.
5. Instead of assigning an ERLB instance directly to ItemsSource, I manually wrap it with the new instance of the MyBindingListCollectionView class.
This workaround seems to work, but I would really really like a better solution than my hack.
Anyone?

Copyright (c) Marimer LLC