WPF MVVM如何在ICollectionView中组合多个过滤器

时间:2017-01-15 09:57:52

标签: c# wpf mvvm

我有一个看似相当容易的问题,但似乎我遇到了问题:

我有一个包含一些数据的数据网格:

            <DataGrid ItemsSource="{Binding Candidates, IsAsync=True}" 
                      AutoGenerateColumns="False" 
                      EnableColumnVirtualization="True" 
                      EnableRowVirtualization="True"
                      VirtualizingStackPanel.VirtualizationMode="Standard"
                      VirtualizingStackPanel.IsVirtualizing="True"
                      CanUserAddRows="false">

                <DataGrid.Columns>
                    <DataGridTextColumn Binding="{Binding Firstname}" Header="Imię" />
                    <DataGridTextColumn Binding="{Binding Lastname}" Header="Nazwisko" />
                    <DataGridTextColumn Binding="{Binding commendation.Name}" Header="Polecenie" />
  </DataGrid.Columns>
</DataGrid>

现在使用mvvm和命令我在这些上应用了一些过滤器,它看起来像:

    public CatalogViewModel()
    {
        this._catalog = new CatalogContexct();
        this._candidates = this._catalog.Candidates.Include("commendation").ToList();

        var candidates = new ListCollectionView(this._candidates);

        this.Candidates = CollectionViewSource.GetDefaultView(candidates);

        this.FirstameCommand = new RelyCommand(FilterFirstname, param => this._canExecute);
        this.LastnameCommand = new RelyCommand(FilterLastname, param => this._canExecute);
        this.CommendationCommand = new RelyCommand(FilterCommendation, param => this._canExecute);
    }

过滤器看起来像:

    public void FilterFirstname(object obj)
    {
        this.Candidates.Filter += item =>
        {
            Candidate candidate = item as Candidate;
            return candidate.Firstname.Contains(obj.ToString());
        };

        this.Candidates.Refresh();
    }

    public void FilterLastname(object obj)
    {
        this.Candidates.Filter += item =>
        {
            Candidate candidate = item as Candidate;

            if(string.IsNullOrWhiteSpace(candidate.Lastname))
            {
                return false;
            }

            return candidate.Lastname.Contains(obj.ToString());
        };

        this.Candidates.Refresh();
    }

    public void FilterCommendation(object obj)
    {
        this.Candidates.Filter += item =>
        {
            Candidate candidate = item as Candidate;

            if (string.IsNullOrWhiteSpace(candidate.commendation.Name))
            {
                return false;
            }

            return candidate.commendation.Name.Contains(obj.ToString());
        };

        this.Candidates.Refresh();
    }

现在这基本上按预期工作但是它分别对每个列起作用,所以如果我输入名称它将过滤我的名字,如果我输入姓氏,它将过滤我的姓氏,但它将覆盖名称,所以我不会得到确切的匹配名称和姓氏,我想汇总这些过滤器。

有没有办法聚合过滤器?

1 个答案:

答案 0 :(得分:3)

为什么不起作用?

在每个FilterCommendationFilterLastnameFilterFirstname上,您要向this.Candidates.Filter添加另一个代理。所有这些都将在Refresh上执行,但只返回最后一个委托的结果。请考虑以下示例:

Predicate<int> tmp = i => true;
tmp += i => i != 0;

Assert.AreEqual(true, tmp(1));
Assert.AreEqual(false, tmp(0));

它将始终返回i != 0,因为它是最后一个谓词,但所有这些都将被执行。他们之间没有'和'。

如何实现目标?

您可以创建过滤器集合并在其中添加或删除过滤器并刷新视图。完整示例如下:

    Dictionary<string, Predicate<Candidate>> filters
        = new Dictionary<string, Predicate<Candidate>>();

    public CatalogViewModel()
    {
        ...

        Candidates.Filter = FilterCandidates;
    }

    private bool FilterCandidates(object obj)
    {
        Candidate c = (Candidate)obj;
        return filters.Values
            .Aggregate(true,
                (prevValue, predicate) => prevValue && predicate(c));
    }
    public void FilterFirstname(object obj)
    {
        string val = obj.ToString();
        AddFilterAndRefresh(
            "FirstName",
            candidate => candidate.Firstname.Contains(val));
    }

    public void FilterLastname(object obj)
    {
        string val = obj.ToString();
        AddFilterAndRefresh(
            "FirstName",
            candidate => !string.IsNullOrWhiteSpace(candidate.Lastname) && candidate.Lastname.Contains(val));
    }

    public void ClearFilters()
    {
        filters.Clear();
        Candidates.Refresh();
    }

    public void RemoveFilter(string filterName)
    {
        if (filters.Remove(filterName))
        {
            Candidates.Refresh();
        }
    }

    private void AddFilterAndRefresh(string name, Predicate<Candidate> predicate)
    {
        filters.Add(name, predicate);
        Candidates.Refresh();
    }