c# - 过滤使用嵌套的 xaml 数据模板显示的分层对象

标签 c# xaml binding filtering datatemplate

我在过滤显示在嵌套 xaml 模板中的分层数据时遇到问题。

我有一个 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,我想用它来过滤此分割。 当我在文本框中键入内容时,我想过滤掉那些不包含文本的问题。理想情况下 Bar如果选项卡没有可见的 quxes,它们也会被隐藏,并且 Foo标签在不可见时隐藏 Bar

我考虑过两种方法:

方法 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?
}

方法二,将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">

我已经尝试了很多有“??????”的事情但我无法正确绑定(bind)到 ListBox 的数据上下文和适当的 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"/>

我将相应的控件设置为这些 View 中的每一个作为控件的 ItemSource。神奇之处在于每个 CVS 的绑定(bind)。每个 CVS 获取其中出现的控件/模板控件的数据上下文,因此您可以使用绑定(bind)对象集合的真实名称。我不确定我是否理解为什么将该源绑定(bind)的源绑定(bind)到自身(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();
    }
}

出色的解决方案,因为它不需要更改底层对象或层次结构。

关于c# - 过滤使用嵌套的 xaml 数据模板显示的分层对象,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/3321217/

相关文章:

c# - 如何解析 xs :annotation from the xs:choice using the System. Xml.Schema

c# - 访问其他 Windows 窗体类中的变量

c# - WPF 设置用户控件依赖属性

c# - XAML Treeview,如何水平而不是垂直显示节点

wcf - 合约需要 Session,但 Binding ‘WSHttpBinding’ 不支持它或未正确配置以支持它

c# - 使用集合的 Azure 移动服务查询包含

c# - 使用 C# 以编程方式更新 MS Access 数据库中的链接表

.net - 为什么刷只有 3 个十六进制值?

mvvm - 在 MVVM 中将 ObservableCollection 绑定(bind)到数据网格

javascript - Angular 一次性绑定(bind)无法等待 bool 条件