【问题标题】:Filter a collection with LINQ vs CollectionView使用 LINQ 与 CollectionView 过滤集合
【发布时间】:2011-04-21 12:18:36
【问题描述】:

我想在具有 6 列的 DataGrid 中过滤最多 3000 个项目的 ObservableCollection。用户应该能够以“&&”方式过滤所有 6 列。

我应该使用 LINQ 还是 CollectionView? LINQ 似乎更快地尝试了一些 www 示例。你有什么优点/缺点吗?

更新

private ObservableCollection<Material> _materialList;
        private ObservableCollection<Material> _materialListInternal;

        public MaterialBrowserListViewModel()
        {           
              _materialListInternal = new ObservableCollection<Material>();          

            for (int i = 0; i < 2222; i++)
            {
                var mat = new Material()
                {
                    Schoolday = DateTime.Now.Date,
                    Period = i,
                    DocumentName = "Excel Sheet" + i,
                    Keywords = "financial budget report",
                    SchoolclassCode = "1",
                };
                _materialListInternal.Add(mat);
                var mat1 = new Material()
                {
                    Schoolday = DateTime.Now.Date,
                    Period = i,
                    DocumentName = "Word Doc" + i,
                    Keywords = "Economical staticstics report",
                    SchoolclassCode = "2",
                };
                _materialListInternal.Add(mat1);
            }

            MaterialList = CollectionViewSource.GetDefaultView(MaterialListInternal);
            MaterialList.Filter = new Predicate<object>(ContainsInFilter); 
        }      

        public bool ContainsInFilter(object item)
        {
            if (String.IsNullOrEmpty(FilterKeywords))
                return true;   

            Material material = item as Material;
            if (DocumentHelper.ContainsCaseInsensitive(material.Keywords,FilterKeywords,StringComparison.CurrentCultureIgnoreCase))        
                return true;          
            else          
                return false;                     
        }

        private string _filterKeywords;
        public string FilterKeywords
        {
            get { return _filterKeywords; }
            set
            {
                if (_filterKeywords == value)
                    return;

                _filterKeywords = value;
                this.RaisePropertyChanged("FilterKeywords");
                MaterialList.Refresh();               
            }
        }

        public ICollectionView MaterialList { get; set; }

        public ObservableCollection<Material> MaterialListInternal
        {
            get { return _materialListInternal; }
            set
            {
                _materialListInternal = value;
                this.RaisePropertyChanged("MaterialList");
            }
        } 

