【问题标题】:ObservableCollection doesn't sort newly added itemsObservableCollection 不对新添加的项目进行排序
【发布时间】:2014-02-07 11:01:43
【问题描述】:

我有以下绑定到 DataGrid 的 ObservableCollection:

public ObservableCollection<Message> Messages = new ObservableCollection<Message>;

XAML:

<DataGrid ItemsSource="{Binding Path=Messages}">

我在启动时使用默认视图对其进行排序:

ICollectionView view = CollectionViewSource.GetDefaultView(Messages);
view.SortDescriptions.Add(new SortDescription("TimeSent", ListSortDirection.Descending));

一切正常,但问题是每当我向 Messages 集合添加新消息时,它只会被附加到列表的底部,而不是自动排序。

Messages.Add(message);

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

【问题讨论】:

    标签: wpf sorting datagrid observablecollection icollectionview


    【解决方案1】:

    我下面的整个答案都是胡言乱语。正如 cmets 中指出的那样,if you bind to the collection itself, then you are implicitly binding to the default collection view。 (然而,正如链接注释中的注释,Silverlight 是一个例外 - 没有隐式创建默认集合视图,除非集合实现 ICollectionViewFactory。)

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

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

    请注意,虽然原始集合(消息)未触及,但 your sorted view will get updated 通过通知事件:

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

    【讨论】:

    • 我很确定绑定到 ObservableCollection 与绑定到默认视图是一样的,因为这是 WPF 在幕后所做的。
    • @Eternal21 怎么可能?然后视图将不得不修改底层源集合,这违背了视图的只读属性......
    • @Eternal21 虽然我注意到这一事实不适用于 Silverlight,但在我看来,我原谅我的无知! :D
    • 您划掉的最后一行实际上是正确的,并且与我的问题有关。结果是,当您修改集合内的项目时,它不会触发 CollectionChanged 事件。根据 MSDN documentation,该事件仅在添加、删除、更改、移动或刷新整个列表时发生。问题是排序似乎只在该事件触发时才会更新。
    【解决方案2】:

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

    【讨论】:

      【解决方案3】:

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

      这是另一个类似的问题: C# WPF Datagrid doesn't dynamically sort on data update

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

      最后,我结合了这两个线程的答案:

      1. ObservableCollection not noticing when Item in it changes (even with INotifyPropertyChanged)
      2. ObservableCollection and Item PropertyChanged

      我还添加了“智能”排序,仅当属性更改为 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);          
                  }
              }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多