Подтвердить что ты не робот

WPF Использование нескольких фильтров в одном элементе ListCollectionView

Я использую шаблон проектирования MVVM, при этом ListView привязан к ListCollectionView в ViewModel. У меня также есть несколько comboboxes, которые используются для фильтрации 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;
            };
        }
    }
4b9b3361

Ответ 1

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

Как вы знаете, существует два способа фильтрации: 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 для фильтрации коллекции. Вы можете создать несколько обработчиков событий для фильтрации по различным критериям. Чтобы узнать больше об этом подходе, посмотрите эту замечательную статью Бэ Столнитца: Как применить несколько фильтров?

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

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

Ответ 2

Каждый раз, когда пользователь фильтрует, ваш код заменяет делегат 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;
}

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