使用LINQ对可观察集合进行排序

20

我有一个可观察集合,并使用linq进行排序。一切都很好,但我的问题是如何对实际的可观察集合进行排序?我只是得到了一些IEnumerable的东西,最后不得不清空集合并重新添加内容。这对性能肯定不利。有人知道更好的方法吗?


可能是Sort ObservableCollection<string> C#的重复问题。 - Tim Pohlmann
6个回答

15

如果你正在使用Silverlight 3.0,那么使用CollectionViewSource是最干净的方法。请参考以下示例:(也可以通过xaml完成)

ObservableCollection<DateTime> ecAll = new ObservableCollection<DateTime>();
CollectionViewSource sortedcvs = new CollectionViewSource();
sortedcvs.SortDescriptions.Add(new System.ComponentModel.SortDescription("Date", 
    System.ComponentModel.ListSortDirection.Ascending));
sortedcvs.Source = ecAll;
ListBoxContainer.DataContext = sortedcvs;

同时在对应的XAML中设置

ItemsSource="{Binding}"

对于ListBox或任何派生自ItemsControl的控件


9
由于该集合不提供任何排序机制,因此这可能是最实用的选择。您可以手动使用Move等实现排序,但这可能比以这种方式进行更慢。
    var arr = list.OrderBy(x => x.SomeProp).ToArray();
    list.Clear();
    foreach (var item in arr) {
        list.Add(item);
    }

此外,在排序时(无论采用哪种方法),您可以考虑解绑任何UI元素,这样只需要重新绑定一次即可:
有趣的是,如果使用的是BindingList ,则可以使用RaiseListChangedEvents来最小化通知的数量:
    var arr = list.OrderBy(x => x).ToArray();
    bool oldRaise = list.RaiseListChangedEvents;
    list.RaiseListChangedEvents = false;
    try {
        list.Clear();
        foreach (var item in arr) {
            list.Add(item);
        }
    } finally {
        list.RaiseListChangedEvents = oldRaise;
        if (oldRaise) list.ResetBindings();
    }

如果你要选择这种方式,为了性能,我建议使用 var ordered = list.OrderBy(x => x.SomeProp); list.Clear(); list.AddRange(ordered);。这样可以避免不必要的迭代和执行ToArray(),并且只在AddRange()期间执行一次。 (我需要反编译来验证这一点,因为AddRange接受一个IEnumerable,它甚至可能只存储而不执行它,然后在调用任何List特定操作之前执行和存储扁平化集合。最终具有相同的性能,但它只是将周期命中推迟到稍后。) - JoeBrockhaus

3
请注意,在Linq中,您会从查询中得到一个IEnumerable对象,并且该查询尚未执行。因此,以下代码仅运行一次查询,将其添加到ObservableCollection中:
var query = from x in Data
            where x.Tag == "Something"
            select x;

foreach(var item in query)
    MyObservableCollection.Add(item);

请看IEnumerable上的"OrderBy"扩展:

foreach(var item in query.OrderBy(x => x.Name))
    MyObservableCollection.Add(item);

我将这个作为我的代码类似部分的基础。我开始觉得Linq相当牛逼 :) - djcouchycouch
我不确定如果你从WCF服务获取ObservableCollection,这个方法是否有效,有什么建议吗?我需要在客户端创建一个新的ObservableCollection并对其应用“OrderBy”吗?-谢谢。 - VoodooChild
@VoodooChild:如果你已经有一个ObservableCollection而不仅仅是IEnumerable,那么请看这个SO问题:https://dev59.com/FnNA5IYBdhLWcg3wWscA - Brian Genisio

3
ObservableCollections不是为排序而设计的。List是可排序的,这就是回答引用List.Sort()的基本机制,但ObservableCollection并不是从List派生的,所以你在那里没有机会。在我看来,“正确”的解决方案不是尝试对ObservableCollection进行排序,而是实现ICollectionView并将其绑定到您的控件上。该接口添加了排序方法,并具有其它好处,它被Silverlight控件(只有支持它的控件,如DataGrid)识别,因此您的排序可以直接从UI层使用。这个问题可能会有所帮助:Silverlight和ICollectionView

0

你必须在Terms属性上引发一个PropertyChanged事件。 - Jack

0

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接