我做了这个例子来重新回答我的问题。
我有一个简单的数据网格,其中包含一个仅用于排序元素的 CollectionViewSource
<Window.Resources>
<CollectionViewSource
Source="{Binding _oNodeFolder.lImgs}"
IsLiveSortingRequested="True"
x:Key="LstImgsViewSourceKey"
>
<CollectionViewSource.SortDescriptions>
<csm:SortDescription PropertyName="iNum"/>
</CollectionViewSource.SortDescriptions>
</CollectionViewSource>
</Window.Resources>
如果我添加一些项目,第一次执行工作正常,订单工作正常。 如果我的代码中的任何地方我的对象_oNodeFolder设置为 null,我就会丢失绑定并且排序顺序不再起作用。 但是这些项目已正确显示。
我想念什么?
我尝试使用隐藏的代码重新设置CollectionViewSource.Source。
提前谢谢。
您的CollectionViewSource
甚至没有保存对_oNodeFolder
对象的引用 - 它保存对其属性的引用:lImgs
集合。因此,即使您将_oNodeFolder
设置为null
,CollectionViewSource
仍然持有对该原始lImgs
集合的引用,而该集合又可能持有对其父级的引用,并且 GUI 也通过其View
保存对CollectionViewSource
本身的引用 - 因此引用链保持不间断, 并且"旧"项目仍然显示,而且您可能有一个令人讨厌的内存泄漏。
您不能只将绑定到CollectionViewSource
Source
的父对象设置为null
- 这不是它应该的工作方式。您要么:
- 根据需要在代码隐藏中设置和重置
CollectionViewSource.Source
(并可能刷新绑定;有时在将其设置为新值之前将其设置为null
会有所帮助 - 我个人不喜欢这种方法) - 切勿将基础源集合设置为 null,仅填充和清理它 - 这是使用
CollectionViewSource
的"默认"方式。
为了更好地控制我的集合及其显示方式(也来自代码隐藏),以及避免上述类型的"意外"内存泄漏(由于引用链的一部分未被处理,整个对象链可能永远挂在内存中),如果我只需要集合上的一个View
,我通常使用解决方法:
public class ViewableCollection<T> : ObservableCollection<T>
{
private ListCollectionView _View;
public ViewableCollection(IEnumerable<T> items)
: base(items) { }
public ViewableCollection()
: base() { }
[XmlIgnore]
public ListCollectionView View
{
get
{
if (_View == null)
{
_View = new ListCollectionView(this);
_View.CurrentChanged += new EventHandler(InnerView_CurrentChanged);
}
return _View;
}
}
[XmlIgnore]
public T CurrentItem
{
get
{
return (T)this.View.CurrentItem;
}
set
{
this.View.MoveCurrentTo(value);
}
}
private void InnerView_CurrentChanged(object sender, EventArgs e)
{
this.OnPropertyChanged(new PropertyChangedEventArgs("CurrentItem"));
}
public void AddRange(IEnumerable<T> range)
{
if (range == null)
throw new ArgumentNullException("range");
foreach (T item in range)
{
this.Items.Add(item);
}
this.OnPropertyChanged(new PropertyChangedEventArgs("Count"));
this.OnPropertyChanged(new PropertyChangedEventArgs("Item[]"));
this.OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
}
public void ReplaceItems(IEnumerable<T> range)
{
if (range == null)
throw new ArgumentNullException("range");
this.Items.Clear();
foreach (T item in range)
{
this.Items.Add(item);
}
this.OnPropertyChanged(new PropertyChangedEventArgs("Count"));
this.OnPropertyChanged(new PropertyChangedEventArgs("Item[]"));
this.OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
}
public void RemoveItems(IEnumerable<T> range)
{
if (range == null)
throw new ArgumentNullException("range");
foreach (T item in range)
{
this.Items.Remove(item);
}
this.OnPropertyChanged(new PropertyChangedEventArgs("Count"));
this.OnPropertyChanged(new PropertyChangedEventArgs("Item[]"));
this.OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
}
public void ClearAll()
{
IList old = this.Items.ToList();
base.Items.Clear();
this.OnPropertyChanged(new PropertyChangedEventArgs("Count"));
this.OnPropertyChanged(new PropertyChangedEventArgs("Item[]"));
this.OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
}
public void CallCollectionChaged()
{
this.OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
}
// necessary for xml easy serialization using [XmlArray] attribute
public static implicit operator List<T>(ViewableCollection<T> o)
{
return o == null ? default: o.ToList();
}
// necessary for xml easy serialization using [XmlArray] attribute
public static implicit operator ViewableCollection<T>(List<T> o)
{
return o == default || o == null ? new ViewableCollection<T>() : new ViewableCollection<T>(o);
}
}
然后,让我们假设我有一个这样的类(为了清楚起见,我在这里跳过PropertyChanged
实现):
public class VM_Persons
{
public ViewableCollection<Person> Persons { get; set; }
public string NameFilter { get; set; }
public VM_Perosns()
{
this.Persons.View.SortDescriptions.Add(new SortDescription("Name", ListSortDirection.Ascending));
PropertyGroupDescription groupDescription = new PropertyGroupDescription("Country");
this.Persons.View.GroupDescriptions.Add(groupDescription);
this.Persons.View.Filter = method_Filter;
}
private bool method_Filter(object item)
{
if (item == null) return false;
if (item is Person p)
{
if (p.Name.Contains(NameFilter)) return true;
}
}
public void LoadData(List<Person> list)
{
this.Persons.ReplaceItems(list);
}
//....
}
在 GUI 中,我将像这样绑定到它(在此处绑定到View
属性很重要):
<ListView ItemsSource="{Binding Path=Persons.View, Mode=OneWay}"/>
这样,我每个集合都有一个视图,并且没有任何内容包含我无法控制的泄漏引用。