扩展TreeView控件以进行增量过滤/搜索

时间:2014-07-24 00:49:06

标签: c# .net winforms treeview

我尝试扩展winforms TreeView控件以允许增量过滤和搜索,类似于VS2012 / VS2013中的解决方案资源管理器。

理想情况下,我希望它能够用最少的代码更改替换现有的TreeView - 就消费者而言,唯一的区别是方法void Filter(string)。因此,我认为Nodes属性返回带有所有节点的TreeNodeCollection是有意义的,即使是因为应用了过滤器而未显示的节点。

我编写的代码用于处理过滤,它实际上运行良好,除非我访问base.Nodes时,它返回我的过滤节点而不是完整列表。

我遇到的问题是,我无法克隆或创建TreeNodeCollection的新实例,因为构造函数被标记为内部。所以我的理想代码看起来像这样:

public class TreeViewEx : TreeView
{
    // results in a compiler error:
    private TreeNodeCollection _allNodes = new TreeNodeCollection();

    public new TreeNodeCollection Nodes { get { return _allNodes; } }

    public TreeNodeCollection FilteredNodes { get { return base.Nodes; } }

    public void Filter(string searchString)
    {
        base.BeginUpdate();
        base.Nodes.Clear();
        foreach (TreeNode node in FilterInternal(_allNodes, searchString))
        {
            base.Nodes.Add(node);
        }
        base.EndUpdate();
    }
}

正如您所看到的,我试图将UI中显示的节点与消费者可以访问的节点分离。当然TreeNodeCollection只有一个内部构造函数,我无法创建新实例或克隆它。

我考虑了这两个选项,但听起来都不是很好的解决方案:

  1. 使用反射为第二个列表实例化TreeNodeCollection对象(由于内部构造函数)。这个选项似乎比#2更有效,但我当然要创建一个我不应该做的对象实例。
  2. 在内存中实例化第二个TreeView,并使用其中的Nodes属性来维护我的第二个列表。这似乎可能是很多开销。
  3. 我希望最终结果仍然是TreeNodeCollection,因此可以使用TreeView以最少的代码替换我们现有的控件,并且我们使用Find方法确实有几个地方,而不是{39} ; t存在于List<TreeNode>

    有没有人对如何处理这个有任何建议?我的两个考虑因素在性能/资源方面呢?

    谢谢

    更新1:

    根据Pat的建议,我决定退一步,避免完全弄乱Nodes。所以现在我添加了List<TreeNode> AllNodes属性并让Nodes只显示TreeView中出现的节点(已过滤的列表),所以现在它更简单了。

    我现在的问题是,我如何知道AllNodes何时添加了一个项目,以便我可以让Nodes保持同步?我考虑使用BindingList,因此我有ListChanged事件,但后来我需要拥有我的TreeNode和节点的子/幼子/等({{1使用继承自AllNodes[0].Nodes的自定义类并更改TreeNode属性,Nodes不可覆盖。还有另外一种方法吗?我可以创建一个名为NodeExs的新属性,但这看起来非常不直观,我可以看到另一个开发人员随后出现并拔出他的头发因为Nodes属性存在但不起作用。

2 个答案:

答案 0 :(得分:1)

关于您提出的解决方案,#2已经出局,因为TreeNode不能属于多个控件。虽然可以通过反射创建TreeNodeCollection的实例,但它不会非常有用,因为它被设计为耦合到TreeView或另一个TreeNode。您无法从集合中添加/删除节点。

  

因此,我认为Nodes属性才有意义   返回TreeNodeCollection与所有节点,甚至是未显示的节点   因为应用过滤器。

我不同意,框架和操作系统使用TreeNodeCollection属性返回的Nodes来呈现控件。你真的不想隐藏这个属性或改变它的功能。

如果消费者需要访问_allNodes,请创建List<TreeNode> AllNodes属性或使用自定义集合。

答案 1 :(得分:1)

我发现TreeNodeCollection只应用于读取列出的节点。相反,我使用List<TreeNode>列出节点。在我的项目中,我为List<TreeNode>上的每个级别创建了TreeView。当我在启动时填充TreeView时,我同时填写了列表。最后,我使用AddRange()来制作和组合所有节点的列表。这样我就列出并分类了所有节点。

创建此类列表非常简单快捷。我还创建了所有节点列表的List<string>版本,我为AutoCompleteCustomSource设置为TextBox。通过这种方式,我可以使用TextBoxAutoComplete来搜索节点。

我会为消费者和其他类别制作不同的列表。然后我只会将项目添加到符合给定条件的TreeView。您还可以使用treeView.Nodes.Remove()删除任何节点。您仍然将实际节点存储在列表中,并可以稍后再将其添加回来。

这些只是一些想法。