WPF CollectionViewSource 失去绑定



我做了这个例子来重新回答我的问题。

我有一个简单的数据网格,其中包含一个仅用于排序元素的 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设置为nullCollectionViewSource仍然持有对该原始lImgs集合的引用,而该集合又可能持有对其父级的引用,并且 GUI 也通过其View保存对CollectionViewSource本身的引用 - 因此引用链保持不间断, 并且"旧"项目仍然显示,而且您可能有一个令人讨厌的内存泄漏。

您不能只将绑定到CollectionViewSourceSource的父对象设置为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}"/>

这样,我每个集合都有一个视图,并且没有任何内容包含我无法控制的泄漏引用。