2

를 추가하지 않습니다 나는이 데이터 그리드에 바인딩 된 다음 ObservableCollection에 : 기본보기를 사용하여,ObservableCollection에 정렬 새로 항목

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

나는 시작에 그것을 분류 :

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

XAML

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

모두 정상적으로 작동하지만 문제는 메시지 컬렉션에 새 메시지를 추가 할 때마다 간단하다는 것입니다. y는 목록 맨 아래에 추가되며 자동으로 정렬되지 않습니다.

Messages.Add(message); 

내가 잘못 했나요? 나는 항목을 추가 할 때마다보기를 새로 고침으로써 문제를 해결할 수 있다고 확신하지만, 이는 성능면에서 언급하지 않는 한 잘못된 방식으로 보입니다.

답변

4

그래서 좀 더 조사를했는데 내 문제가 WPF DataGrid의 제한 때문인 것으로 판명되었습니다. 기본 데이터가 변경되면 컬렉션을 자동으로 다시 정렬하지 않습니다. 즉, 항목을 처음 추가하면 해당 항목이 정렬되어 올바른 위치에 배치되지만 항목의 속성을 변경하면 다시 정렬되지 않습니다. INotifyPropertyChanged는 업데이트 정렬에 영향을주지 않습니다. 표시된 데이터를 업데이트 만 처리하지만 정렬은 트리거하지 않습니다. 다시 정렬을 강제하는 CollectionChanged 이벤트이지만 컬렉션에 이미있는 항목을 수정해도이 특정 이벤트가 트리거되지 않으므로 정렬이 수행되지 않습니다. 사용자의 솔루션은 수동으로 OnCollectionChanged를 호출하는 것을 C# WPF Datagrid doesn't dynamically sort on data update

() :

는 여기에 또 다른 유사한 문제입니다.

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

가 나는 또한에만 OnCollectionChanged()이 경우 호출하는 정렬 '스마트'추가 : 결국

, 나는이 두 개의 스레드에서 답변을 결합 property changed는 현재 SortDescription에서 사용중인 값입니다.

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);   
      } 
     } 
1

아래의 전체 대답은 횡설수설입니다. 의견에서 지적한대로 if you bind to the collection itself, then you are implicitly binding to the default collection view. (그러나, 링크 노트에 주석으로, 실버 라이트는 예외입니다 - 디폴트 콜렉션 뷰가 하지 않는 컬렉션이 ICollectionViewFactory를 구현, 암시가 작성되지 않습니다.)

CollectionViewSource는 기본을 수정하지 않습니다 수집. 정렬을 얻으려면, 당신은 view 자체에 바인딩 예해야합니다 :

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

참고, 그 원래의 콜렉션 (메시지) 알림 이벤트를 통해 your sorted view will get updated, 훼손되지 않은 상태에서 :

소스 콜렉션이 INotifyCollectionChanged 인터페이스를 구현하면 CollectionChanged 이벤트가 발생시킨 변경 사항이 뷰에 전파됩니다.

+0

저는 ObservableCollection에 바인딩하는 것이 기본보기에 바인딩하는 것과 동일하다는 것을 알았습니다. 그 이유는 이것이 WPF가 배경에서하는 일이기 때문입니다. – Eternal21

+0

@ Eternal21 그게 어떻게 될 수 있니? 그런 다음보기는보기의 읽기 전용 속성에 대한 기본 원본 컬렉션을 수정해야 할 것입니다 ... – McGarnagle

+1

http://stackoverflow.com/questions/6317860/should-i-bind-to-icollectionview-or- 관측 가능한 컬렉션 – Eternal21

1

난 그냥 다른 특성을 정렬하는 것을 시도하고 작동하는 일이 있음을 알아 차리지 후, 문제를 발견했다. 내 메시지가 TimeSent 속성이 MinDate로 초기화되고 실제 날짜로만 업데이트 된 컬렉션에 추가 될 때 나타납니다. 그래서 그것은 목록의 맨 아래에 적절히 배치되었습니다. 문제는 TimeSent 속성을 수정할 때 위치가 업데이트되지 않는 것입니다. INotifyPropertyChanged 이벤트 전파에 문제가있는 것 같습니다 (TimeSent는 Message 객체 내의 다른 객체에 있습니다).