从XAML初始化已排序的集合

时间:2011-04-02 16:51:10

标签: wpf xaml

我有一个SortedObservableCollection课程(最初基于this)。它完全符合它的承诺 - 它是一个泛型集合,它实现INotifyCollectionChanged并按排序顺序维护其元素(根据提供的IComparer)。订单仅在插入时检查 - 当插入项目时,它会被插入到集合中的正确位置。

但是我尝试使用这样的语法从XAML初始化集合时遇到了一个主要问题(Items属性为SortedObservableCollection<MyItem>类型,Priority是排序键):< / p>

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

这应该导致使用按顺序2,1,0的项目进行收集,但结果是1,2,0。

我花了很长时间才发现原因:首先构建集合项,然后将其添加到集合中,然后才分配其属性值。

我无法在任何地方找到这种行为,我同意它通常并不重要。但在我的情况下,Priority属性总是值为0,因此排序根本不会发生(实际上,项目的插入顺序与它们在XAML中的顺序相反)。在排序完成后,Priority被初始化。

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

我能想到的唯一解决方案是让项目实现INotifyPropertyChanged然后在Add方法中订阅它(然后在必要时更新订单),但我想这会带来比实际更麻烦(性能,内存泄漏......)。

感谢您的帮助!

1 个答案:

答案 0 :(得分: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...
}