我有一个ObservableCollection< Foo> Foos,我在XAML中展示.
让我们说Foo看起来像:
class Foo { public ObservableCollection<Bar> Bars; } class Bar { public ObservableCollection<Qux> Quxes; }
我正在使用以下xaml显示Foos:
<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都是一个选项卡控件,每个Bar都包含在它自己的选项卡中.每个Bar都包含其Quxes的列表框.
要么:
______ ______ ______ | Foo1 | Foo2 | Foo3 | |______ ______ | | Bar1 | Bar2 |______| | | qux1 || | | qux2 || | | qux3 || ----------------------
我还有一个TextBox,我想用它来过滤这个细分.当我在文本框中输入内容时,我想过滤掉quxes,以便那些不包含文本的内容不可见.理想情况下,如果Bar选项卡没有可见的qux,它们也会被隐藏,而当Foo选项卡没有可见的Bars时隐藏它们
我考虑过两种方法:
方法1,重置相应CollectionViewSources上的Filter属性
在我的文本框的TextChanged事件中,我遍历我的Foo,询问相应的(静态)TabControl的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? }
方法2,将ListView的ItemSource声明为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">
我尝试过很多东西,我有“?????”但我无法正确绑定到ListBox的datacontext和相应的Quxes成员.我没有尝试任何结果显示quxes,我在控制台上没有错误.即使我可以使用这种方法,我也不确定当搜索框中的文本发生变化时,我将如何重新触发此过滤器.
任何建议或方向将不胜感激.
解决方法
最后,我已经满足您的要求.
Here is the link to the updated project.
(由卢克编辑)
这是我最终选择的(优秀)解决方案,所以我将提取重要的部分,并实际上将它们作为帖子的一部分:
关键的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)有效,但它确实如此美妙.
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(); } }
优秀的解决方案,因为它不需要更改底层对象或层次结构.