2017-01-15 2 views
1

У меня есть вопрос, который кажется довольно легко, но это, кажется, у меня есть проблема с этим:WPF MVVM Как объединить несколько фильтров в ICollectionView

У меня есть сетка данных с некоторыми данными:

  <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

Почему это не работает?

На каждом FilterCommendation, FilterLastname, FilterFirstname вы добавляете еще один делегат 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(); 
    } 
+0

Как будет выглядеть свойство фильтра? –

+0

Я только что добавил определение поля 'filters'. – Kedrzu

+0

Он отлично работает, спасибо –

Смежные вопросы