考虑这个代码(为了举例的目的,通用化的类型名称):
// 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));
答案 0 :(得分:8)
我发现this article by Dr. WPF开始时回答了我的问题,然后继续讨论调用Refresh
对性能的影响。一些关键摘录:
然后,他描述了一种改善性能的hacky解决方法。而不是调用刷新,删除,更改然后重新添加项目。不幸的是,Refresh()方法导致视图完全重新生成。当视图中出现Refresh()时,它会引发CollectionChanged通知并将Action提供为“Reset”。 ListBox的ItemContainerGenerator接收此通知,并通过丢弃项目的所有现有视觉效果进行响应。然后它完全重新生成新的项目容器和视觉效果。
我原本以为列表视图可以跟踪一个更改的项目,并知道在视图中单独重新定位该项目。
在.NET 3.5 SP1中引入了一种新方法,涉及接口IEditableObject
,通过方法BeginEdit()
,CancelEdit()
和{{1}通过数据绑定提供事务编辑}}。请阅读the article以获取更多信息。
编辑作为user346528 points out,EndEdit()
在3.5SP1中实际上并不是新的。它实际上看起来像是在since 1.0框架中。
答案 1 :(得分:8)
由于接受的答案指示我,我能够强制单项重新定位代码
IEditableCollectionView collectionView = DataGrid.Items;
collectionView.EditItem(changedItem);
collectionView.CommitEdit();
changedItem
是视图模型(ItemsSource
集合中的项目)。
通过这种方式,您不需要您的项目来实现IEditableObject
之类的任何接口(在我看来,在某些情况下,这些接口非常复杂且难以实施)。
答案 2 :(得分:2)
压缩旧帖子,但只创建一个继承自ListViewCollection和Overrides 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
与Jesse Johnsons ObjectListView类似的集合上的最佳示例是Jesse Johnsons ObjectListView,尽管这是特定于.NET 2.0的(IBindingList而不是INotifyCollectionChanged / ObservableCollection / ListCollectionView)并且使用非常严格的许可证。他的博客在完成这项工作方面可能仍然非常有价值。
编辑:
忘记添加Sender将是您需要使用的项目,并且您将需要使用e.PropertyName来确定它是否在SortDescriptions中。如果不是,则对该属性的更改将不会导致需要度假村。如果e.PropertyName为Nothing,则它就像刷新一样,其中许多属性可能已经改变并且应该进行求助。
要确定是否需要过滤,只需通过FilterPredicate运行它,并在需要时将其删除。过滤比分拣要便宜得多。
希望有帮助,
TamusJRoyce
答案 3 :(得分:1)
鉴于您正在使用自定义排序,ListCollectionView
无法知道触发刷新的标准。因此,您需要自己在集合视图上调用Refresh()
。
答案 4 :(得分:1)
在.NET 4.5中,WPF添加了实时整形: Powershell Tutorial
这允许您设置属性以进行排序/分组/过滤,如果属性值发生更改,则将再次对项目进行排序/分组/过滤。就您而言:
// Bound to ListBox.ItemsSource
_items = new ObservableCollection<Item>();
// ...Items are added here ...
// Add sorting
_itemsView = CollectionViewSource.GetDefaultView(_items)
_itemsView.SortDescriptions.Add(new SortDescription("PropertyName", ListSortDirection.Ascending));
// Enable live sorting
// Note that if you don't add any LiveSortingProperties, it will use SortDescriptions
((ICollectionViewLiveShaping)_itemsView).LiveSortingProperties.Add("PropertyName");
((ICollectionViewLiveShaping)_itemsView).IsLiveSorting = true;