2011-04-02 67 views
3

有序集合我有一个SortedObservableCollection类(最初是基于this)。这也正是它的承诺 - 这是一个泛型集合,它实现INotifyCollectionChanged并保持其在排序顺序元素(根据提供IComparer)。订单仅在插入时进行检查,但是 - 插入项目时,将插入集合中的正确位置。初始化从XAML

但是我遇到的一个重大问题,而试图从XAML,像这样的语法初始化集合(该Items属性为SortedObservableCollection<MyItem>型,Priority是排序键):

<my:SomeElement.Items> 
    <my:MyItem Priority="0"> 
    <my:MyItem Priority="2"> 
    <my:MyItem Priority="1"> 
</my:SomeElement.Items> 

这将导致集合与项目2,1,0,但它导致秩序1,2,0,

我花了相当一段时间来发现原因:集合项目是第一次构建,然后添加到集合,只有那么他们的属性的价值assig斯内德。

我找不到这种行为在任何地方记录和我同意它并不真正的问题通常。但在我的情况下,Priority属性始终为值0,所以排序完全不会发生(实际上,项目的插入顺序与它们在XAML中的顺序相反)。在排序发生后,Priority被初始化。

你遇到自己这种行为?为什么XAML是这样实现的?我该如何解决这个问题?

我能想到的唯一的解决办法是让项目实施INotifyPropertyChanged,然后订阅它在Add方法(必要时再更新的顺序),但我想这会带来更多的麻烦比它的价值(性能,内存泄漏......)。

感谢您的帮助!

回答

1

如果你的目标是在这是正常在所有时间排序的集合,那么你就需要去聆听的办法。你可以让你的项目支持一个弱事件机制,以防止他们持有强大的参考收集。

另一种方法是推迟排序,直到收集“完全构建”为止。例如,您可以在您的收集实施中使用标记isSorted。无论何时修改集合(为简单起见),都将此标志设置为false,并在收集“读取”之前对其进行检查。

事情是这样的:

public void Add(T item) 
{ 
    _innerList.Add(item); 
    _isSorted = false; 
} 

和:

public int IndexOf(T item) 
{ 
    EnsureSorted(); 
    return _innerList.IndexOf(item); 
} 

其中EnsureSorted可能是这个样子:

private void EnsureSorted() 
{ 
    if (!_isSorted) 
    { 
    _innerList.Sort(_comparer); 
    _isSorted = true; 

    // TODO: Raise the CollectionChanged event here, specifying 
    //  NotifyCollectionChangedAction.Reset 
    } 
} 

这应该使您的收藏显得排序,同时仍允许其在填充列表时被排序。

也许这将是一个可行的解决方法吗?


更新:

我创建了一个简单的观察集合与这种递延排序。我想你可能会发现它有帮助,至少它应该清楚我的意思。

想法是在“读取”集合之前调用EnsureSorted方法,并在收集被修改时清除isSorted标志。

public class SortedObservableCollection<T> : IList<T>, IList, INotifyCollectionChanged, INotifyPropertyChanged 
{ 
    private readonly List<T> _innerList; 
    private IComparer<T> _comparer; 
    private bool _isSorted; 

    public event NotifyCollectionChangedEventHandler CollectionChanged; 

    public event PropertyChangedEventHandler PropertyChanged; 

    public SortedObservableCollection() 
     : this(null) 
    { 
    } 

    public SortedObservableCollection(IComparer<T> comparer) 
    { 
     _innerList = new List<T>(); 
     _comparer = comparer ?? Comparer<T>.Default; 
    } 

    // Call this before "reading" collection 
    private void EnsureSorted() 
    { 
     if (!_isSorted) 
     { 
      _innerList.Sort(_comparer); 
      _isSorted = true; 
     } 
    } 

    // Call this after modifying the collection 
    private void NotifyChanged() 
    { 
     if (PropertyChanged != null) 
     { 
      PropertyChanged(this, new PropertyChangedEventArgs("Count")); 
     } 

     if (CollectionChanged != null) 
     { 
      CollectionChanged(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset)); 
     } 

     _isSorted = false; 
    } 

    #region List implementation 

    public int IndexOf(T item) 
    { 
     EnsureSorted(); 
     return _innerList.IndexOf(item); 
    } 

    public void Insert(int index, T item) 
    { 
     EnsureSorted(); 
     _innerList.Insert(index, item); 
     NotifyChanged(); 
    } 

    public void RemoveAt(int index) 
    { 
     EnsureSorted(); 
     _innerList.RemoveAt(index); 
     NotifyChanged(); 
    } 

    public T this[int index] 
    { 
     get 
     { 
      EnsureSorted(); 
      return _innerList[index]; 
     } 

     set 
     { 
      EnsureSorted(); 
      _innerList[index] = value; 
      NotifyChanged(); 
     } 
    } 

    public void Add(T item) 
    { 
     _innerList.Add(item); 
     NotifyChanged(); 
    } 

    public void Clear() 
    { 
     _innerList.Clear(); 
     NotifyChanged(); 
    } 

    public bool Contains(T item) 
    { 
     return _innerList.Contains(item); 
    } 

    public void CopyTo(T[] array, int arrayIndex) 
    { 
     EnsureSorted(); 
     _innerList.CopyTo(array, arrayIndex); 
    } 

    public int Count 
    { 
     get { return _innerList.Count; } 
    } 

    public bool IsReadOnly 
    { 
     get { return false; } 
    } 

    public bool Remove(T item) 
    { 
     if (!_innerList.Remove(item)) 
     { 
      return false; 
     } 

     NotifyChanged(); 
     return true; 
    } 

    public IEnumerator<T> GetEnumerator() 
    { 
     EnsureSorted(); 
     return _innerList.GetEnumerator(); 
    } 

    #endregion 

    // Non-generic implementation omitted for brevity... 
} 
+0

我不认为这会奏效。该集合主要用作其他控件的数据源,这意味着它不会要求更新,而是等待它们。因此我无法知道收藏品何时完全构建。 – 2011-04-03 08:06:31

+0

我想它应该无论如何工作。只要确保你有你的ObservableCollection实现提升适当的事件。 – 2011-04-03 08:17:52

+0

最简单的方法可能(取决于您的实现)在列表排序后提出重置更改操作。我更新了我的帖子以反映这一点。 – 2011-04-03 08:24:35