WPF Использование нескольких фильтров в одном ListCollectionView - PullRequest
12 голосов
/ 20 декабря 2009

Я использую шаблон проектирования MVVM с ListView, привязанным к ListCollectionView в ViewModel. У меня также есть несколько комбинированных списков, которые используются для фильтрации ListView. Когда пользователь выбирает элемент из выпадающего списка, ListView фильтруется для выбранного элемента. Всякий раз, когда я хочу фильтровать поверх того, что уже отфильтровано, он отменяет мой предыдущий фильтр, как будто этого никогда не происходило. То же самое относится и к удалению фильтра. Удаление фильтра для одного выпадающего списка удаляет все фильтры и отображает исходный список. Можно ли иметь несколько отдельных фильтров в одном ListCollectionView?

Я что-то не так делаю, или это просто не поддерживается? Вы можете найти снимок экрана моего приложения здесь , чтобы увидеть, чего я пытаюсь достичь. Вот мой код для фильтрации ...

    /// <summary>
    /// Filter the list
    /// </summary>
    /// <param name="filter">Criteria and Item to filter the list</param>
    [MediatorMessageSink("FilterList", ParameterType = typeof(FilterItem))]
    public void FilterList(FilterItem filter)
    {
        // Make sure the list can be filtered...
        if (Products.CanFilter)
        {
            // Now filter the list
            Products.Filter = delegate(object obj)
            {
                Product product = obj as Product;

                // Make sure there is an object
                if (product != null)
                {
                    bool isFiltered = false;
                    switch (filter.FilterItemName)
                    {
                        case "Category":
                            isFiltered = (product.Category.IndexOf(filter.Criteria, StringComparison.CurrentCultureIgnoreCase)) != -1 ? true : false;
                            break;

                        case "ClothingType":
                            isFiltered = (product.ClothingType.IndexOf(filter.Criteria, StringComparison.CurrentCultureIgnoreCase)) != -1 ? true : false;
                            break;

                        case "ProductName":
                            isFiltered = (product.ProductName.IndexOf(filter.Criteria, StringComparison.CurrentCultureIgnoreCase)) != -1 ? true : false;
                            break;

                        default:
                            break;
                    }

                    return isFiltered;
                }
                else
                    return false;
            };
        }
    }

Ответы [ 2 ]

26 голосов
/ 20 декабря 2009

Каждый раз, когда вы устанавливаете свойство фильтра, вы сбрасываете предыдущий фильтр. Это факт. Теперь, как вы можете иметь несколько фильтров?

Как вы знаете, есть два способа фильтрации: CollectionView и CollectionViewSource. В первом случае с CollectionView мы фильтруем с делегатом, и для создания нескольких фильтров я бы создал класс для агрегации пользовательских фильтров, а затем вызывал их один за другим для каждого элемента фильтра. Как в следующем коде:

  public class GroupFilter
  {
    private List<Predicate<object>> _filters;

    public Predicate<object> Filter {get; private set;}

    public GroupFilter()
    {
      _filters = new List<Predicate<object>>();
      Filter = InternalFilter;
    }

    private bool InternalFilter(object o)
    {
      foreach(var filter in _filters)
      {
        if (!filter(o))
        {
          return false;
        }
      }

      return true;
    }

    public void AddFilter(Predicate<object> filter)
    {
      _filters.Add(filter);
    }

    public void RemoveFilter(Predicate<object> filter)
    {
      if (_filters.Contains(filter))
      {
        _filters.Remove(filter);
      }
    }    
  }

  // Somewhere later:
  GroupFilter gf = new GroupFilter();
  gf.AddFilter(filter1);
  listCollectionView.Filter = gf.Filter;

Чтобы обновить отфильтрованное представление, вы можете вызвать метод ListCollectionView.Refresh().

А во втором случае с CollectionViewSource вы используете Filter событие для фильтрации коллекции. Вы можете создать несколько обработчиков событий для фильтрации по различным критериям. Чтобы узнать больше об этом подходе, прочитайте эту замечательную статью Беа Штольниц: Как применить более одного фильтра?

Надеюсь, это поможет.

Приветствия, Анвака.

8 голосов
/ 20 декабря 2009

Каждый раз, когда пользователь фильтрует, ваш код заменяет делегат Filter в представлении вашей коллекции новым, свежим. Более того, новый проверяет только те критерии, которые пользователь только что выбрал с помощью ComboBox.

То, что вы хотите, это один обработчик фильтра, который проверяет все критерии. Что-то вроде:

public MyViewModel()
{
    products = new ObservableCollection<Product>();
    productsView = new ListCollectionView(products);
    productsView.Filter += FilterProduct;
}

public Item SelectedItem
{
    //get,set omitted. set needs to invalidate filter with refresh call
}

public Type SelectedType
{
    //get,set omitted. set needs to invalidate filter with refresh call
}

public Category SelectedCategory
{
    //get,set omitted. set needs to invalidate filter with refresh call
}

public ICollection<Product> FilteredProducts
{
    get { return productsView; }
}

private bool FilterProduct(object o)
{
    var product = o as Product;

    if (product == null)
    {
        return false;
    }

    if (SelectedItem != null)
    {
        // filter according to selected item
    }

    if (SelectedType != null)
    {
        // filter according to selected type
    }

    if (SelectedCategory != null)
    {
        // filter according to selected category
    }

    return true;
}

Ваше представление теперь может просто привязываться к соответствующим свойствам, и фильтрация будет работать.

...