【问题讨论】:

    标签: wpf linq datagrid filter nscollectionview


    【解决方案1】:
    • 当您调用 Refresh 时,使用 ICollectionView 会自动通知您收集更改。使用 LINQ,您需要在需要重新运行过滤器以更新 UI 时触发自己的更改通知。不难,但比仅仅调用 Refresh 需要更多的思考。

    • LINQ 比 ICollectionView 使用的简单是/否过滤更灵活,但如果您不做复杂的事情,那么这种灵活性并没有任何优势。

    • 正如 Henk 所说,在 UI 中不应该有明显的性能差异。

    【讨论】:

      【解决方案2】:

      对于交互式(DataGrid?)体验,您可能应该使用 CollectionView。对于更面向代码的排序,LINQ。

      对于最多 3000 个项目,速度不应该是 UI 中的(主要)因素。

      【讨论】:

      • 我目前只使用一列的 CollectionViewSource 过滤器。你知道为什么只有当我将 .Refresh() 方法放在下面的属性中时才会更新集合吗?私有字符串 _filterKeywords 公共字符串 FilterKeywords { get { return _filterKeywords; } set { if (_filterKeywords == value) return; _filterKeywords = 值; this.RaisePropertyChanged("FilterKeywords"); MaterialList.Refresh(); } }
      • “你知道为什么......” - 不,但也许如果你将代码添加到问题中,我可能能够阅读它。
      【解决方案3】:

      两者如何? Thomas Levesque 围绕 ICollectionView 构建了一个支持 LINQ 的包装器。

      用法:

      IEnumerable<Person> people;
      
      // Using query comprehension
      var query =
          from p in people.ShapeView()
          where p.Age >= 18
          orderby p.LastName, p.FirstName
          group p by p.Country;
      
      query.Apply();
      
      // Using extension methods
      people.ShapeView()
            .Where(p => p.Age >= 18)
            .OrderBy(p => p.LastName)
            .ThenBy(p => p.FirstName)
            .Apply();
      

      代码:

      public static class CollectionViewShaper
      {
          public static CollectionViewShaper<TSource> ShapeView<TSource>(this IEnumerable<TSource> source)
          {
              var view = CollectionViewSource.GetDefaultView(source);
              return new CollectionViewShaper<TSource>(view);
          }
      
          public static CollectionViewShaper<TSource> Shape<TSource>(this ICollectionView view)
          {
              return new CollectionViewShaper<TSource>(view);
          }
      }
      
      public class CollectionViewShaper<TSource>
      {
          private readonly ICollectionView _view;
          private Predicate<object> _filter;
          private readonly List<SortDescription> _sortDescriptions = new List<SortDescription>();
          private readonly List<GroupDescription> _groupDescriptions = new List<GroupDescription>();
      
          public CollectionViewShaper(ICollectionView view)
          {
              if (view == null)
                  throw new ArgumentNullException("view");
              _view = view;
              _filter = view.Filter;
              _sortDescriptions = view.SortDescriptions.ToList();
              _groupDescriptions = view.GroupDescriptions.ToList();
          }
      
          public void Apply()
          {
              using (_view.DeferRefresh())
              {
                  _view.Filter = _filter;
                  _view.SortDescriptions.Clear();
                  foreach (var s in _sortDescriptions)
                  {
                      _view.SortDescriptions.Add(s);
                  }
                  _view.GroupDescriptions.Clear();
                  foreach (var g in _groupDescriptions)
                  {
                      _view.GroupDescriptions.Add(g);
                  }
              }
          }
      
          public CollectionViewShaper<TSource> ClearGrouping()
          {
              _groupDescriptions.Clear();
              return this;
          }
      
          public CollectionViewShaper<TSource> ClearSort()
          {
              _sortDescriptions.Clear();
              return this;
          }
      
          public CollectionViewShaper<TSource> ClearFilter()
          {
              _filter = null;
              return this;
          }
      
          public CollectionViewShaper<TSource> ClearAll()
          {
              _filter = null;
              _sortDescriptions.Clear();
              _groupDescriptions.Clear();
              return this;
          }
      
          public CollectionViewShaper<TSource> Where(Func<TSource, bool> predicate)
          {
              _filter = o => predicate((TSource)o);
              return this;
          }
      
          public CollectionViewShaper<TSource> OrderBy<TKey>(Expression<Func<TSource, TKey>> keySelector)
          {
              return OrderBy(keySelector, true, ListSortDirection.Ascending);
          }
      
          public CollectionViewShaper<TSource> OrderByDescending<TKey>(Expression<Func<TSource, TKey>> keySelector)
          {
              return OrderBy(keySelector, true, ListSortDirection.Descending);
          }
      
          public CollectionViewShaper<TSource> ThenBy<TKey>(Expression<Func<TSource, TKey>> keySelector)
          {
              return OrderBy(keySelector, false, ListSortDirection.Ascending);
          }
      
          public CollectionViewShaper<TSource> ThenByDescending<TKey>(Expression<Func<TSource, TKey>> keySelector)
          {
              return OrderBy(keySelector, false, ListSortDirection.Descending);
          }
      
          private CollectionViewShaper<TSource> OrderBy<TKey>(Expression<Func<TSource, TKey>> keySelector, bool clear, ListSortDirection direction)
          {
              string path = GetPropertyPath(keySelector.Body);
              if (clear)
                  _sortDescriptions.Clear();
              _sortDescriptions.Add(new SortDescription(path, direction));
              return this;
          }
      
          public CollectionViewShaper<TSource> GroupBy<TKey>(Expression<Func<TSource, TKey>> keySelector)
          {
              string path = GetPropertyPath(keySelector.Body);
              _groupDescriptions.Add(new PropertyGroupDescription(path));
              return this;
          }
      
          private static string GetPropertyPath(Expression expression)
          {
              var names = new Stack<string>();
              var expr = expression;
              while (expr != null && !(expr is ParameterExpression) && !(expr is ConstantExpression))
              {
                  var memberExpr = expr as MemberExpression;
                  if (memberExpr == null)
                      throw new ArgumentException("The selector body must contain only property or field access expressions");
                  names.Push(memberExpr.Member.Name);
                  expr = memberExpr.Expression;
              }
              return String.Join(".", names.ToArray());
          }
      }
      

      信用: http://www.thomaslevesque.com/2011/11/30/wpf-using-linq-to-shape-data-in-a-collectionview/

      【讨论】:

        【解决方案4】:

        基于视觉复杂性和项目数量,确实会有明显的性能差异,因为 Refresh 方法会重新创建整个视图!!!

        【讨论】:

          【解决方案5】:

          您需要我的ObservableComputations 库。使用这个库,您可以像这样编写代码:

          ObservableCollection<Material> MaterialList = MaterialListInternal.Filtering(m => 
               String.IsNullOrEmpty(FilterKeywords) 
               || DocumentHelper.ContainsCaseInsensitive(
                   material.Keywords, FilterKeywords, StringComparison.CurrentCultureIgnoreCase));
          

          MaterialList 反映了 MaterialListInternal 集合中的所有变化。不要忘记将INotifyPropertyChanged 接口的实现添加到Material 类中,以便MaterialList 集合反映material.Keywords 属性的变化。

          【讨论】:

            猜你喜欢
            • 2012-04-08
            • 1970-01-01
            • 2022-11-26
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2012-03-03
            相关资源
            最近更新 更多