Wpf 可观察收集不';t对新添加的项目进行排序

Wpf 可观察收集不';t对新添加的项目进行排序,wpf,sorting,datagrid,observablecollection,icollectionview,Wpf,Sorting,Datagrid,Observablecollection,Icollectionview,我有以下绑定到DataGrid的ObservableCollection: public ObservableCollection<Message> Messages = new ObservableCollection<Message>; 这一切都很好,但问题是每当我向Messages集合添加新消息时,它只是被追加到列表的底部,而不是自动排序 Messages.Add(message); 我做错什么了吗?我相信我可以通过每次添加项目时刷新视图来解决这个问题,但这样做

我有以下绑定到DataGrid的ObservableCollection:

public ObservableCollection<Message> Messages = new ObservableCollection<Message>;
这一切都很好,但问题是每当我向Messages集合添加新消息时,它只是被追加到列表的底部,而不是自动排序

Messages.Add(message);

我做错什么了吗?我相信我可以通过每次添加项目时刷新视图来解决这个问题,但这样做似乎是错误的(更不用说性能了)。

我下面的全部答案都是胡说八道。正如评论中指出的那样。(然而,作为链接注释中的注释,Silverlight是一个例外——除非集合实现了
ICollectionViewFactory
,否则不会隐式创建默认集合视图)

CollectionViewSource
不会修改基础集合。要获得排序,您需要绑定到自身,例如:

<DataGrid ItemsSource="{Binding Path=CollectionViewSource.View}">

请注意,虽然原始集合(消息)未被触及,但通过通知事件:

如果源集合实现INotifyCollectionChanged接口,则CollectionChanged事件引发的更改将传播到视图。


在尝试对另一个属性进行排序并注意到它碰巧起作用后,我发现了问题。当我的消息被添加到集合中时,TimeSent属性被初始化为MinDate,然后才更新为实际日期。因此,它被正确地放在列表的底部。问题是修改TimeSent属性时,位置没有得到更新。看起来我在传播INotifyPropertyChanged事件时遇到了问题(TimeSent驻留在消息对象内的另一个对象中)。

因此我做了更多的调查,结果表明我的问题是由于WPF datagrid的限制。当基础数据更改时,它不会自动对集合重新排序。换句话说,当您第一次添加项目时,它将被排序并放置在正确的位置,但是如果您更改项目的属性,它将不会被重新排序。INotifyPropertyChanged与排序更新无关。它只处理更新显示的数据,但不会触发排序。强制重新排序的是CollectionChanged事件,但修改集合中已有的项不会触发此特定事件,因此不会执行排序

还有一个类似的问题:

该用户的解决方案是手动调用OnCollectionChanged()

