Фильтрация Datagird с 1+ комбинированными списками - несколько условий фильтра (MVVM) - PullRequest
1 голос
/ 21 февраля 2020

сценарий прост, но я застрял с деталями:

У меня есть сетка данных, которая связана с наблюдаемой коллекцией в моей viewModel. Я хочу отфильтровать сетку данных с записями трех комбинированных списков. Здесь есть другие посты по нескольким фильтрам, но я не смог найти ту, которая подходила бы моему делу или MVVM. Во время моего исследования я несколько раз сталкивался с ControlViewSource как с go. Для простоты я добавил изображение после текста. Вот мой код: MainWindow.xaml

<Window.DataContext>
   <viewModels: MainViewModel />
</Window.DataContext>

<Window.Resources>
   <CollectionViewSource x:Key=“CVS“ Source=“{Binding FirstFilter}“/>
</Window.Resources>

<StackPanel>
   <StackPanel Orientation=“Horizontal“>
      <ComboBox x:Name=“ItemsFilter“ ItemsSource=“{Binding ItemsList}“ SelectedItem="{Binding CurrentItem}"/>
      <ComboBox x:Name=“ElementsFilter“ ItemsSource=“{Binding ElementsList}“ SelectedItem="{Binding CurrentElement}"/>
      <ComboBox x:Name=“ValuesFilter“ ItemsSource=“{Binding ValuesList}“ SelectedItem="{Binding CurrentValue}"/>
</StackPanel>

