2009-03-02 14 views
8

考慮這個代碼(genericised例如目的類型名稱):什麼導致使用自定義排序重新排序其項目的WPF ListCollectionView?

// 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傳遞一個空字符串,看看是否一個通用的通知會導致排序啓動。沒有香蕉。

編輯迴應肯特的建議我認爲我會指出,使用這種具有相同的結果,即收集各種各樣一次,但重新排序的數據更改排序的項目:

_itemsView.SortDescriptions.Add(
    new SortDescription("PropertyName", ListSortDirection.Ascending)); 

回答

7

我發現this article by Dr. WPF開始時回答了我的問題,然後繼續討論調用Refresh的性能影響。一些關鍵摘錄:

不幸的是,Refresh()方法會導致視圖的完整重新生成。當刷新()發生在視圖內時,它會引發一個CollectionChanged通知並提供Action爲「Reset」。 ListBox的ItemContainerGenerator接收到此通知,並通過放棄所有項目的現有視覺對象作出響應。然後它完全重新生成新的物品容器和視覺效果。

然後他描述了一種可以提高性能的hacky解決方法。除了調用刷新,刪除,更改然後重新添加項目。

我原以爲有可能列表視圖可以跟蹤一個項目的變化,並知道在視圖內單獨重新定位該項目。

一種新的方法在.NET 3.5 SP1引入涉及該通過數據綁定與方法BeginEdit()CancelEdit()EndEdit()模板提供事務編輯界面IEditableObject。有關更多信息,請閱讀the article

EDIT由於user346528 points outIEditableObject在3.5SP1中實際上並不新鮮。它實際上看起來像是在框架since 1.0

+2

鏈接已經死了新的:http:// drwpf .com/blog/2008/10/20/itemscontrol-e-is-for-editable-collection/ – Gman 2013-04-04 15:00:09

1

鑑於您正在使用自定義排序,因此ListCollectionView無法知道觸發刷新的條件。因此,您需要自己在集合視圖上調用Refresh()

+0

@Kent,謝謝我會試試這個。我不清楚這裏的邏輯,因爲我也嘗試使用「SortDescriptions.Add(new SortDescription」)進行排序。即使ListCollectionView確實知道要排序的條件,它也不會這樣做。 – 2009-03-02 10:44:37

+0

@Kent - I'm我仍然不明白爲什麼框架無法觀察`SortDescription`中指定的屬性是否需要修改,當這個屬性發生變化時,我的類會發出通知 – 2009-08-24 19:14:00

1

顛覆舊的帖子,但只是做一個繼承自ListViewCollection和覆蓋OnPropertyChanged(IBindingList,ListChanged事件將包含ListChangedEventArgs參數中的屬性更改)的新集合類。並確保當屬性更改(由您引發)時,集合中的項目實現並使用INotifyPropertyChange,或者集合不會綁定到屬性更改。

然後在這個OnPropertyChanged方法中,發件人將是該項目。如果 - 且僅當 - 會導致度假村更改的屬性,然後重新添加它(將其插入到已排序的位置(如果已添加),則除去該項目)。移動一個項目是可取的,如果它可用,而不是刪除/添加它。同樣,這也應該通過過濾來完成(檢查過濾器謂詞)。

IEditableObject不是必需的!如果您希望編輯多個屬性,然後完成編輯(如編輯3個屬性,然後在WinForm DataGridView的不同行上選擇),然後進行排序,這將是使其正常工作的正確方法。但很多時候,您可能希望集合在更改每個屬性後進行度量,而無需手動調用BeginEdit/EndEdit。 IEditableObject,btw,存在於.NET 2.0框架中,並不是.NET 3.5的新功能(如果您閱讀Dr的文章)。

注意:使用BeginEdit()和EndEdit()對同一項進行多次編輯可能會出現問題 - 除非您爲整數而增加(爲true)/遞減(爲false)整數,而不是設置布爾值!請記住增加/減少一個整數,以便在編輯完成時真正知道。

保持一個永久排序的列表是耗時且容易出錯的(如果您強制排序插入,它可能會破壞插入契約),並且應該只能在某些地方使用,例如ComboBoxes。在任何網格中,這是一個非常糟糕的主意,因爲更改一行將導致它從用戶當前位置下退出。

Public Class ListView 
    Inherits ListCollectionView 

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

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

上集合,做類似於你需要的是傑西·約翰遜ObjectListView最好的例子,雖然這是.NET 2.0的具體(而不是INotifyCollectionChanged /的ObservableCollection/ListCollectionView的IBindingList的),並採用了非常嚴格的許可證。他的博客在他如何完成這個任務中可能仍然非常有價值。

編輯:

忘了補充一點,發件人將是你需要求助於的項目,e.PropertyName是你將需要使用以確定它是否是SortDescriptions內的東西。如果不是這樣,對該物業的更改不會導致需要度假村。如果e.PropertyName是Nothing,那麼它就像刷新一樣,許多屬性可能已經改變並且應該完成。

要確定它是否需要過濾,只需通過FilterPredicate運行它,並在需要時將其刪除。過濾比分類要便宜很多。

希望有用,

TamusJRoyce

6

作爲公認的答案引導我,我能夠迫使單個項目的代碼重新

IEditableCollectionView collectionView = DataGrid.Items; 

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

哪裏changedItem視圖模型ItemsSource集合中的項目)。

這樣你就不需要你的項目來實現像IEditableObject這樣的接口(在我看來,在某些情況下它有非常複雜和難以實現的契約)。