marc wellman
marc wellman

Reputation: 5886

DataBinding reverses the order of my ObservableCollection?

I have the following custom observable collection (The code is taken in parts from Dean Chalk's blog http://www.deanchalk.me.uk/post/Thread-Safe-Dispatcher-Safe-Observable-Collection-for-WPF.aspx and slightly altered):

public class ThreadSaveObservableCollection <T> : IList<T>, INotifyCollectionChanged  {

    private IList<T> collection;
    private Dispatcher uiDispatcher;
    private ReaderWriterLock rwLock;

    public ThreadSaveObservableCollection () {

        collection = new List<T>();
        rwLock = new ReaderWriterLock();
        uiDispatcher = Dispatcher.CurrentDispatcher;
    }

    public void Insert (int index, T item) {

        if (Thread.CurrentThread == uiDispatcher.Thread) {

            insert_(index, item);
        } else {

            uiDispatcher.BeginInvoke(new Action<int, T>(insert_), DispatcherPriority.Normal, new object[] {index, item});
        }
    }

    private void insert_ (int index, T item) {

        rwLock.AcquireWriterLock(Timeout.Infinite);

        collection.Insert(index, item);
        CollectionChanged(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, item));

        rwLock.ReleaseWriterLock();
    }

    public IEnumerator<T> GetEnumerator () {

        rwLock.AcquireReaderLock(Timeout.Infinite);

        IEnumerator<T> enumerator = collection.GetEnumerator();

        rwLock.ReleaseReaderLock();

        return enumerator;
    }

    System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator () {

        rwLock.AcquireReaderLock(Timeout.Infinite);

        IEnumerator<T> enumerator = collection.GetEnumerator();

        rwLock.ReleaseReaderLock();

        return enumerator;
    }

    public event NotifyCollectionChangedEventHandler CollectionChanged;

    ... // the remaining methods of the IList<> interface

}

Further I have a ViewModel which holds an instance of this class:

public class ViewModel {

    private ThreadSaveObservableCollection<string> Collection {get; set;}

    public ViewModel () {
        Collection = new ThreadSaveObservableCollection<string>();
    }

    public void Insert (string item) {

        Collection.Insert(0, item);
    }

}

I apply data binding in code-behind because I create the corresponding WPF control (an ordinary List control) with name "LogList" dynamically:

wpfContainer.LogList.ItemsSource = viewModel.Collection;

Everything works quite fine except the fact that the order of items in the wpf list control is reversed with respect to the items in the Collection object of the ViewModel.

With the statement Collection.Insert(0, intem) I expect to add the new item at the top of the list but what I get is the same result as I would use Collection.Add(item).

When I step into the code during runtime I can verify that the items inside my Collection are in the correct order but on the surface inside the wpf list control the order is altered i.e. reversed.

What am I making wrong ?

I guess the problem must be found somewhere around the data binding because it's the 'wire' that connects my ObservableCollection with the wpf control and it seems that a correct order is getting into the wire and an incorrect is leaving it.

Maybe it has something to do with the GetEnumerator() methods of the IList interface since the ItemSource property of the wpf control is awaiting an Enumerator ?

I have no clue and I am really stuck ...

Thank you in advance for any help ...

Upvotes: 0

Views: 714

Answers (2)

Judah Gabriel Himango
Judah Gabriel Himango

Reputation: 60011

Couple notes about your code:

  • Naming: ThreadSafe, not ThreadSave
  • Race conditions: you're acquiring a lock to call .GetEnumerator. Then releasing the lock, and returning that enumerator. That is not safe, and will throw an exception at runtime if the thread conditions are right. What you should do here is create a copy of the list while under lock, then return an enumerator to that copy.
  • ReaderWriterLock has some known performance, scalability, and error-prone usage (e.g. re-entrance) concerns. Use ReaderWriterLocksSlim instead.
  • The whole idea here is to marshal all operations to the UI thread. If everything happens on the UI thread, there's no need for any locking at all.

Finally, rather than re-invent the wheel, I suggest using one of the existing thread-safe ObservableCollections.

Upvotes: 1

Andrei Neagu
Andrei Neagu

Reputation: 896

Can you try to do this:

http://msdn.microsoft.com/en-us/library/ms653208.aspx

CollectionChanged(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, new List<object>() { item }, 0));

I think that this event is the problem.

Upvotes: 1

Related Questions