2011-04-02 73 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