wpf 如何对ObservableCollection进行排序

dy1byipe  于 2023-01-18  发布在  其他
关注(0)|答案(7)|浏览(432)

我有一个ObservableCollection和一个WPF UserControl数据绑定到它。该控件是一个图形,为ObservableCollection中BarData类型的每个项显示一个垂直条。

ObservableCollection<BarData>

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

现在我想根据StartDate对ObservableCollection进行排序,这样BarData在集合中的顺序就会从StartDate开始,然后我就可以计算每个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来排序、筛选数据以便查看,但这不会更改实际的集合。我需要对实际的集合进行排序,以便可以计算每个项目的TotalMoneySpentTillThisBar。它的值取决于集合中项目的顺序。
谢谢。

laawzig2

laawzig21#

嗯,我想问你第一个问题是:对ObservableCollection进行排序真的很重要吗?还是您真正想要的是对GUI中的显示进行排序?
我假设目标是有一个排序的显示,将更新“真实的”。然后我看到2个解决方案
1.获取ObservableCollectionICollectionView并对其进行排序,如此处所述http://marlongrech.wordpress.com/2008/11/22/icollectionview-explained/
1.将您ObservableCollection绑定到CollectionViewsource,在其上添加排序,然后将该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}}" >
xu3bshqb

xu3bshqb2#

我刚刚创建了一个扩展ObservableCollection的类,因为随着时间的推移,我还希望使用其他我习惯于使用的List功能(ContainsIndexOfAddRangeRemoveRange等)
我通常用它来搭配
MyCollection.Sort(p => p.Name);
下面是我的排序实现

/// <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
}
yuvru6vn

yuvru6vn3#

对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并提供自己的insert/add方法,就可以保持调用的一致性。

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);
ewm0tg9j

ewm0tg9j4#

如何使用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);

这对我很有效。

m528fe3b

m528fe3b5#

同样使用LINQ/Extensionmethod,可以通过不将源列设置为已排序的列来避免触发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
nnt7mjpx

nnt7mjpx6#

我知道这是老帖子,但是我对大多数的解决方案都不满意,因为它破坏了绑定。所以如果有人遇到,这就是我所做的。你可以为更多的属性排序创建更多的重载。
这不会破坏绑定。

public static void AddRangeSorted<T, TSort>(this ObservableCollection<T> collection, IEnumerable<T> toAdd, Func<T, TSort> sortSelector, OrderByDirection direction)
    {
        var sortArr = Enumerable.Concat(collection, toAdd).OrderBy(sortSelector, direction).ToList();
        foreach (T obj in toAdd.OrderBy(o => sortArr.IndexOf(o)).ToList())
        {
            collection.Insert(sortArr.IndexOf(obj), obj);
        }
    }

    public static void AddRangeSorted<T, TSort, TSort2>(this ObservableCollection<T> collection, IEnumerable<T> toAdd, Func<T, TSort> sortSelector, OrderByDirection direction, Func<T, TSort2> sortSelector2, OrderByDirection direction2)
    {
        var sortArr = Enumerable.Concat(collection, toAdd).OrderBy(sortSelector, direction).ThenBy(sortSelector2, direction2).ToList();
        foreach (T obj in toAdd.OrderBy(o=> sortArr.IndexOf(o)).ToList())
        {
            collection.Insert(sortArr.IndexOf(obj), obj);
        }
    }

和用法:

OrderLines.AddRangeSorted(toAdd,ol=>ol.ID, OrderByDirection.Ascending);
l7mqbcuq

l7mqbcuq7#

我用的是VMMV模型。你可以把这段代码添加到一个事件或者btn或者其他什么东西中。它会根据参数(int,string...)-sortVar对你的集合进行排序。

OrgCollection = new ObservableCollection<ClassModel>();
        var sortedCollection = new ObservableCollection<ClassModel>(OrgCollection.OrderBy(x => x.sortVar));
        OrgCollection.Clear();
        foreach (var x in sortedCollection)
        {
            ClassModel modelToSort = new ClassModel()
            {// 'var y in ClassModel' = x.'var y in ClassModel'
             // e.g. VarName = x.VarName, ...
            };
            OrgCollection.Add(modelToSort);
        }

相关问题