When Clearing an ObservableCollection, There are No Items in e.OldItems

前端 未结 20 1661
不知归路
不知归路 2020-11-30 00:27

I have something here that is really catching me off guard.

I have an ObservableCollection of T that is filled with items. I also have an event handler attached to t

相关标签:
20条回答
  • 2020-11-30 00:37

    If your ObservableCollection is not getting clear, then you may try this below code. it may help you:

    private TestEntities context; // This is your context
    
    context.Refresh(System.Data.Objects.RefreshMode.StoreWins, context.UserTables); // to refresh the object context
    
    0 讨论(0)
  • 2020-11-30 00:40

    I was just going through some of the charting code in the Silverlight and WPF toolkits and noticed that they also solved this problem (in a kind of similar way) ... and I thought I would go ahead and post their solution.

    Basically, they also created a derived ObservableCollection and overrode ClearItems, calling Remove on each item being cleared.

    Here is the code:

    /// <summary>
    /// An observable collection that cannot be reset.  When clear is called
    /// items are removed individually, giving listeners the chance to detect
    /// each remove event and perform operations such as unhooking event 
    /// handlers.
    /// </summary>
    /// <typeparam name="T">The type of item in the collection.</typeparam>
    public class NoResetObservableCollection<T> : ObservableCollection<T>
    {
        public NoResetObservableCollection()
        {
        }
    
        /// <summary>
        /// Clears all items in the collection by removing them individually.
        /// </summary>
        protected override void ClearItems()
        {
            IList<T> items = new List<T>(this);
            foreach (T item in items)
            {
                Remove(item);
            }
        }
    }
    
    0 讨论(0)
  • 2020-11-30 00:40

    To keep it simple why don't you override the ClearItem method and do whatever you want there ie Detach the items from the event.

    public class PeopleAttributeList : ObservableCollection<PeopleAttributeDto>,    {
    {
      protected override void ClearItems()
      {
        Do what ever you want
        base.ClearItems();
      }
    
      rest of the code omitted
    }
    

    Simple, clean, and contain within the collection code.

    0 讨论(0)
  • 2020-11-30 00:42

    I had the same issue, and this was my solution. It seems to work. Does anyone see any potential problems with this approach?

    // overriden so that we can call GetInvocationList
    public override event NotifyCollectionChangedEventHandler CollectionChanged;
    
    protected override void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
    {
        NotifyCollectionChangedEventHandler collectionChanged = CollectionChanged;
        if (collectionChanged != null)
        {
            lock (collectionChanged)
            {
                foreach (NotifyCollectionChangedEventHandler handler in collectionChanged.GetInvocationList())
                {
                    try
                    {
                        handler(this, e);
                    }
                    catch (NotSupportedException ex)
                    {
                        // this will occur if this collection is used as an ItemsControl.ItemsSource
                        if (ex.Message == "Range actions are not supported.")
                        {
                            handler(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
                        }
                        else
                        {
                            throw ex;
                        }
                    }
                }
            }
        }
    }
    

    Here are some other useful methods in my class:

    public void SetItems(IEnumerable<T> newItems)
    {
        Items.Clear();
        foreach (T newItem in newItems)
        {
            Items.Add(newItem);
        }
        NotifyCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
    }
    
    public void AddRange(IEnumerable<T> newItems)
    {
        int index = Count;
        foreach (T item in newItems)
        {
            Items.Add(item);
        }
        NotifyCollectionChangedEventArgs e = new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, new List<T>(newItems), index);
        NotifyCollectionChanged(e);
    }
    
    public void RemoveRange(int startingIndex, int count)
    {
        IList<T> oldItems = new List<T>();
        for (int i = 0; i < count; i++)
        {
            oldItems.Add(Items[startingIndex]);
            Items.RemoveAt(startingIndex);
        }
        NotifyCollectionChangedEventArgs e = new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, new List<T>(oldItems), startingIndex);
        NotifyCollectionChanged(e);
    }
    
    // this needs to be overridden to avoid raising a NotifyCollectionChangedEvent with NotifyCollectionChangedAction.Reset, which our other lists don't support
    new public void Clear()
    {
        RemoveRange(0, Count);
    }
    
    public void RemoveWhere(Func<T, bool> criterion)
    {
        List<T> removedItems = null;
        int startingIndex = default(int);
        int contiguousCount = default(int);
        for (int i = 0; i < Count; i++)
        {
            T item = Items[i];
            if (criterion(item))
            {
                if (removedItems == null)
                {
                    removedItems = new List<T>();
                    startingIndex = i;
                    contiguousCount = 0;
                }
                Items.RemoveAt(i);
                removedItems.Add(item);
                contiguousCount++;
            }
            else if (removedItems != null)
            {
                NotifyCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, removedItems, startingIndex));
                removedItems = null;
                i = startingIndex;
            }
        }
        if (removedItems != null)
        {
            NotifyCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, removedItems, startingIndex));
        }
    }
    
    private void NotifyCollectionChanged(NotifyCollectionChangedEventArgs e)
    {
        OnPropertyChanged(new PropertyChangedEventArgs("Count"));
        OnPropertyChanged(new PropertyChangedEventArgs("Item[]"));
        OnCollectionChanged(e);
    }
    
    0 讨论(0)
  • 2020-11-30 00:45

    Ok, even though I still wish that ObservableCollection behaved as I wished ... the code below is what I ended up doing. Basically, I created a new collection of T called TrulyObservableCollection and overrided the ClearItems method which I then used to raise a Clearing event.

    In the code that uses this TrulyObservableCollection, I use this Clearing event to loop through the items that are still in the collection at that point to do the detach on the event that I was wishing to detach from.

    Hope this approach helps someone else as well.

    public class TrulyObservableCollection<T> : ObservableCollection<T>
    {
        public event EventHandler<EventArgs> Clearing;
        protected virtual void OnClearing(EventArgs e)
        {
            if (Clearing != null)
                Clearing(this, e);
        }
    
        protected override void ClearItems()
        {
            OnClearing(EventArgs.Empty);
            base.ClearItems();
        }
    }
    
    0 讨论(0)
  • 2020-11-30 00:45

    I tackled this one in a slightly different manner as I wanted to register to one event and handle all additions and removals in the event handler. I started off overriding the collection changed event and redirecting reset actions to removal actions with a list of items. This all went wrong as I was using the observable collection as an items source for a collection view and got "Range actions not supported".

    I finally created a new event called CollectionChangedRange which acts in the manner I expected the inbuilt version to act.

    I can't imagine why this limitation would be allowed and hope that this post at least stops others from going down the dead end that I did.

    /// <summary>
    /// An observable collection with support for addrange and clear
    /// </summary>
    /// <typeparam name="T"></typeparam>
    [Serializable]
    [TypeConverter(typeof(ExpandableObjectConverter))]
    public class ObservableCollectionRange<T> : ObservableCollection<T>
    {
        private bool _addingRange;
    
        [field: NonSerialized]
        public event NotifyCollectionChangedEventHandler CollectionChangedRange;
    
        protected virtual void OnCollectionChangedRange(NotifyCollectionChangedEventArgs e)
        {
            if ((CollectionChangedRange == null) || _addingRange) return;
            using (BlockReentrancy())
            {
                CollectionChangedRange(this, e);
            }
        }
    
        public void AddRange(IEnumerable<T> collection)
        {
            CheckReentrancy();
            var newItems = new List<T>();
            if ((collection == null) || (Items == null)) return;
            using (var enumerator = collection.GetEnumerator())
            {
                while (enumerator.MoveNext())
                {
                    _addingRange = true;
                    Add(enumerator.Current);
                    _addingRange = false;
                    newItems.Add(enumerator.Current);
                }
            }
            OnCollectionChangedRange(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, newItems));
        }
    
        protected override void ClearItems()
        {
            CheckReentrancy();
            var oldItems = new List<T>(this);
            base.ClearItems();
            OnCollectionChangedRange(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, oldItems));
        }
    
        protected override void InsertItem(int index, T item)
        {
            CheckReentrancy();
            base.InsertItem(index, item);
            OnCollectionChangedRange(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, item, index));
        }
    
        protected override void MoveItem(int oldIndex, int newIndex)
        {
            CheckReentrancy();
            var item = base[oldIndex];
            base.MoveItem(oldIndex, newIndex);
            OnCollectionChangedRange(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Move, item, newIndex, oldIndex));
        }
    
        protected override void RemoveItem(int index)
        {
            CheckReentrancy();
            var item = base[index];
            base.RemoveItem(index);
            OnCollectionChangedRange(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, item, index));
        }
    
        protected override void SetItem(int index, T item)
        {
            CheckReentrancy();
            var oldItem = base[index];
            base.SetItem(index, item);
            OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Replace, oldItem, item, index));
        }
    }
    
    /// <summary>
    /// A read only observable collection with support for addrange and clear
    /// </summary>
    /// <typeparam name="T"></typeparam>
    [Serializable]
    [TypeConverter(typeof(ExpandableObjectConverter))]
    public class ReadOnlyObservableCollectionRange<T> : ReadOnlyObservableCollection<T>
    {
        [field: NonSerialized]
        public event NotifyCollectionChangedEventHandler CollectionChangedRange;
    
        public ReadOnlyObservableCollectionRange(ObservableCollectionRange<T> list) : base(list)
        {
            list.CollectionChangedRange += HandleCollectionChangedRange;
        }
    
        private void HandleCollectionChangedRange(object sender, NotifyCollectionChangedEventArgs e)
        {
            OnCollectionChangedRange(e);
        }
    
        protected virtual void OnCollectionChangedRange(NotifyCollectionChangedEventArgs args)
        {
            if (CollectionChangedRange != null)
            {
                CollectionChangedRange(this, args);
            }
        }
    
    }
    
    0 讨论(0)
提交回复
热议问题