사용자 지정 정렬을 사용하여 항목을 재 분류하는 WPF ListCollectionView의 원인은 무엇입니까?

StackOverflow https://stackoverflow.com/questions/601864

  •  03-07-2019
  •  | 
  •  

문제

이 코드를 고려하십시오 (예 : 예제 목적으로 일반화 된 유형 이름) : :

// Bound to ListBox.ItemsSource
_items = new ObservableCollection<Item>();

// ...Items are added here ...

// Specify custom IComparer for this collection view
_itemsView = CollectionViewSource.GetDefaultView(_items)
((ListCollectionView)_itemsView).CustomSort = new ItemComparer();

내가 설정했을 때 CustomSort, 컬렉션은 내가 예상 한대로 정렬됩니다.

그러나 속성의 변경에 응답하여 런타임에 자체적으로 데이터를 다시 사용해야합니다. Item. 그만큼 Item 수업에서 파생됩니다 INotifyPropertyChanged 그리고 내 데이터 템플릿이 화면의 값을 업데이트함에 따라 속성이 올바르게 발생한다는 것을 알고 있습니다. 분류 로직 만 호출되지 않습니다.

나는 또한 모금을 시도했다 INotifyPropertyChanged.PropertyChanged 빈 문자열을 전달하여 일반 알림이 정렬이 시작되는지 확인합니다. 바나나가 없습니다.

편집하다 Kent의 제안에 응답하여 나는 이것을 사용하여 항목을 분류하는 것이 동일한 결과, 즉 컬렉션이 한 번 분류한다고 생각할 것이라고 생각했습니다. 하지 않습니다 데이터가 변경됨에 따라 재 분류 :

_itemsView.SortDescriptions.Add(
    new SortDescription("PropertyName", ListSortDirection.Ascending));
도움이 되었습니까?

해결책

나는 찾았다 WPF 박사 의이 기사 내 질문에 대한 답으로 시작하여 전화의 성능 영향에 대해 논의하기 위해 노력합니다. Refresh. 일부 주요 발췌 :

불행히도, 새로 고침 () 메소드는 뷰의 완전한 재생을 초래합니다. view 내에서 새로 고침 ()이 발생하면 CollectionChanged 알림을 올리고 동작을 "재설정"으로 제공합니다. ListBox의 ItemContainerGenerator는이 알림을 받고 기존의 모든 비주얼을 버림으로써 응답합니다. 그런 다음 새로운 항목 컨테이너와 비주얼을 완전히 재생합니다.

그런 다음 성능을 향상시키는 해킹 해결 방법을 설명합니다. 새로 고침을 호출하는 대신 제거하고 변경 한 다음 항목을 다시 추가하십시오.

목록보기가 변경되는 항목을 추적하고 뷰 내에서 해당 항목 만 재배치하는 것을 알 수 있다고 생각했을 것입니다.

새로운 접근 방식이 도입되었습니다 .NET 3.5 SP1 인터페이스 포함 IEditableObject 방법으로 템플릿에 데이터 바인딩을 통해 트랜잭션 편집을 제공합니다. BeginEdit(), CancelEdit(), 그리고 EndEdit(). 읽다 기사 자세한 내용은.

편집하다 처럼 user346528이 지적합니다, IEditableObject 실제로 3.5SP1에서는 새로운 것이 아닙니다. 실제로 프레임 워크에있는 것 같습니다. 1.0 이후.

다른 팁

As the accepted answer directs me to, I am able to force single item reposition with code

IEditableCollectionView collectionView = DataGrid.Items;

collectionView.EditItem(changedItem);
collectionView.CommitEdit();

Where changedItem is the view model (the item in the ItemsSource collection).

This way you don't need your items to implement any interfaces like IEditableObject (which in my opinion has very complex and hard to implement contract in some cases).

Bumping an old post, but just make a new collection class which inherits from ListViewCollection and Overrides OnPropertyChanged (for an IBindingList, ListChanged events will contain the property change in the ListChangedEventArgs parameter). And make sure the items within the collection implements and uses INotifyPropertyChange whenever a property changes (raised by you), or the collection won't bind to property changes.

Then in this OnPropertyChanged method, sender will be the item. Remove the item if--and only if--a property which would cause a resort is changed, then re-add it (insert it in sorted position if adding it doesn't do this already). Moving an item is preferable if it is available instead of removing/adding it. Similarly, this should also be done with filtering (checking the filter predicate).

IEditableObject is not needed! If you desire to edit several properties, then finish editing (like editing 3 properties and then selecting on a different row in WinForm DataGridView), then having it sort, this would be the proper method of getting this to work. But a lot of times you will probably want the collection to resort after changing each property without having to manually call BeginEdit/EndEdit. IEditableObject, btw, is present in the .NET 2.0 framework and is not new to .NET 3.5 (if you read the Dr's Article).

Note: Issues can occur using BeginEdit() and EndEdit() with multiple edits to the same item--unless you increment (for true)/decrement (for false) an integer instead of setting a Boolean! Remember to increment/decrement an integer to truly know when editing is finished.

Keeping a perpetually sorted list is time consuming and error prone (and it can break the insert contract if you force sorted inserts), and should only be used in certain places such as ComboBoxes. On any grid, this is a very bad idea, as changing a row will cause it to resort out from under the users current position.

Public Class ListView
  Inherits ListCollectionView

  Protected Overrides Sub OnPropertyChanged(sender As Object, e As PropertyChangedEventArgs)

    ' Add resorting/filtering logic here.
  End Sub
End Class

The best example on a collection that does similar to what you need is Jesse Johnsons ObjectListView, although this is .NET 2.0 specific (IBindingList instead of INotifyCollectionChanged/ObservableCollection/ListCollectionView) and uses a very restrictive license. His blog may still be very valuable in how he accomplished this.

Edit:

Forgot to add that Sender will be the the item you need to resort, and e.PropertyName is what you will need to use to determine if it is within the SortDescriptions. If it isn't, a change to that property won't result in a resort being needed. If e.PropertyName is Nothing, then it is just like a refresh, where many properties may have changed and resorting should be done.

To determine if it needs filtered, just run it through the FilterPredicate, and remove it if needed. Filtering is a lot less expensive than sorting.

Hopefully Helpful,

TamusJRoyce

Given that you're using custom sorting, there's no way for the ListCollectionView to know what criteria should trigger a refresh. Therefore, you will need to call Refresh() on the collection view yourself.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top