Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

INotifyCollectionChanged: Added item does not appear at given index '0'

Tags:

c#

wpf

I'm making a observable class. The Add methods works fine. But then I'm trying to call the Remove() method I get this error:

"Added item does not appear at given index '0'"

but I'm setting the NotifyCollectionChangedAction enum to Remove as the code below.

    public class ObservableOrderResponseQueue : INotifyCollectionChanged, IEnumerable<OrderResponse>
{
    public event NotifyCollectionChangedEventHandler CollectionChanged;
    private List<OrderResponse> _list = new List<OrderResponse>();


    public void Add(OrderResponse orderResponse)
    {
        this._list.Add(orderResponse);
        if (CollectionChanged != null)
        {
            CollectionChanged(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, orderResponse, 0));
        }
    }

    public void RemoveAt(int index)
    {
        OrderResponse order = this._list[index];
        this._list.RemoveAt(index);
        if (CollectionChanged != null)
        {
            CollectionChanged(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, order, index));
        }
    }

    public void Remove(OrderResponse orderResponse)
    {
        var item = _list.Where(o => o.OrderDetail.TrayCode == orderResponse.OrderDetail.TrayCode).FirstOrDefault();
        int index = _list.IndexOf(item);

        this._list.RemoveAt(index);
        if (CollectionChanged != null)
        {
            CollectionChanged(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, item, index));
        }
    }
like image 583
Tan Avatar asked May 04 '11 13:05

Tan


2 Answers

Are you sure the error is with the Remove method? The error message and your source code indicate it is in the Add method. Try using the correct index _list.Count - 1 in the constructor of NotifyCollectionChangedEventArgs:

CollectionChanged(this, new NotifyCollectionChangedEventArgs(
                                              NotifyCollectionChangedAction.Add, 
                                              orderResponse, _list.Count - 1)
                 );
like image 86
Daniel Hilgarth Avatar answered Oct 29 '22 05:10

Daniel Hilgarth


If I were to guess, I'd say that this line...

var item = _list.Where(
    o => o.OrderDetail.TrayCode == orderResponse.OrderDetail.TrayCode)
    .FirstOrDefault();

...because it calls FirstOrDefault(), may be returning null. Set a breakpoint and see what's actually going on.

To that end, why even do that query? Since you're passing in the object to be removed, why not simply do this:

int index = _list.IndexOf(item);
if (index >= 0)
    this._list.RemoveAt(index);
//then pass item to your eventargs since it was the object removed.

Even better, since List<T> has its own Remove(T object) method:

this._list.Remove(item);
//this may throw an exception or not if the item is not in that collection,
// which is behavior you should probably retain

Sidenotes

There is also a possible race condition in how you raise the CollectionChanged event. In between checking for null and raising it a subscriber could remove its delegate from the event. Here's an easy way to avoid that:

// initialize it with an empty lamda so it is never null
public event NotifyCollectionChangedEventHandler CollectionChanged = (o,e) => {};

Now you can just raise it and never need to check if it is null:

public void Add(OrderResponse orderResponse)
{
    this._list.Add(orderResponse);
    CollectionChanged(this,
        new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add,
            orderResponse, this._list.Count - 1));
}

This doesn't make it completely thread-safe, but it's an easy way of making sure raising the event won't throw a null reference exception.

like image 25
Joel B Fant Avatar answered Oct 29 '22 04:10

Joel B Fant