Фильтрация иерархического объекта, отображенного с вложенными xaml шаблонами данных

Я испытываю затруднения при фильтрации иерархических данных, это отображается во вложенных шаблонах xaml.

У меня есть a ObservableCollection<Foo> Foos, то, что я отображаюсь в XAML.

Позволяет говорят, что Foo похож:

class Foo
{
    public ObservableCollection<Bar> Bars;
}

class Bar
{
    public ObservableCollection<Qux> Quxes;
}

Я отображаю Foos со следующим xaml:

<Grid>
    <Grid.Resources>
        <CollectionViewSource x:Key="MyCVS" Source="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type ListView}}, Path=DataContext.UnifiedSymbols}" Filter="MyCVS_Filter" />

        <DataTemplate x:Key="NestedTabHeaderTemplate">
            <TextBlock Text="{Binding Path=Name}"/>
        </DataTemplate>
        <DataTemplate x:Key="NestedTabContentTemplate">
            <ListBox ItemsSource="{Binding Path=Quxes}" DisplayMemberPath="Name"/>
        </DataTemplate>

        <DataTemplate x:Key="TopLevelTabHeaderTemplate">
            <TextBlock Text="{Binding Path=Name}"/>
        </DataTemplate>
        <DataTemplate x:Key="TopLevelTabContentTemplate">
            <TabControl ItemsSource="{Binding Path=Bars}"
                        ItemTemplate="{StaticResource NestedTabHeaderTemplate}" 
                        ContentTemplate="{StaticResource NestedTabContentTemplate}"
                        />
        </DataTemplate>
    </Grid.Resources>

    <TabControl ItemSource="{Binding correct binding for my control's collection of Foos}"
                ItemTemplate="{StaticResource TopLevelTabHeaderTemplate}" 
                ContentTemplate="{StaticResource TopLevelTabContentTemplate}"
                            x:Name="tabControl"
                />
</Grid>

Для выражения его словами существует управление вкладкой с вкладкой для каждого Foo. Каждый Foo является управлением вкладкой с каждой Панелью, которую оно содержит на своей собственной вкладке. Каждый contain's Панели поле списка его Quxes.

или:

 ______ ______ ______  
| Foo1 | Foo2 | Foo3 |  
|______ ______       |  
| Bar1 | Bar2 |______|  
| | qux1            ||  
| | qux2            ||  
| | qux3            ||  
---------------------- 

У меня также есть TextBox, который я хотел бы использовать для фильтрации этой разбивки. Когда я ввожу в текстовом поле, я хотел бы отфильтровать quxes, таким образом, те, которые не содержат текст, не будут видимы. Идеально Bar вкладки также были бы скрыты, если у них нет видимого quxes, и Foo вкладки, скрытые, когда они имеют не видимый Bars

Я рассмотрел два подхода:

Приблизьтесь 1, сбросьте Свойство фильтра на соответствующем CollectionViewSources

На событии TextChanged моего текстового поля я циклично выполняюсь посредством просьбы своего Foo о CollectionViewSource соответствующего (статического) TabControl:

foreach(Foo foo in tabControl.Items)
{
    var tabItem = tabControl.ItemContainerGenerator.ContainerFromItem(foo);    // This is always of type TabItem
    // How do I get the TabControl that will belong to each of Foo's Bar's?
}

Приблизьтесь 2, объявите ItemSource ListView к CollectionViewSource

Я пытался установить Фильтр через xaml путем изменения этой строки:

<ListBox ItemsSource="{Binding Path=Quxes}" DisplayMemberPath="Name">

к этому,

<CollectionViewSource x:Key="MyCVS" Source="?????" Filter="MyCVS_Filter" />
...
<ListBox ItemsSource="{Binding Source={StaticResource MyCVS}}" DisplayMemberPath="Name">

Я попробовал много вещей, где я имею"?????" но я не могу правильно связать с datacontext ListBox и соответствующим членом Quxes. Ничто я пробую результаты в quxes том, чтобы быть отображенным, и я не получаю ошибок на консоли. Даже если бы я мог бы заставить этот подход работать, я не уверен, как я повторно инициировал бы этот фильтр когда текст в измененном поле поиска.

Любой совет или направление ценились бы.

8
задан luke 30 July 2010 в 12:44
поделиться

3 ответа

Edit

Наконец-то все заработало с вашими требованиями.

Вот ссылка на обновленный проект .


(отредактировал luke)

