WPF CollectionViewSource 丢失绑定
WPF CollectionViewSource lost binding
我举这个例子来重述我的问题。
我有一个带有 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
object 的引用 - 它持有对其 属性 的引用: lImgs
collection。因此,即使在将 _oNodeFolder
设置为 null
之后,CollectionViewSource
仍持有对原始 lImgs
collection 的引用,而后者可能持有引用到它的 parent,并且 GUI 也通过它的 View
持有对 CollectionViewSource
本身的引用 - 因此引用链保持完整,并且 'old' 项目仍然显示,而且你有潜在的严重内存泄漏。
您不能只将绑定到 CollectionViewSource
的 Source
的 parent object 设置为 null
- 这不是应该的方式工作。你要么:
- 根据需要在 code-behind 中设置和重置
CollectionViewSource.Source
(并可能刷新绑定;有时在将其设置为新值之前将其设置为 null
有帮助 - 我个人不'喜欢这种方法)
- 从不将基础源 collection 设置为空,仅填充和清理它 - 这是使用
CollectionViewSource
. 的“默认”方式
更好地控制我的 collection 及其显示方式(也来自 code-behind),以及避免上述类型的“意外”内存泄漏(其中一部分是因为引用链没有被处理掉,整个 object 链可能永远挂在内存中)我通常使用 work-around 如果我只需要一个 View
collection:
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);
}
}
然后,假设我有一个这样的 class(为了清楚起见,我在这里跳过 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}"/>
这样一来,我一个视图一个视图 collection,没有任何内容包含我无法控制的泄漏引用。
我举这个例子来重述我的问题。
我有一个带有 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
object 的引用 - 它持有对其 属性 的引用: lImgs
collection。因此,即使在将 _oNodeFolder
设置为 null
之后,CollectionViewSource
仍持有对原始 lImgs
collection 的引用,而后者可能持有引用到它的 parent,并且 GUI 也通过它的 View
持有对 CollectionViewSource
本身的引用 - 因此引用链保持完整,并且 'old' 项目仍然显示,而且你有潜在的严重内存泄漏。
您不能只将绑定到 CollectionViewSource
的 Source
的 parent object 设置为 null
- 这不是应该的方式工作。你要么:
- 根据需要在 code-behind 中设置和重置
CollectionViewSource.Source
(并可能刷新绑定;有时在将其设置为新值之前将其设置为null
有帮助 - 我个人不'喜欢这种方法) - 从不将基础源 collection 设置为空,仅填充和清理它 - 这是使用
CollectionViewSource
. 的“默认”方式
更好地控制我的 collection 及其显示方式(也来自 code-behind),以及避免上述类型的“意外”内存泄漏(其中一部分是因为引用链没有被处理掉,整个 object 链可能永远挂在内存中)我通常使用 work-around 如果我只需要一个 View
collection:
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);
}
}
然后,假设我有一个这样的 class(为了清楚起见,我在这里跳过 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}"/>
这样一来,我一个视图一个视图 collection,没有任何内容包含我无法控制的泄漏引用。