0

我有以下自定义可观察集合(代码部分来自 Dean Chalk 的博客http://www.deanchalk.me.uk/post/Thread-Safe-Dispatcher-Safe-Observable-Collection-for-WPF.aspx和略有改动):

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

}

此外,我有一个 ViewModel 包含此类的实例:

public class ViewModel {

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

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

    public void Insert (string item) {

        Collection.Insert(0, item);
    }

}

我在代码隐藏中应用数据绑定,因为我动态创建了名称为“LogList”的相应 WPF 控件(一个普通的 List 控件):

wpfContainer.LogList.ItemsSource = viewModel.Collection;

除了 wpf 列表控件中的项目顺序相对于 ViewModel 的 Collection 对象中的项目相反的事实之外,一切都工作得很好。

使用语句 Collection.Insert(0, intem) 我希望将新项目添加到列表的顶部,但我得到的结果与使用 Collection.Add(item) 的结果相同。

当我在运行时进入代码时,我可以验证我的 Collection 中的项目的顺序是否正确,但在 wpf 列表控件内部的表面上,顺序被改变了,即颠倒了。

我做错了什么?

我想问题必须在数据绑定周围的某个地方找到,因为它是将我的 ObservableCollection 与 wpf 控件连接起来的“线”,并且似乎正确的顺序正在进入线路并且不正确的顺序正在离开它。

也许它与 IList 接口的 GetEnumerator() 方法有关,因为 wpf 控件的 ItemSource 属性正在等待 Enumerator ?

我不知道,我真的被困住了......

预先感谢您的任何帮助 ...

4

2 回答 2

1

你可以尝试这样做:

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

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

我认为这个事件是问题所在。

于 2012-06-07T21:55:42.410 回答
1

关于您的代码的几个注意事项:

  • 命名:ThreadSafe,不是 ThreadSave
  • 竞争条件:您正在获取一个调用 .GetEnumerator 的锁。然后释放锁,并返回那个枚举数。这是不安全的,如果线程条件正确,会在运行时抛出异常。您应该在这里做的是在锁定时创建列表的副本,然后将枚举数返回到该副本。
  • ReaderWriterLock 有一些已知的性能、可伸缩性和容易出错的使用(例如重入)问题。请改用ReaderWriterLocksSlim
  • 这里的整个想法是将所有操作编组到 UI 线程。如果一切都发生在 UI 线程上,则根本不需要任何锁定。

最后,我建议使用现有的线程安全 ObservableCollections之一,而不是重新发明轮子。

于 2012-06-07T22:02:12.563 回答