Это (отличное) решение, которое я выбрал, поэтому я собираюсь извлечь важные части и фактически сделать их частью сообщения здесь:

Ключ xaml в конечном итоге выглядит следующим образом:

<CollectionViewSource x:Key="FooCVS" x:Name="_fooCVS" Source="{Binding Foos, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type WpfApplication1:MainWindow}}}" Filter="_fooCVS_Filter"/>
<CollectionViewSource x:Key="BarCVS" x:Name="_barCVS" Source="{Binding Bars, Source={StaticResource FooCVS}}" Filter="_barCVS_Filter"/>
<CollectionViewSource x:Key="QuxCVS" x:Name="_quxCVS" Source="{Binding Quxs, Source={StaticResource BarCVS}}"  Filter="_quxCVS_Filter"/>

Я установил соответствующий элемент управления для каждого из этих представлений как элемент управления ItemSource . Магия заключается в привязке каждой CVS. Каждая CVS получает контекст данных для элемента управления / шаблонного элемента управления, в котором появляется, поэтому вы можете использовать настоящее имя коллекции связанных объектов. Я не уверен, что понимаю, почему привязка источника этой привязки к самому себе (CVS) работает, но это прекрасно.

Код фильтра TextBox затем становится примерно таким:

private void filterTextBox_TextChanged(object sender, TextChangedEventArgs e)
{
    var cvs = TryFindResource("FooCVS") as CollectionViewSource;
    if (cvs != null)
    {
        if (cvs.View != null)
            cvs.View.Refresh();
    }
    cvs = TryFindResource("QuxCVS") as CollectionViewSource;
    if (cvs != null)
    {
        if (cvs.View != null)
            cvs.View.Refresh();
    }
    cvs = TryFindResource("BarCVS") as CollectionViewSource;
    if (cvs != null)
    {
        if (cvs.View != null)
            cvs.View.Refresh();
    }
}

Отличное решение, поскольку оно не требует изменения базовых объектов или иерархии.

3
ответ дан 5 December 2019 в 23:13
поделиться

Я думаю, вам следует раскрыть ICollectionView из вашей модели представления вместо (или в дополнение) ObservableCollection . Это перенесет всю бизнес-логику, связанную с фильтрацией / сортировкой, в виртуальную машину, которая является подходящим местом для этого.

Вы можете получить ICollectionView коллекции, создав CollectionViewSource , установив его свойство Source для коллекции и получив представление View ] имущество.

(Обновление) Вот пример кода:

class Foo
{
    public Foo()
    {
        _bars = new ObservableCollection<Bar>();
        Bars = new CollectionViewSource { Source = _bars }.View;
    }

    private ObservableCollection<Bar> _bars;
    public ICollectionView Bars { get; private set; }

    public void Filter(string quxName)
    {
        Bars.Filter = o => ((Bar)o).Quxes.Any(q => q.Name == quxName);

        foreach (Bar bar in Bars)
        {
            bar.Filter(quxName);
        }
    }
}   

class Bar
{
    private ObservableCollection<Qux> _quxes;
    public ICollectionView Quxes { get; private set; }

    public void Filter(string quxName)
    {
        Quexs.Filter = o => ((Qux)o).Name == quxName;
    }
}

class Qux
{
    public string Name { get; set; }
}
2
ответ дан 5 December 2019 в 23:13
поделиться

У меня сегодня была аналогичная проблема на работе, и я предложил следующее решение:

  1. Добавьте свойство Visibility ко всем вашим элементам напрямую или через шаблон адаптера.

      Видимость Видимость
     {
    получить {вернуть видимость; }
    установить {видимость = значение; PropertyChanged («Видимость»); }
     }
    
  2. Привязать свойство Visibility элементов управления к соответствующим свойствам Visibility из шага 1.

  3. Реализуйте простую фильтрацию ваших данных с помощью методов расширения или внутри них.

     void Filter (Func  filterFunc)
    {
    foreach (элемент var в foos)
     {
    если (! filterFunc (элемент))
    item.Visibility = Видимость.Collapsed;
    еще
    item.Visibility = Видимость.Видимый;
     }
    }
    
  4. Добавьте вызовы простого фильтра к событию TextChanged вашего TextBox.

    Filter (n => n.Name.ToLower (). Contains (textBox.Text));

или более продвинутые элементы управления контейнером:

Filter(c => c.Items.Any(i => i.Visibility == Visibility.Visible));
0
ответ дан 5 December 2019 в 23:13
поделиться
Другие вопросы по тегам:

Похожие вопросы: