Observable Collection replace item

28,599

Solution 1

collection[someIndex] = newItem;

Solution 2

Updated: Indexer uses overridden SetItem and notifies about changes.

I think the answer about using indexer may be wrong, because the question was about replace and notify.

Just to clarify: ObservableCollection<T> uses indexer from its base Collection<T> class, which in turn is a wrapper of List<T>, which is a wrapper of simple array of T. And there's no override for indexer method in ObservableCollection implementation.

So when you use indexer to replace an item in ObservableCollection it invokes the following code from Collection class:

public T this[int index] {
        get { return items[index]; }
        set {
            if( items.IsReadOnly) {
                ThrowHelper.ThrowNotSupportedException(ExceptionResource.NotSupported_ReadOnlyCollection);
            }

            if (index < 0 || index >= items.Count) {
                ThrowHelper.ThrowArgumentOutOfRangeException();
            }

            SetItem(index, value);
        }

It just checks boundaries and calls SetItem that uses indexer of underlying List class:

protected virtual void SetItem(int index, T item) {
        items[index] = item;
    }

During assignment there is no call to the CollectionChanged event, because underlying collections know nothing of it.

But when you use SetItem method, it is called from ObservableCollection class:

protected override void SetItem(int index, T item)
    {
        CheckReentrancy();
        T originalItem = this[index];
        base.SetItem(index, item);

        OnPropertyChanged(IndexerName);
        OnCollectionChanged(NotifyCollectionChangedAction.Replace, originalItem, item, index);
    }

After assignment it calls OnCollectionChanged method, which fires CollectionChanged event with NotifyCollectionChangedAction.Replace action parameter.

    protected virtual void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
    {
        if (CollectionChanged != null)
        {
            using (BlockReentrancy())
            {
                CollectionChanged(this, e);
            }
        }
    }

As a conclusion: the idea with custom class inherited from ObservableCollection and Replace method that calls base.SetItem() worth a try.

Share:
28,599
Taufiq Abdur Rahman
Author by

Taufiq Abdur Rahman

Updated on September 22, 2020

Comments

  • Taufiq Abdur Rahman
    Taufiq Abdur Rahman almost 4 years

    I have a ObservableCollection, I can add and remove item from the collection. But I can't replace an existing item in the collection. There is a way to replace an item and reflect that on my bound components.

    System.Collections.Specialized.NotifyCollectionChangedAction.Replace
    

    Can anyone please show me how to accomplish this?