2011-09-02 103 views
9

我有一个ObservableCollection和一个WPF UserControl是Databound它。 Control是一个图表,显示ObservableCollection中每个BarData类型项目的垂直条。如何排序ObservableCollection

ObservableCollection<BarData> 

class BarData 
{ 
    public DateTime StartDate {get; set;} 
    public double MoneySpent {get; set;} 
    public double TotalMoneySpentTillThisBar {get; set;} 
} 

现在我想整理出基于起始日期的的ObservableCollection,以便BarData的将在集合中增加起始日期的顺序。 然后,我可以在这样每个BarData计算TotalMoneySpentTillThisBar的价值观 -

var collection = new ObservableCollection<BarData>(); 
//add few BarData objects to collection 
collection.Sort(bar => bar.StartData); // this is ideally the kind of function I was looking for which does not exist 
double total = 0.0; 
collection.ToList().ForEach(bar => { 
            bar.TotalMoneySpentTillThisBar = total + bar.MoneySpent; 
            total = bar.TotalMoneySpentTillThisBar; 
            } 
          ); 

我知道我可以使用ICollectionView进行排序,对veiwing过滤数据,但不会改变实际的集合。我需要对实际集合进行排序,以便为每个项目计算TotalMoneySpentTillThisBar。它的价值取决于收集项目的顺序。

谢谢。

+0

这是一次性任务,即在收集绑定到控件之前可以完成的任务吗? –

+0

集合即使在绑定时也会保持更改(这就是我使用ObservableCollection的原因,以便在集合更改时更新UI)。解决这个问题的一个选择是在向收藏中添加项目时处理它,以确保按照排序顺序将其插入到适当的索引中,或者第二个选项是在添加或删除项目时对收集进行排序。我想在这里评估第二个选项。 –

+1

在我看来,这是一个设计缺陷,对象本身知道到现在为止花了多少钱,而且这些信息取决于排序。这应该是用户控件中的一个功能('ShowTotal = true')。 –

回答

10

对ObservableCollection进行排序的问题在于,每次更改集合时,都会触发事件。所以对于一种从一个位置移除物品并将它们添加到另一个位置的行为,最终会发生大量事件。

我认为你最好打赌就是只要按正确的顺序插入ObservableCollection开始。从集合中删除项目不会影响订购。我刮起了快速的扩展方法来说明

public static void InsertSorted<T>(this ObservableCollection<T> collection, T item, Comparison<T> comparison) 
    { 
     if (collection.Count == 0) 
      collection.Add(item); 
     else 
     { 
      bool last = true; 
      for (int i = 0; i < collection.Count; i++) 
      { 
       int result = comparison.Invoke(collection[i], item); 
       if (result >= 1) 
       { 
        collection.Insert(i, item); 
        last = false; 
        break; 
       } 
      } 
      if (last) 
       collection.Add(item); 
     } 
    } 

所以,如果你使用的字符串(例如),代码看起来像这样

 ObservableCollection<string> strs = new ObservableCollection<string>(); 
     Comparison<string> comparison = new Comparison<string>((s1, s2) => { return String.Compare(s1, s2); }); 
     strs.InsertSorted("Mark", comparison); 
     strs.InsertSorted("Tim", comparison); 
     strs.InsertSorted("Joe", comparison); 
     strs.InsertSorted("Al", comparison); 

编辑

你可以如果扩展ObservableCollection并提供您自己的插入/添加方法,请保持呼叫相同。类似这样的:

public class BarDataCollection : ObservableCollection<BarData> 
{ 
    private Comparison<BarData> _comparison = new Comparison<BarData>((bd1, bd2) => { return DateTime.Compare(bd1.StartDate, bd2.StartDate); }); 

    public new void Insert(int index, BarData item) 
    { 
     InternalInsert(item); 
    } 

    protected override void InsertItem(int index, BarData item) 
    { 
     InternalInsert(item); 
    } 

    public new void Add(BarData item) 
    { 
     InternalInsert(item); 
    } 

    private void InternalInsert(BarData item) 
    { 
     if (Items.Count == 0) 
      Items.Add(item); 
     else 
     { 
      bool last = true; 
      for (int i = 0; i < Items.Count; i++) 
      { 
       int result = _comparison.Invoke(Items[i], item); 
       if (result >= 1) 
       { 
        Items.Insert(i, item); 
        last = false; 
        break; 
       } 
      } 
      if (last) 
       Items.Add(item); 
     } 
    } 
} 

插入索引被忽略。

 BarData db1 = new BarData(DateTime.Now.AddDays(-1)); 
     BarData db2 = new BarData(DateTime.Now.AddDays(-2)); 
     BarData db3 = new BarData(DateTime.Now.AddDays(1)); 
     BarData db4 = new BarData(DateTime.Now); 
     BarDataCollection bdc = new BarDataCollection(); 
     bdc.Add(db1); 
     bdc.Insert(100, db2); 
     bdc.Insert(1, db3); 
     bdc.Add(db4); 
+0

是的,我有这个作为第一个选项插入在正确的索引,但认为这会引入相当多的复杂性,同时添加一个项目收集。但是,关于大量事件被解雇的观点是真实的,可能我应该重新考虑这一点。 –

+0

如果您希望调用保持不变,则可以扩展ObserableCollection。我添加了代码。 – mdm20

13

我刚刚创建一个扩展ObservableCollection,因为随着时间的推移,我也想,我已经习惯了从ListContainsIndexOfAddRangeRemoveRange等)使用其他功能

我通常使用的东西像

MyCollection.Sort(p => p.Name);

这里是我的排序implementa重刑

/// <summary> 
/// Expanded ObservableCollection to include some List<T> Methods 
/// </summary> 
[Serializable] 
public class ObservableCollectionEx<T> : ObservableCollection<T> 
{ 

    /// <summary> 
    /// Constructors 
    /// </summary> 
    public ObservableCollectionEx() : base() { } 
    public ObservableCollectionEx(List<T> l) : base(l) { } 
    public ObservableCollectionEx(IEnumerable<T> l) : base(l) { } 

    #region Sorting 

    /// <summary> 
    /// Sorts the items of the collection in ascending order according to a key. 
    /// </summary> 
    /// <typeparam name="TKey">The type of the key returned by <paramref name="keySelector"/>.</typeparam> 
    /// <param name="keySelector">A function to extract a key from an item.</param> 
    public void Sort<TKey>(Func<T, TKey> keySelector) 
    { 
     InternalSort(Items.OrderBy(keySelector)); 
    } 

    /// <summary> 
    /// Sorts the items of the collection in descending order according to a key. 
    /// </summary> 
    /// <typeparam name="TKey">The type of the key returned by <paramref name="keySelector"/>.</typeparam> 
    /// <param name="keySelector">A function to extract a key from an item.</param> 
    public void SortDescending<TKey>(Func<T, TKey> keySelector) 
    { 
     InternalSort(Items.OrderByDescending(keySelector)); 
    } 

    /// <summary> 
    /// Sorts the items of the collection in ascending order according to a key. 
    /// </summary> 
    /// <typeparam name="TKey">The type of the key returned by <paramref name="keySelector"/>.</typeparam> 
    /// <param name="keySelector">A function to extract a key from an item.</param> 
    /// <param name="comparer">An <see cref="IComparer{T}"/> to compare keys.</param> 
    public void Sort<TKey>(Func<T, TKey> keySelector, IComparer<TKey> comparer) 
    { 
     InternalSort(Items.OrderBy(keySelector, comparer)); 
    } 

    /// <summary> 
    /// Moves the items of the collection so that their orders are the same as those of the items provided. 
    /// </summary> 
    /// <param name="sortedItems">An <see cref="IEnumerable{T}"/> to provide item orders.</param> 
    private void InternalSort(IEnumerable<T> sortedItems) 
    { 
     var sortedItemsList = sortedItems.ToList(); 

     foreach (var item in sortedItemsList) 
     { 
      Move(IndexOf(item), sortedItemsList.IndexOf(item)); 
     } 
    } 

    #endregion // Sorting 
} 
+4

无论何时调用Sort(),您都将收到集合中每个项目的CollectionChanged事件... – mdm20

+0

可以在排序过程中禁用通知吗? –

+0

@romkyns。我不知道ObservableCollection直接支持BindingList的方式禁用通知的能力(即list.RaiseListChangedEvents = false)。但是你可以在排序之前注销你的处理程序,然后重新注册。 – Berryl

26

hummm第一个问题,我对你是: 有什么重要的是你的ObservableCollection进行排序,或者是你真正想要的是有分类的图形用户界面中显示?

我假定目标是有一个排序显示,将“实时”更新。然后我看到2个解决方案

  1. 让你ObservableCollectionICollectionView,并对其进行排序,这里 http://marlongrech.wordpress.com/2008/11/22/icollectionview-explained/

  2. 解释绑定您的ObservableCollectionCollectionViewsource,就可以添加排序,然后使用该CollectionViewSource作为ListViewItemSource

即:

添加这个命名空间

xmlns:scm="clr-namespace:System.ComponentModel;assembly=WindowsBase" 

然后

<CollectionViewSource x:Key='src' Source="{Binding MyObservableCollection, ElementName=MainWindowName}"> 
    <CollectionViewSource.SortDescriptions> 
     <scm:SortDescription PropertyName="MyField" /> 
    </CollectionViewSource.SortDescriptions> 

</CollectionViewSource> 

并绑定这样

<ListView ItemsSource="{Binding Source={StaticResource src}}" > 
+0

确定我完全忽略了一点遗憾... – Gregfr

+0

然后尝试回答你的问题,我想我会创建一个从继承的ObservableCollection一个新的类。然后我会重写构造函数重新计算每个项目的TotalMoneySpentTillThisBar。像 的foreach项目得到的东西与stardate项目的集合越快,目前的一个,做的总和,并更新电流。 然后覆盖Add()方法与集合中添加的每个新的实例类似的机制,并使用ICollectionViewSource排序排序之外的ObservableCollection显示 – Gregfr

+0

+1 - 这是它应该怎么做。 – Doug

1

那么排序在不同的集合使用LINQ的数据:这为我工作

var collection = new List<BarData>(); 
//add few BarData objects to collection 

// sort the data using LINQ 
var sorted = from item in collection orderby item.StartData select item; 

// create observable collection 
var oc = new ObservableCollection<BarData>(sorted); 

0

同样使用LINQ/Extension方法,可以通过不设置源col到已排序的源来排除NotifyPropertyChanged事件,但清除原始并添加已排序项的项目。 (如果实施,这将继续触发Collectionchanged事件)。

<Extension> 
Public Sub SortByProp(Of T)(ByRef c As ICollection(Of T), PropertyName As String) 
    Dim l = c.ToList 
    Dim sorted = l.OrderBy(Function(x) x.GetType.GetProperty(PropertyName).GetValue(x)) 

    c.Clear() 
    For Each i In sorted 
     c.Add(i) 
    Next 

End Sub 
相关问题