Sanfilippo
Sanfilippo

Reputation: 29

Implement RemoveAll on AsyncObservableCollection

I'm trying to implement the RemoveAll on AsyncObservableCollection, this class was created for edit the collection on another thread, the structure is this:

public class AsyncObservableCollection<T> : ObservableCollection<T>
{
    private readonly SynchronizationContext _synchronizationContext = SynchronizationContext.Current;

    public AsyncObservableCollection()
    {
    }

    public AsyncObservableCollection(IEnumerable<T> list)
        : base(list)
    {
    }

    private void ExecuteOnSyncContext(Action action)
    {
        if (SynchronizationContext.Current == _synchronizationContext)
        {
            action();
        }
        else
        {
            _synchronizationContext.Send(_ => action(), null);
        }
    }

    protected override void InsertItem(int index, T item)
    {
        ExecuteOnSyncContext(() => base.InsertItem(index, item));
    }

    protected override void RemoveItem(int index)
    {
        ExecuteOnSyncContext(() => base.RemoveItem(index));
    }

    protected override void SetItem(int index, T item)
    {
        ExecuteOnSyncContext(() => base.SetItem(index, item));
    }

    protected override void MoveItem(int oldIndex, int newIndex)
    {
        ExecuteOnSyncContext(() => base.MoveItem(oldIndex, newIndex));
    }

    protected override void ClearItems()
    {
        ExecuteOnSyncContext(() => base.ClearItems());
    }

    public void RemoveAll(Predicate<T> predicate)
    {
        CheckReentrancy();

        List<T> itemsToRemove = Items.Where(x => predicate(x)).ToList();
        itemsToRemove.ForEach(item => Items.Remove(item));

        OnPropertyChanged(new PropertyChangedEventArgs("Count"));
        OnPropertyChanged(new PropertyChangedEventArgs("Item[]"));
        OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
    }
}

actually the method working well if the collection is used on the main thread, but if I use on a different thread I get:

Not Supported Exception

on this line:

OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));

there is something that I can do for fix this?

Upvotes: 2

Views: 515

Answers (1)

Ambidex
Ambidex

Reputation: 372

I am not really sure the purpose of this class and why you need to keep the current context, but anyways the solution to your problem is

public class ObservableCollectionAsync<T> : ObservableCollection<T>
{
    public ObservableCollectionAsync()
    {
        BindingOperations.EnableCollectionSynchronization(this, _lock);
    }

    public ObservableCollectionAsync(List<T> list) : base(list)
    {
        BindingOperations.EnableCollectionSynchronization(this, _lock);
    }

    public ObservableCollectionAsync(IEnumerable<T> collection) : base(collection)
    {
        BindingOperations.EnableCollectionSynchronization(this, _lock);
    }
    private readonly SynchronizationContext _synchronizationContext = SynchronizationContext.Current;
    private readonly object _lock = new object();
    private void ExecuteOnSyncContext(Action action)
    {
        if (SynchronizationContext.Current == _synchronizationContext)
        {
            action();
        }
        else
        {
            _synchronizationContext.Send(_ => action(), null);
        }
    }

    protected override void ClearItems()
    {
        ExecuteOnSyncContext(() => base.ClearItems());
    }

    protected override void InsertItem(int index, T item)
    {
        ExecuteOnSyncContext(() => base.InsertItem(index, item));
    }

    protected override void MoveItem(int oldIndex, int newIndex)
    {
        ExecuteOnSyncContext(() => base.MoveItem(oldIndex, newIndex));
    }

    protected override void RemoveItem(int index)
    {
        ExecuteOnSyncContext(() => base.RemoveItem(index));
    }

    protected override void SetItem(int index, T item)
    {

        ExecuteOnSyncContext(() => base.SetItem(index, item));


    }

    public void RemoveAll(Func<T,bool> predicate)
    {
        CheckReentrancy();
        foreach (var item in this.Where(predicate).ToArray())
        {
            this.Remove(item);
        }
    }

But any of the other methods within the Observable Collection itself will not go through your "ExecuteOnSyncContext", so it may be better to just make it public and call it outside the class for the other methods.

Otherwise you'll need to build an Observable Collection using the ICollection interface.

Upvotes: 1

Related Questions