2011-09-02 36 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 
相關問題