【问题标题】:Collection that references items of other collection引用其他集合的项目的集合
【发布时间】:2012-05-02 20:00:21
【问题描述】:

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

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

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

有没有办法做到这一点,只要 MainCollection 中的项目更新,这个新的“伪集合”也会更新。

【问题讨论】:

  • 请不要在标题前加上“C# -”之类的前缀。这就是标签的用途。

标签: c# collections


【解决方案1】:

也许您正在寻找CollectionView class

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

然而,

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

所以也许最好的起点是How to: Sort and Group Data Using a View in XAML。可以在 CollectionView 页面底部找到这篇文章和其他一些操作指南文章。

【讨论】:

  • 现在看看 - 看起来很有希望!
【解决方案2】:

有一些开源框架可以实现这一目标。我使用BindableLinq 取得了一些成功。虽然正如主页上所指出的那样,开发已经停滞不前,并且还有其他框架替代方案。

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

【讨论】:

    【解决方案3】:

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

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

    【讨论】:

    【解决方案4】:

    更新 MainCollection 以添加此分组功能

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

    【讨论】:

      【解决方案5】:

      换句话说,您想创建 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 };  
      

      如果您需要 observable,那么只需将序列传递给 ctor:

      var observableColl = new ObservableCollection(newCollection);
      

      【讨论】:

      • 谢谢 lukas - 如果我这样做,如果 MainCollection 更新,observableColl 会更新吗?
      • 不,不会。必须再次运行查询。
      【解决方案6】:

      您可以创建一个 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:
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2017-10-01
        • 2018-05-05
        • 2015-04-06
        • 1970-01-01
        • 2019-07-12
        • 2016-02-25
        • 2018-06-11
        • 1970-01-01
        相关资源
        最近更新 更多