最后,我结合了这两条线索的答案:

  • 我还添加了“智能”排序,只有当属性changed是当前在SortDescription中使用的值时,才会调用OnCollectionChanged()

        public class MessageCollection : ObservableCollection<Message>
        {
            ICollectionView _view;
    
            public MessageCollection()
            {
                _view = CollectionViewSource.GetDefaultView(this);                        
            }
    
            public void Sort(string propertyName, ListSortDirection sortDirection)
            {
                _view.SortDescriptions.Clear();
                _view.SortDescriptions.Add(new SortDescription(propertyName, sortDirection));
            }
    
            protected override void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
            {            
                switch (e.Action)
                {
                    case NotifyCollectionChangedAction.Add:
                        this.AddPropertyChanged(e.NewItems);
                        break;
    
                    case NotifyCollectionChangedAction.Remove:
                        this.RemovePropertyChanged(e.OldItems);
                        break;
    
                    case NotifyCollectionChangedAction.Replace:
                    case NotifyCollectionChangedAction.Reset:
                        this.RemovePropertyChanged(e.OldItems);
                        this.AddPropertyChanged(e.NewItems);
                        break;
                }
    
                base.OnCollectionChanged(e);
            }
    
            private void AddPropertyChanged(IEnumerable items)
            {
                if (items != null)
                {
                    foreach (var obj in items.OfType<INotifyPropertyChanged>())
                    {
                        obj.PropertyChanged += OnItemPropertyChanged;
                    }
                }
            }
    
            private void RemovePropertyChanged(IEnumerable items)
            {
                if (items != null)
                {
                    foreach (var obj in items.OfType<INotifyPropertyChanged>())
                    {
                        obj.PropertyChanged -= OnItemPropertyChanged;
                    }
                }
            }
    
            private void OnItemPropertyChanged(object sender, PropertyChangedEventArgs e)
            {
                bool sortedPropertyChanged = false;
                foreach (SortDescription sortDescription in _view.SortDescriptions)
                {
                    if (sortDescription.PropertyName == e.PropertyName)
                        sortedPropertyChanged = true;
                }
    
                if (sortedPropertyChanged)
                {                
                    NotifyCollectionChangedEventArgs arg = new NotifyCollectionChangedEventArgs(
                        NotifyCollectionChangedAction.Replace, sender, sender, this.Items.IndexOf((Message)sender));
    
                    OnCollectionChanged(arg);          
                }
            }
    
    公共类MessageCollection:ObservableCollection
    {
    ICollectionView\u视图;
    公共信息收集()
    {
    _view=CollectionViewSource.GetDefaultView(此);
    }
    公共void排序(字符串属性名称、ListSortDirection和sortDirection)
    {
    _view.SortDescriptions.Clear();
    _添加(新的SortDescription(propertyName,sortDirection));
    }
    CollectionChanged上的受保护覆盖无效(NotifyCollectionChangedEventArgs e)
    {            
    开关(电动)
    {
    案例NotifyCollectionChangedAction。添加:
    此.AddPropertyChanged(如NewItems);
    打破
    案例NotifyCollectionChangedAction。删除:
    此。移除已更改的属性(如旧项目);
    打破
    案例通知收集更改操作。替换:
    案例通知CollectionChangedAction.Reset:
    此。移除已更改的属性(如旧项目);
    此.AddPropertyChanged(如NewItems);
    打破
    }
    基础。变更的集合(e);
    }
    私有void AddPropertyChanged(IEnumerable items)
    {
    如果(项!=null)
    {
    foreach(items.OfType()中的var obj)
    {
    obj.PropertyChanged+=ONTEMPROPERTYCHANGED;
    }
    }
    }
    私有void RemovePropertyChanged(IEnumerable items)
    {
    如果(项!=null)
    {
    foreach(items.OfType()中的var obj)
    {
    obj.PropertyChanged-=ONTEMPROPERTYCHANGED;
    }
    }
    }
    私有void OnItemPropertyChanged(对象发送方,PropertyChangedEventArgs e)
    {
    bool sortedPropertyChanged=错误;
    foreach(SortDescription SortDescription in _view.SortDescriptions中的SortDescription)
    {
    if(sortDescription.PropertyName==e.PropertyName)
    sortedPropertyChanged=真;
    }
    如果(SortedProperty已更改)
    {                
    NotifyCollectionChangedEventArgs arg=新的NotifyCollectionChangedEventArgs(
    NotifyCollectionChangedAction.Replace,发件人,发件人,this.Items.IndexOf((消息)发件人));
    OnCollectionChanged(arg);
    }
    }
    
    我很确定绑定到ObservableCollection与绑定到默认视图是一样的,因为WPF在幕后就是这么做的。@Eternal21这怎么可能呢?那么视图必须修改底层源集合,这与视图的只读属性相违背。@Eternal21尽管我注意到事实并非如此适用于Silverlight,在我看来这是我无知的借口!:Dy你划掉的最后一行实际上是正确的,并且与我的问题有关。当你修改集合中的项目时,它不会触发CollectionChanged
    <DataGrid ItemsSource="{Binding Path=CollectionViewSource.View}">
    
        public class MessageCollection : ObservableCollection<Message>
        {
            ICollectionView _view;
    
            public MessageCollection()
            {
                _view = CollectionViewSource.GetDefaultView(this);                        
            }
    
            public void Sort(string propertyName, ListSortDirection sortDirection)
            {
                _view.SortDescriptions.Clear();
                _view.SortDescriptions.Add(new SortDescription(propertyName, sortDirection));
            }
    
            protected override void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
            {            
                switch (e.Action)
                {
                    case NotifyCollectionChangedAction.Add:
                        this.AddPropertyChanged(e.NewItems);
                        break;
    
                    case NotifyCollectionChangedAction.Remove:
                        this.RemovePropertyChanged(e.OldItems);
                        break;
    
                    case NotifyCollectionChangedAction.Replace:
                    case NotifyCollectionChangedAction.Reset:
                        this.RemovePropertyChanged(e.OldItems);
                        this.AddPropertyChanged(e.NewItems);
                        break;
                }
    
                base.OnCollectionChanged(e);
            }
    
            private void AddPropertyChanged(IEnumerable items)
            {
                if (items != null)
                {
                    foreach (var obj in items.OfType<INotifyPropertyChanged>())
                    {
                        obj.PropertyChanged += OnItemPropertyChanged;
                    }
                }
            }
    
            private void RemovePropertyChanged(IEnumerable items)
            {
                if (items != null)
                {
                    foreach (var obj in items.OfType<INotifyPropertyChanged>())
                    {
                        obj.PropertyChanged -= OnItemPropertyChanged;
                    }
                }
            }
    
            private void OnItemPropertyChanged(object sender, PropertyChangedEventArgs e)
            {
                bool sortedPropertyChanged = false;
                foreach (SortDescription sortDescription in _view.SortDescriptions)
                {
                    if (sortDescription.PropertyName == e.PropertyName)
                        sortedPropertyChanged = true;
                }
    
                if (sortedPropertyChanged)
                {                
                    NotifyCollectionChangedEventArgs arg = new NotifyCollectionChangedEventArgs(
                        NotifyCollectionChangedAction.Replace, sender, sender, this.Items.IndexOf((Message)sender));
    
                    OnCollectionChanged(arg);          
                }
            }