luk*_*uke 8 c# xaml binding filtering datatemplate
我无法过滤嵌套的xaml模板中显示的分层数据.
我有一个ObservableCollection<Foo> Foos,我在XAML中显示.
让我们说Foo看起来像:
class Foo
{
public ObservableCollection<Bar> Bars;
}
class Bar
{
public ObservableCollection<Qux> Quxes;
}
Run Code Online (Sandbox Code Playgroud)
我正在使用以下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>
Run Code Online (Sandbox Code Playgroud)
换句话说,有一个标签控件,每个Foo都有一个标签.每个Foo都是一个选项卡控件,每个Bar都包含在它自己的选项卡中.每个Bar都包含其Quxes的列表框.
要么:
______ ______ ______
| Foo1 | Foo2 | Foo3 |
|______ ______ |
| Bar1 | Bar2 |______|
| | qux1 ||
| | qux2 ||
| | qux3 ||
----------------------
Run Code Online (Sandbox Code Playgroud)
我还有一个TextBox,我想用它来过滤这个细分. 当我在文本框中输入内容时,我想过滤掉quxes,以便那些不包含文本的内容不可见. 理想情况下,Bar如果选项卡没有可见的qux,也会隐藏Foo选项卡,并且当选项卡没有可见的Bars 时隐藏选项卡
我考虑过两种方法:
在我的文本框的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?
}
Run Code Online (Sandbox Code Playgroud)
我尝试通过更改此行来设置过滤器xaml:
<ListBox ItemsSource="{Binding Path=Quxes}" DisplayMemberPath="Name">
Run Code Online (Sandbox Code Playgroud)
对此,
<CollectionViewSource x:Key="MyCVS" Source="?????" Filter="MyCVS_Filter" />
...
<ListBox ItemsSource="{Binding Source={StaticResource MyCVS}}" DisplayMemberPath="Name">
Run Code Online (Sandbox Code Playgroud)
我尝试过很多东西,我有"?????" 但我无法正确绑定到ListBox的datacontext和相应的Quxes成员.我没有尝试任何结果显示quxes,我在控制台上没有错误.即使我可以使用这种方法,我也不确定当搜索框中的文本发生变化时,我将如何重新触发此过滤器.
任何建议或方向将不胜感激.
编辑
最后我让它符合你的要求。
(卢克编辑)
这是我最终采用的(优秀)解决方案,因此我将提取重要部分,并将它们实际上作为帖子的一部分:
关键的 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"/>
Run Code Online (Sandbox Code Playgroud)
我将相应的控件设置为这些视图中的每一个作为控件的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();
}
}
Run Code Online (Sandbox Code Playgroud)
优秀的解决方案,因为它不需要更改底层对象或层次结构。