使用LINQ vs CollectionView过滤集合

时间:2010-10-05 20:26:23

标签: wpf linq datagrid filter nscollectionview

我想在一个包含6列的DataGrid中使用max 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");
            }
        } 

5 个答案:

答案 0 :(得分:3)

  • 使用ICollectionView可以在调用Refresh时自动收集更改的通知。使用LINQ时,您需要在需要重新运行过滤器以更新UI时触发自己的更改通知。并不困难,但需要更多的思考,而不仅仅是调用刷新。

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

  • 正如Henk所说,用户界面不应该有明显的性能差异。

答案 1 :(得分:1)

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

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

答案 2 :(得分:1)

两者怎么样? 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/

答案 3 :(得分:0)

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

答案 4 :(得分:0)

您需要我的ObservableComputations库。使用该库,您可以像这样进行编码:

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

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