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();
}
#端区
//为简洁起见,省略了非泛型实现。。。
}
我认为这行不通。该集合主要用作数据集