引用其他集合项的集合
本文关键字:集合 引用 其他 | 更新日期: 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: