引用其他集合项的集合

本文关键字:集合 引用 其他 | 更新日期: 2023-09-27 18:33:10

我试图弄清楚是否有办法在 C# 4.0 中执行以下操作:

我有一个包含大量自定义类的 ObservableCollection - 我们称之为"MainCollection"。 我的大部分代码都会持续更新 MainCollection 中这些类的值,这些值都运行良好。

我现在需要创建一个"集合"[或要在 WPF DataGrid DataContext 绑定中使用的内容],它只是将 MainCollection 中的类分组到基础类中的单个参数上。

有没有办法做到这一点,每当 MainCollection 中的项目更新时,这个新的"伪集合"也是如此。

引用其他集合项的集合

也许您正在寻找 CollectionView 类:

表示用于对数据集合进行分组、排序、筛选和导航的视图。

然而

不应在代码中创建此类的对象。若要为仅实现 IEnumerable 的集合创建集合视图,请创建一个 CollectionViewSource 对象,将集合添加到 Source 属性,然后从 View 属性获取集合视图。

因此,也许最好的起点是"如何:在 XAML 中使用视图对数据进行排序和分组"。 本文和其他一些操作方法文章可以在 CollectionView 页面的底部找到。

有一些开源框架可以实现这一点。我已经使用BindableLinq取得了一些成功。尽管如主页所述,开发已经停滞不前,并且还有其他框架替代方案。

这些库旨在当多个级别的依赖项更新时提供更新(例如,集合本身,或集合所依赖的项的属性,甚至是外部依赖项)。

是否可以简单地公开按需创建新集合的属性? 类似的东西

public List<Whatever> Items
{
    get
    {
        return MainCollection.Where( x => [someCondition] ).ToList();
    }
}

更新主集合以添加此分组功能

class MainCollection 
{
  public Dictionary<TGroupBySingleParameter, TValue> TheLookup{get; private set;}
  Update()
  {
        TheLookup.Add(//...
        //do work
  }
}

换句话说,您希望创建MainCollection的视图。这听起来像是 LINQ 的工作!

   var newCollection = from item in MainCollection
        group item  by /*item condintion */ into g //use where for filtering
         select new { Prop = g.Prop, Item = g };  

如果您需要可观察的,那么只需将序列传递给 ctor:

var observableColl = new ObservableCollection(newCollection);

您可以创建一个 FilteredCollection,该集合侦听更改事件以更新筛选的集合。这允许在源集合发生更改时进行有效的重新筛选。

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.Linq;
namespace ConsoleApplication26
{
    class FilteredObservableCollection<T> : INotifyCollectionChanged, IEnumerable<T>
    {
        List<T> _FilteredCached;
        ObservableCollection<T> Source;
        Func<T,bool> Filter;
        public FilteredObservableCollection(ObservableCollection<T> source, Func<T,bool> filter)
        {
            Source = source;
            Filter = filter;
            source.CollectionChanged += source_CollectionChanged;
        }
        void source_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
        {
            if (e.Action == NotifyCollectionChangedAction.Add)
            {
                var addedMatching = e.NewItems.Cast<T>().Where(Filter).ToList();
                _FilteredCached.AddRange(addedMatching);
                if (addedMatching.Count > 0)
                {
                    CollectionChanged(sender, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, addedMatching));
                }
            }
            else // make life easy and refresh fully
            {
                _FilteredCached = null;
                CollectionChanged(sender, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
            }
        }
        public IEnumerator<T> GetEnumerator()
        {
            if (_FilteredCached == null)
            {
                _FilteredCached = Source.Where(Filter).ToList(); // make it easy to get right. If someone would call e.g. First() only 
                // we would end up with an incomplete filtered collection.
            }
            foreach (var filtered in _FilteredCached)
            {
                yield return filtered;
            }
        }
        System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
        {
            return GetEnumerator();
        }
        public event NotifyCollectionChangedEventHandler CollectionChanged = (o,e) => { };
    }

    class Program
    {
        static void Main(string[] args)
        {
            ObservableCollection<int> data = new ObservableCollection<int>(new int[] { 1, 2, 3, 4, 1 });
            var filteredObservable = new FilteredObservableCollection<int>(data, x => x > 2);
            Print(filteredObservable); // show that filter works
            data.Add(1);
            Print(filteredObservable); // no change
            data.Add(10);
            Print(filteredObservable); // change
            data.Clear();
            Print(filteredObservable); // collection is empy
            data.Add(5);
            Print(filteredObservable); // add item in filter range
            data[0] = 1;
            Print(filteredObservable); // replace it
        }
        static void Print<T>(FilteredObservableCollection<T> coll)
        {
            Console.WriteLine("Filtered: {0}", String.Join(",", coll));
        }
    }
}

这将打印

Filtered: 3,4
Filtered: 3,4
Filtered: 3,4,10
Filtered:
Filtered: 5
Filtered: