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

我无法过滤嵌套的xaml模板中显示的分层数据。

我有一个ObservableCollection Foos ,我在XAML中显示。

让我们说Foo看起来像:

 class Foo { public ObservableCollection Bars; } class Bar { public ObservableCollection Quxes; } 

我正在使用以下xaml显示Foos:

                   

换句话说,有一个标签控件,每个Foo都有一个标签。 每个Foo都是一个选项卡控件,每个Bar都包含在它自己的选项卡中。 每个Bar都包含其Quxes的列表框。

要么:

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

我还有一个TextBox,我想用它来过滤这个细分。 当我在文本框中输入内容时,我想过滤掉quxes,以便那些不包含文本的内容不可见。 理想情况下,如果Bar选项卡没有可见的qux,它们也会被隐藏,而当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? } 

方法2,将ListView的ItemSource声明为CollectionViewSource

我尝试通过更改此行来设置filterxaml:

  

对此,

  ...  

我尝试过很多东西,我有“?????” 但我无法正确绑定到ListBox的datacontext和相应的Quxes成员。 我没有尝试任何结果显示quxes,我在控制台上没有错误。 即使我能够使用这种方法,我也不确定当搜索框中的文本发生变化时,我将如何重新触发此filter。

任何建议或方向将不胜感激。

编辑

最后,我已经满足您的要求。

这是更新项目的链接 。


(由卢克编辑)

这是我最终选择的(优秀)解决方案,所以我将提取重要的部分,并实际上将它们作为post的一部分:

关键的xaml部分最终看起来像这样:

    

我将相应的控件设置为每个视图作为控件的ItemSource 。 神奇的是每个CVS的绑定。 每个CVS都会获取出现的控件/模板控件的数据上下文,因此您可以使用绑定对象集合的真实名称。 我不确定我理解为什么绑定源绑定的源自身(CVS)有效,但它确实如此美妙。

filterTextBox的代码然后变成如下:

 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(); } } 

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

我认为您应该从View-Model中公开ICollectionView ,而不是(或者除了) ObservableCollection 。 这将把过滤/排序所涉及的所有业务逻辑带入VM,这是适合它的地方。

您可以通过创建CollectionViewSource ,将其Source属性设置为集合并检索View属性来获取集合的ICollectionView

(更新)以下是一些示例代码:

 class Foo { public Foo() { _bars = new ObservableCollection(); Bars = new CollectionViewSource { Source = _bars }.View; } private ObservableCollection _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 _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; } } 

我今天的工作遇到了类似的问题,并提出了以下解决方案:

  1. 直接或通过适配器模式将Visibility属性添加到所有元素。

      Visibility Visibility { get { return visibility; } set { visibility = value; PropertyChanged("Visibility"); } } 
  2. 将控件的Vis Visibility属性绑定到step1中的相应Visibility属性。

  3. 通过扩展方法或在其中实现对数据的简单过滤。

     void Filter(Func filterFunc) { foreach (var item in foos) { if (!filterFunc(item)) item.Visibility = Visibility.Collapsed; else item.Visibility = Visibility.Visible; } } 
  4. 在TextBox的TextChanged事件上添加简单的filter调用。

    filter(n => n.Name.ToLower()。包含(textBox.Text));

或容器控件更高级:

 Filter(c => c.Items.Any(i => i.Visibility == Visibility.Visible));