<DataGrid x:Name=“MainGrid“ ItemsSource=“{Binding DataList}“/>
</StackPanel>

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

  • comboBox «ItemsFilter» содержит записи «AllItems», «ItemsA» и «ItemsB» (тогда как A и B относятся к элементу такие категории, как «арбузы» и «яблоки», так что все «AllItems» будут «AllFruits»).

  • comboBox «ElementsFilter», кроме того, определяет выбор «ItemsFilter»: он должен есть что-то вроде «AllElements», «ElementA», «ElementB» (тогда как это может относиться к брендам, например, «Арбузы» (ITemsFilter) и «Бренд A» (ElementsFilter).

  • comboBox «ValuesFilter» будет «AllValues», «ValueA», «ValueB» и может быть ценой.

Итак, до сих пор я получил ОДИН фильтр для работы. Код в моей viewModel:

public ObservableCollection<DataItem> DataList {get; set;} = new     ObservableCollection<DataItem>();
public ObservableCollection<DataItem> FirstFilter {get; set;} = new     ObservableCollection<DataItem>();

private ICollectionView _myFilterView;
public ICollectionView MyFilterView
{
   get {return _myFilterView;}
   set 
   {
      _myFilterView = value;
      OnPropertyChange(nameof(MyFilterView);
   }
}

private string _currentItem;
public string CurrentItem
{
   get { return _currentItem}
   set 
   {
      _currentItem = value;
      OnPropertyChange(nameofMyFilterView);
   }
}
   // I also have properties CurrentElement, CurrentValue --> all of them representing the currently selected item, element or value (as string) in one of the associated comboBoxes.

public MainViewModel()
{
   //… loading data into my collections
   MyFilterView = CollectionViewSource.GetDefaultView(FirstFilter);
   MyFilterView.Filter = new Predicate<object>(ItemsFilter);
}

Последнее, что нужно сделать, чтобы сделать эту работу, это установить метод, который будет обрабатывать событие фильтра в моей viewModel:

private bool ItemsFilter(object o)
{
   if(string.IsNullOrWhitespace(CurrentFilter)
   {
      return true;
   }
   else
   {
      if(CurrentFilter == „All Items“)
      {
         return true;
      }
      else
      {
         DataItem d = new DataItem();

         return (d.Item == o.Item);
      }
   }
}

По сути - это все для бега Один фильтр. Как я могу реализовать другие фильтры? Что я пробовал до сих пор:

  • Назначить другой ICollectionView (не работает - сетка данных всегда пуста, даже если DefaultView установлен на то же самое коллекция)
  • Попробуйте установить MyFilterView.Filter в отдельных методах, по одному за раз. Это «своего рода» работает, поскольку мой список не основан на иерархических структурах: код оказывается беспорядком, и для каждого нового comboBox мне придется кодировать множество вложенных операторов if, делегируя их следующий контрольно-пропускной пункт. Может кто-нибудь объяснить мне простой способ достижения этого? Примеры кода очень приветствуются! Большое спасибо!

short example

Ответы [ 2 ]

0 голосов
/ 29 февраля 2020

Итак, я нашел решение, которое также учитывает MVVM. Это может быть не самым элегантным, но, возможно, это можно рассматривать как кикстарт, который можно реорганизовать и улучшить. Прежде всего: вам нужно создать экземпляр CollectionViewSource, а не устанавливать ICollectionView. Итак, в вашей viewModel:

public CollectionViewSource _primaryView;
public CollectionViewSource PrimaryView
{
   get {return _primaryView; }
   set
   {
   _primaryView = value;
   PropertyChange(nameof(_primaryView);
   }
}

Также создайте экземпляр вашей коллекции элементов, которые вы хотите отфильтровать. В моем случае:

//This is the collection you want to filter. In my case it holds fields for  
//objects of type item, element and value.

public ObservableCollection<FilterObjects> DefaultFilter {get; set;} = new     
ObservableCollection<FilterObject>();

Затем вам нужно будет установить источник PrimaryView для вашей коллекции (ту, которую вы хотите отфильтровать). Мы устанавливаем это в конструкторе viewModel. Итак, в моем случае:

public MainViewModel()
{
   //Here I fill my dummy data to my collection I want to filter
   // -- load dummy data --

   //Then set PrimaryView.Source to the collection you want to filter
   PrimaryView.Source = DefaultFilter;
}

Далее настройте фильтры. Поэтому вам нужно два ComboBox в вашем представлении. Вот так:

<ComboBox x:Name="cmbItemFilter" Margin="10" ItemsSource="{Binding ItemsList}"    
SelectedIndex="0" SelectedItem="{Binding CurrentItem}">                    
</ComboBox>

ItemsList - это просто еще одно свойство в моей viewModel, и в основном это список объектов элементов. CurrentItem - это свойство типа string, которое содержит выбранный в данный момент элемент comboBox. Не забудьте поднять здесь PropertyChange:

private string _currentItem;
public string CurrentItem
{
get { return _currentItem; }
set
{
   _currentItem = value;

   PropertyChange(nameof(_currentItem));
}

}

Выполните соответствующие действия для любого другого комбинированного списка, который вы можете использовать в качестве фильтра. Итак, в моем случае, у меня есть еще два comboBox, связанные со списком типа элемента и списком типа значения. Аналогично, каждый выбранный элемент comboBox привязан к свойству в моей viewModel (CurrentElement, CurrentValue). Убедитесь, что ваш dataContext для вашего представления установлен на viewModel. Мне нравится делать это в XAML, поэтому вот оно:

<Window.DataContext>
<viewModels:MainViewModel/>
</Window.DataContext>

ViewModels - это пространство имен, которое необходимо импортировать в операторы моего представления. Это довольно просто, но если вы не знакомы с этим: поместите все ваши viewModels в папку вашего решения под названием «ViewModels». Теперь вам нужно сослаться на эту папку в вашем XAML, например, так:

<Window x:Class="DataGrid_Templating.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
…
xmlns:viewModels ="clr-namespace:[nameOfYourProjectWithoutRectangularBrackets].    
[FoldernameWhereYourViewModels resideWithoutRectangularBrackets]"  

Последнее, что вам нужно сделать здесь, это показать вашу сетку данных, откуда она получает данные. Поэтому вам нужно настроить itemsSource следующим образом:

<DataGrid x:Name="dtgMainGrid" ItemsSource="{Binding PrimaryView.View}"  
HorizontalContentAlignment="Center"> </DataGrid>

Убедитесь, что это свойство представления CollectionViewSource, к которому вы привязываете, иначе оно просто не будет работать!

From с этого момента все, что вам нужно сделать, это настроить фильтр logi c. Мальчик, это дало мне кошмары, но это работает! Итак, еще раз, go для вашего viewModel, и измените его в свойстве CurrentItem (CurrentElement / CurrentValue):

private string _currentItem;
public string CurrentItem
{
   get { return _currentItem; }
   set
       {
         _currentItem = value;

         if (CurrentValue == „Show all Items“)
       {
         ApplyElementFilter();
         ApplyValueFilter();
       }
         else
       {
         ApplyItemFilter();
       }       

         PropertyChange(nameof(_currentItem));
     }
}

Эта часть очень сложная, и я до сих пор не уверен, что происходит здесь - я получил желаемый результат более или менее благодаря счастливой случайности ... Просто убедитесь, что если у вас есть критерии фильтра, такие как «Выбрать всех людей» или «Показать все категории» (вроде настройки по умолчанию), он запустит все другие фильтры, которые у вас есть at посмотрите в следующем параграфе, я уверен, что код является тихим пояснением об этом. ApplyElementFilter () и ApplyValueFilter (или любой другой фильтр, который вам нравится) должны быть реализованы в вашей viewModel:

private void ApplyItemFilter()
{
   PrimaryView.Filter -= new FilterEventHandler(FilterByItems);
   PrimaryView.Filter += new FilterEventHandler(FilterByItems);           
}

private void ApplyElementFilter()
{
   PrimaryView.Filter -= new FilterEventHandler(FilterByElement);
   PrimaryView.Filter += new FilterEventHandler(FilterByElement);            
}

private void ApplyValueFilter()
{
   PrimaryView.Filter -= new FilterEventHandler(FilterByValue);
   PrimaryView.Filter += new FilterEventHandler(FilterByValue);            
}

Вы близки к этому, так что терпите меня. Последнее, что вам нужно сделать, это назначить методы, на которые вы ссылаетесь в FilterEventHandler. Они все одинаковы, поэтому для простоты я обращаюсь только к одному:

public void FilterByItems(object sender, FilterEventArgs e)

{var sr c = e.Item as Item;

   if (src == null)
   {
      e.Accepted = false;
      else if (CurrentItem== "Show all Items")
      e.Accepted = true;
   }
   else if (string.Compare(CurrentItem, src.CurrentItem) != 0)
   {
      e.Accepted = false;           
   }

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

0 голосов
/ 23 февраля 2020

Важно было видеть, как структурируется DataItem и как заполняются ItemsList и другие списки фильтров.

В любом случае, я предполагаю, что DataItem имеет 3 свойства как string Item, string Element и string value и что ItemsList содержит все возможные значения для поля Item, найденного в DataList плюс "All Items".

Внутри ItemsFilter вы можете кодировать любой фильтр, который вы хотите так почему бы не внедрить туда другие два?

private bool ItemsFilter(object o)
{
    var dataItem = o as DataItem;

    // Item filter
    if(!string.IsNullOrWhitespace(CurrentFilter))
        if(CurrentFilter != "All Items")
            if (o.Item != CurrentFilter)
                return false;

    // Element filter
    if(!string.IsNullOrWhitespace(CurrentElement))
        if(CurrentElement != "All Elements")
            if (o.Element != CurrentElement)
                return false;

    // Value filter
    if(!string.IsNullOrWhitespace(CurrentValue))
        if(CurrentValue != "All Values")
            if (o.Value != CurrentValue)
                return false;

    return true;
}

Надеюсь, я помог.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...