如何使用ICollectionView过滤wpf树视图层次结构?

时间:2009-08-21 17:48:13

标签: c# wpf treeview

我有一个包含这些数据的假设树视图:

RootNode
   Leaf
   vein
SecondRoot
   seeds
   flowers

我正在尝试过滤节点,以便仅显示包含特定文本的节点。假如我指定“L”,树将被过滤并仅显示RootNode-> Leaf和SecondRoot->花(因为它们都包含字母L)。

遵循m-v-vm模式,我有一个基本的TreeViewViewModel类,如下所示:

public class ToolboxViewModel
{
    ...
    readonly ObservableCollection<TreeViewItemViewModel> _treeViewItems = new ObservableCollection<TreeViewItemViewModel>();
    public ObservableCollection<TreeViewItemViewModel> Headers
    {
        get { return _treeViewItems; }
    }

    private string _filterText;
    public string FilterText
    {
        get { return _filterText; }
        set
        {
            if (value == _filterText)
                return;

            _filterText = value;

            ICollectionView view = CollectionViewSource.GetDefaultView(Headers);
            view.Filter = obj => ((TreeViewItemViewModel)obj).ShowNode(_filterText);
        }
    }
    ...
}

一个基本的TreeViewItemViewModel:

public class ToolboxItemViewModel
{
    ...
    public string Name { get; private set; }
    public ObservableCollection<TreeViewItemViewModel> Children { get; private set; }
    public bool ShowNode(string filterText)
    {
        ... return true if filterText is contained in Name or has children that contain filterText ... 
    } 
    ...
}

所有内容都在xaml中设置,所以我看到了树视图和搜索框。

执行此代码时,过滤器仅适用于不足的根节点。有没有办法让过滤器在节点层次结构中流淌,以便为每个节点调用我的谓词?换句话说,过滤器可以作为一个整体应用于TreeView吗?

6 个答案:

答案 0 :(得分:7)

这是我过滤TreeView上的项目的方式:

我有课:

class Node
{
    public string Name { get; set; }
    public List<Node> Children { get; set; }

    // this is the magic method!
    public Node Search(Func<Node, bool> predicate)
    {
         // if node is a leaf
         if(this.Children == null || this.Children.Count == 0)
         {
             if (predicate(this))
                return this;
             else
                return null;
         }
         else // Otherwise if node is not a leaf
         {
             var results = Children
                               .Select(i => i.Search(predicate))
                               .Where(i => i != null).ToList();

             if (results.Any()){
                var result = (Node)MemberwiseClone();
                result.Items = results;
                return result;
             }
             return null;
         }             
    }
}

然后我可以将结果过滤为:

// initialize Node root
// pretend root has some children and those children have more children
// then filter the results as:
var newRootNode = root.Search(x=>x.Name == "Foo");

答案 1 :(得分:4)

不幸的是,没有办法让相同的Filter自动应用于所有节点。 Filter是ItemsCollection的一个属性(不是DP),它不是DependencyObject,因此DP Value继承不存在。

树中的每个节点都有自己的ItemsCollection,它有自己的Filter。使其工作的唯一方法是手动将它们全部设置为调用相同的委托。

最简单的方法是公开Predicate&lt; object&gt;类型的Filter属性在你的ToolBoxViewModel和它的setter中触发一个事件。然后ToolboxItemViewModel将负责使用此事件并更新其Filter。

很漂亮,我不确定树中大量项目的表现会是什么样的。

答案 2 :(得分:3)

我发现这样做的唯一方法(这有点像黑客攻击)是创建一个从IList转换为IEnumerable的ValueConverter。在ConvertTo()中,从传入的IList返回一个新的CollectionViewSource。

如果有更好的方法,我很乐意听到它。但这似乎有用。

答案 3 :(得分:2)

我决定使用Philipp Sumi提到的树视图:http://www.codeproject.com/KB/WPF/versatile_treeview.aspx

并对其应用过滤器,如下所示:http://www.hardcodet.net/2008/02/programmatically-filtering-the-wpf-treeview

我不能推荐它:)

答案 4 :(得分:1)

为什么需要过滤器或CollectionSource?这是处理TreeView项的简单MVVM方法。

您只需使用DataTriggers即可使项目可见,折叠,更改颜色,突出显示,闪烁等等。

.hero-image-row {
   margin-top: -50px !important;
   overflow-x: hidden !important;
   width: 100% !important;
   vertical-align: top;
 }

我将把完整的示例包含在我的WPF库中:

https://www.codeproject.com/Articles/264955/WPF-MichaelAgroskin

答案 5 :(得分:0)

您可以使用ItemContainerGenerator获取树中给定元素的TreeViewItem,一旦有了,您就可以设置过滤器。