【问题标题】:WPF CollectionViewSource lost binding [closed]WPF CollectionViewSource 丢失绑定 [关闭]
【发布时间】:2021-08-05 12:46:24
【问题描述】:

我做了这个例子来重复我的问题。

我有一个带有 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。

提前致谢。

【问题讨论】:

    标签: c# wpf data-binding


    【解决方案1】:

    您的 CollectionViewSource 甚至没有持有对 _oNodeFolder 对象的引用 - 它持有对其属性的引用:lImgs 集合。因此,即使您将 _oNodeFolder 设置为 nullCollectionViewSource 仍然持有对原始 lImgs 集合的引用,而该集合又可能持有对其父集合的引用,而 GUI 也持有通过其ViewCollectionViewSource 本身的引用-因此引用链保持完整,并且仍然显示“旧”项,而且您可能存在严重的内存泄漏。

    您不能只将绑定到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}"/>
    

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

    【讨论】:

    • 感谢您的回答,我现在就试试...
    • 您的解决方案通常是正确的,但您的问题分析却不是。这不是绑定的工作方式,当您在绑定中使用点 (_oNodeFolder.lImgs) 时,绑定值 是内部对象,但沿途的所有对象都安装了 PropertyChanged 处理程序以响应更改,这包括外部对象。改变它会改变绑定值。
    • @Bindy 假设 _oNodeFolder 实现了 PropertyChanged 处理程序。从符号上看,它在我看来是一个私有字段,而不是一个属性。我认为 OP 的案例仍然会受到此影响:stackoverflow.com/a/18543350/891715 虽然我想知道这种行为是否在较新的框架中以某种方式得到了修复?
    猜你喜欢
    • 2017-07-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-02-06
    • 2011-07-14
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多