I’m having trouble filtering hierarchical data that’s being displayed in nested xaml templates.
I’ve got a ObservableCollection<Foo> Foos, that I’m displaying in XAML.
Lets say Foo looks like:
class Foo
{
public ObservableCollection<Bar> Bars;
}
class Bar
{
public ObservableCollection<Qux> Quxes;
}
I’m displaying Foos with the following 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>
To put it into words, there’s a tab control, with a tab for each Foo. Each Foo is a tab control, with each Bar it contains in it’s own tab. Each Bar contain’s a listbox of its Quxes.
or:
______ ______ ______
| Foo1 | Foo2 | Foo3 |
|______ ______ |
| Bar1 | Bar2 |______|
| | qux1 ||
| | qux2 ||
| | qux3 ||
----------------------
I also have a TextBox that I’d like to use to filter this breakdown. When I type in the text box, I’d like to filter the quxes so those not containing the text wouldn’t be visible. Ideally Bar tabs would also be hidden if they have no visible quxes, and Foo tabs hidden when they have no visible Bars
I have considered two approaches:
Approach 1, reset the Filter property on the appropriate CollectionViewSources
On my text box’s TextChanged event, I loop through my Foo’s asking for the corresponding (static) TabControl’s CollectionViewSource:
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?
}
Approach 2, declare the ListView’s ItemSource to a CollectionViewSource
I tried setting the Filter via xaml, by changing this line:
<ListBox ItemsSource="{Binding Path=Quxes}" DisplayMemberPath="Name">
to this,
<CollectionViewSource x:Key="MyCVS" Source="?????" Filter="MyCVS_Filter" />
...
<ListBox ItemsSource="{Binding Source={StaticResource MyCVS}}" DisplayMemberPath="Name">
I’ve tried a number of things where I have “?????” but I cannot correctly bind to the ListBox’s datacontext and appropriate Quxes member. Nothing I try results in the quxes being displayed, and I get no errors on the console. Even If I could get this approach to work, I’m not sure how I would re-trigger this filter when the text in the search box changed.
Any advice or direction would be appreciated.
Edit
Finally I’ve got it working with your requirements.
Here is the link to the updated project.
(edit by luke)
This is the (excellent) solution I ended up going with, so I’m going to extract the important parts, and actually make them part of the post here:
The key xaml portion ends up looking like this:
I set the respective control to each one of these views as the control’s
ItemSource. The magic is in the binding of each CVS. Each CVS gets the data context for the control/templated control in which appears, so you can use the the real name of the bound object’s collection. I’m not sure I understand why binding the source of that source binding to itself (the CVS) works, but it does so beautifully.The code for the filter
TextBoxthen becomes something like:Excellent solution as it requires no changing of the underlying objects or hierarchy.