Wpf 从XAML初始化已排序集合

Wpf 从XAML初始化已排序集合,wpf,xaml,Wpf,Xaml,我有一个SortedObservableCollection类(最初基于)。它完全按照它所承诺的那样做——它是一个通用集合,它实现了INotifyCollectionChanged,并按排序顺序维护其元素(根据提供的IComparer)。但是,只有在插入时才检查订单-插入项目时,会将其插入到集合中的正确位置 但是,我在尝试使用以下语法从XAML初始化集合时遇到了一个主要问题(Items属性是sortedObjectCollection类型,Priority是排序键): 这将导致项目的集合顺序

我有一个
SortedObservableCollection
类(最初基于)。它完全按照它所承诺的那样做——它是一个通用集合,它实现了
INotifyCollectionChanged
,并按排序顺序维护其元素(根据提供的
IComparer
)。但是,只有在插入时才检查订单-插入项目时,会将其插入到集合中的正确位置

但是,我在尝试使用以下语法从XAML初始化集合时遇到了一个主要问题(
Items
属性是
sortedObjectCollection
类型,
Priority
是排序键):


这将导致项目的集合顺序为2、1、0,但它的结果顺序为1、2、0

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

我在任何地方都找不到这种行为的记录,我同意这通常并不重要。但在我的例子中,
Priority
属性总是值为0,因此排序根本不会发生(事实上,项目的插入顺序与XAML中的顺序相反)。并且,在进行排序之后,
优先级
被初始化

您自己是否遇到过这种行为?为什么XAML是这样实现的?如何解决此问题?

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


谢谢你的帮助

如果您的目标是始终正确排序的集合,那么您需要采用倾听的方法。您可以使您的项目支持弱事件机制,以防止它们持有对集合的强引用

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

大概是这样的:

public void Add(T item)
{
  _innerList.Add(item);
  _isSorted = false;
}
private void EnsureSorted()
{
  if (!_isSorted)
  {
    _innerList.Sort(_comparer);
    _isSorted = true;

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

随后出现的
可能如下所示:

public void Add(T item)
{
  _innerList.Add(item);
  _isSorted = false;
}
private void EnsureSorted()
{
  if (!_isSorted)
  {
    _innerList.Sort(_comparer);
    _isSorted = true;

    // TODO: Raise the CollectionChanged event here, specifying
    //       NotifyCollectionChangedAction.Reset
  }
}
这将使您的集合显示为已排序,同时在填充列表时仍允许取消排序

也许这是一个可行的解决办法


更新:

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

其思想是在“读取”集合之前调用
ensurereduced
方法,并在修改集合时清除
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...
}
公共类SortedObjectCollection:IList、IList、INotifyCollectionChanged、INotifyPropertyChanged
{
私有只读列表_innerList;
私人IComparer\u comparer;
私人住宅被分类;
公共事件通知CollectionChangedEventHandler CollectionChanged;
公共事件属性更改事件处理程序属性更改;
公共服务集合()
:此(空)
{
}
公共分类服务收集(IComparer比较器)
{
_innerList=新列表();
_比较器=比较器??比较器。默认值;
}
//在“阅读”集合之前调用此选项
由此产生的私人无效()
{
如果(!\u已排序)
{
_排序(_comparer);
_isSorted=true;
}
}
//修改集合后调用此函数
私有void NotifyChanged()
{
if(PropertyChanged!=null)
{
PropertyChanged(新PropertyChangedEventArgs(“计数”);
}
如果(CollectionChanged!=null)
{
CollectionChanged(这是新的NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
}
_isSorted=假;
}
#区域清单的实施
公共整数索引(T项)
{
随后发生();
返回_innerList.IndexOf(项);
}
公共空白插入(整数索引,T项)
{
随后发生();
_插入(索引,项);
NotifyChanged();
}
公共无效删除(整数索引)
{
随后发生();
_RemoveAt(索引);
NotifyChanged();
}
公共T此[int索引]
{
得到
{
随后发生();
返回_innerList[索引];
}
设置
{
随后发生();
_innerList[索引]=值;
NotifyChanged();
}
}
公共作废新增(T项)
{
_innerList.Add(项);
NotifyChanged();
}
公共空间清除()
{
_Clear();
NotifyChanged();
}
公共布尔包含(T项)
{
返回_innerList.Contains(项);
}
public void CopyTo(T[]数组,int arrayIndex)
{
随后发生();
_CopyTo(数组,arrayIndex);
}
公共整数计数
{
获取{return\u innerList.Count;}
}
公共图书馆是只读的
{
获取{return false;}
}
公共布尔删除(T项)
{
如果(!\u innerList.Remove(项目))
{
返回false;
}
NotifyChanged();
返回true;
}
公共IEnumerator GetEnumerator()
{
随后发生();
返回_innerList.GetEnumerator();
}
#端区
//为简洁起见,省略了非泛型实现。。。
}

我认为这行不通。该集合主要用作数据集