I implemented an ObservableStack, per this post: Observable Stack and Queue. It's working 99% of the time, but in some cases - rarely & seemingly without reason - when I try to pop the stack, I get an exception:
System.InvalidOperationException: Added item does not appear at given index '1'.
at MS.Internal.Data.EnumerableCollectionView.ProcessCollectionChanged(NotifyCollectionChangedEventArgs args)
at System.Windows.Data.CollectionView.OnCollectionChanged(Object sender, NotifyCollectionChangedEventArgs args)
at System.Collections.Specialized.NotifyCollectionChangedEventHandler.Invoke(Object sender, NotifyCollectionChangedEventArgs e)
...
Here's the relevant code:
public class ObservableStack<T> : Stack<T>, INotifyCollectionChanged, INotifyPropertyChanged
{
public virtual event NotifyCollectionChangedEventHandler CollectionChanged;
public new virtual void Push(T item)
{
base.Push(item);
var e = new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, item, base.Count-1);
if (this.CollectionChanged != null)
this.CollectionChanged(this, e);
}
public new virtual T Pop()
{
var item = base.Pop();
var e = new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, item, base.Count);
if (this.CollectionChanged != null)
this.CollectionChanged(this, e);
return item;
}
//...
}
In the rare cases it happens, the exception is thrown on the call to CollectionChanged() in Pop(). This is similar to the question INotifyCollectionChanged: Added item does not appear at given index '0' , but none of the answers seem applicable in this case. Note that replacing base.count with -1 or 0 on the Remove line, where the exception is being thrown, causes it to always fail. Likewise if I just exclude the index from the event args, it always complains "Collection Remove event must specify item position." I can technically make it work by changing the notification args to
var e = new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset);
...But of course, that isn't actually correct, and I would like to get 'Remove' to report properly. None of the items being inserted have an overridden Equals() or operator=.
A little more background: the stack is being used in an UndoHistory, where when values are changed, the previous values are pushed to the stack, & popped when I undo. This works for the vast, vast majority of cases, which is why I'm having difficulty figuring out why the error is occurring in just a small number of cases. And unfortunately, I've been thus far unsuccessful in whittling it down to a small self-contained example that always shows the issue. For instance, in the real application, I might do an operation and undo it with no problem, but then do the same operation twice in a row, after which undoing will raise the exception. In a smaller/simplified example, I can't get it to happen at all. Note that the stack is also bound to an ItemsControl for displaying the undo history - though it's one-way only (i.e. the control isn't used for modifying the stack, only displaying its items).
Any ideas why it would throw such an exception in such seemingly random, rare cases would be greatly appreciated.