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' 项目仍然显示,而且你有潜在的严重内存泄漏。

您不能只将绑定到 CollectionViewSourceSource 的 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,没有任何内容包含我无法控制的泄漏引用。