TreeView节点过滤的最佳方法

10

如何最好地/高效地过滤 Treeview 节点?

例如:我输入了"abc",只有包含"abc"文本的节点会显示。然后我输入了"abcd",应该只看到包含"abcd"文本的节点。每次更改过滤条件时,TreeView内容也会相应改变。

有什么想法吗?

4个回答

10

如果你想要最佳的性能,先克隆树形结构,然后从克隆的树形结构中删除所有项目,接着将现有的树形结构简单替换为克隆的(和筛选过的)树形结构。

我还保留了一个始终未筛选的备份树形结构。

对于具有1000-2000个节点的相当大的树形结构,这对我非常有效。


我同意这种方法,与此有关的模式称为备忘录模式。 - Burt
3
很遗憾,树形节点上没有“可见”属性,如果有的话就好多了。 - leppie
我已经测试了这个方法。感谢您的建议。我计划在TreeView上实现这个功能,节点数可能会达到10-20k... - Murat from Daminion Software
1
+1 当父级不符合条件,但其子级符合条件的情况下,您实现了什么行为? - Jon Seigel
@Murat(或leppie)关于克隆树 - 是指TreeNodeCollection吗?通过其子节点递归克隆每个TreeNode?不是整个TreeView控件,对吧? - Code Jockey
显示剩余2条评论

1
我继承TreeView以进行过滤(仅限于第一层,我正在处理它):
public partial class winTree : TreeView
{
    private NodesCollection allNodes = new NodesCollection();

    [ReadOnly(true)]
    public new NodesCollection Nodes { get { return allNodes; } }

    private string filtro = string.Empty;
    public String Filtro
    {
        get { return filtro; }
        set { filtro = value; filtrarNodos(); }
    }

    public winTree()
    {
        InitializeComponent();

        allNodes.NodeAdd += OnNodeAdd;
        allNodes.NodeRemove += OnNodeRemove;
        allNodes.NodesClear += OnNodesClear;
    }


    private void OnNodeAdd(object sender, EventArgs e)
    {
        TreeNode n = (TreeNode)sender;

        if (passFilter(n))
        {
            base.Nodes.Add(n);
        }
    }

    private void OnNodeRemove(object sender, EventArgs e)
    {
        base.Nodes.Remove((TreeNode)sender);
    }

    private void OnNodesClear(object sender, EventArgs e)
    {
        base.Nodes.Clear();
    }


    private void filtrarNodos()
    {
        this.BeginUpdate();

        base.Nodes.Clear();

        foreach(TreeNode n in this.Nodes)
        {
            if (passFilter(n))
            {
                base.Nodes.Add(n);
            }
        }

        this.EndUpdate();
    }

    private bool passFilter(TreeNode nodo)
    {
        if (string.IsNullOrWhiteSpace(filtro))
        {
            return true;
        }
        else
        {
            return nodo.Text.ToLower().Contains(filtro.ToLower());
        }
    }
}

public class NodesCollection : IList<TreeNode>
{
    private List<TreeNode> nodos = new List<TreeNode>();

    public event EventHandler NodeAdd;
    public event EventHandler NodeRemove;
    public event EventHandler NodesClear;

    private void OnNodeAdd(TreeNode nodo)
    {
        if (NodeAdd != null)
        {
            NodeAdd(nodo, EventArgs.Empty);
        }
    }

    private void OnNodeRemove(TreeNode nodo)
    {
        if (NodeRemove != null)
        {
            NodeRemove(nodo, EventArgs.Empty);
        }
    }

    private void OnNodesClear()
    {
        if (NodeRemove != null)
        {
            NodesClear(this, EventArgs.Empty);
        }
    }


    #region IList<TreeNode>

        public int IndexOf(TreeNode item)
        {
            return nodos.IndexOf(item);

            OnNodeAdd(item);
        }

        public void Insert(int index, TreeNode item)
        {
            nodos.Insert(index, item);

            OnNodeAdd(item);
        }

        public void RemoveAt(int index)
        {
            TreeNode nodo = nodos[index];

            nodos.RemoveAt(index);

            OnNodeRemove(nodo);
        }

        public TreeNode this[int index]
        {
            get
            {
                return nodos[index];
            }
            set
            {
                OnNodeRemove(nodos[index]);
                nodos[index] = value;
                OnNodeAdd(nodos[index]);
            }
        }

        public void Add(TreeNode item)
        {
            nodos.Add(item);

            OnNodeAdd(item);
        }

        public void Clear()
        {
            nodos.Clear();

            OnNodesClear();
        }

        public bool Contains(TreeNode item)
        {
            return nodos.Contains(item);
        }

        public void CopyTo(TreeNode[] array, int arrayIndex)
        {
            nodos.CopyTo(array, arrayIndex);
        }

        public int Count
        {
            get { return nodos.Count(); }
        }

        public bool IsReadOnly
        {
            get { return true; }
        }

        public bool Remove(TreeNode item)
        {
            bool res = nodos.Remove(item);

            if (res)
            {
                OnNodeRemove(item);
            }

            return res;
        }

        public IEnumerator<TreeNode> GetEnumerator()
        {
            return nodos.GetEnumerator();
        }

        IEnumerator IEnumerable.GetEnumerator()
        {
            return nodos.GetEnumerator();
        }

    #endregion
}

0

我想提供一个更好的解决方案,关键是我不想从一个新类中派生,减少涉及的代码。

我的解决方案:

  • 按照正常方式构建树,不要进行过滤

  • 备份树(下面的代码)(仅在进行搜索时才备份,而不是为了备份)

  • 可以随意更改树,删除节点等

  • 如果需要将树重置为初始状态,请调用_backup.Restore()

      using System.Linq;
      using System.Windows.Forms;
    
      public class TreeViewBackup : List<TreeViewBackup>
      {
          public TreeNode Parent { get; }
          public TreeNodeCollection Children { get; }
    
          public TreeViewBackup(TreeNodeCollection children, TreeNode parent = null)
          {
              Parent = parent;
              Children = children;
              AddRange(Children.Cast<TreeNode>().Select(child => new TreeViewBackup(child.Nodes, child)));
          }
    
          public void Restore()
          {
              Children.Clear();
              this.ForEach(clone => clone.Restore());
              Children.AddRange(this.Select(n => n.Parent).ToArray());
          }
      }
    
      public class Form1
      {
          public void Filter()
          {
             _backup = new TreeViewBackup(_treeView.Nodes);
             _treeView.BeginUpdate();
             MessWithMe();
             _treeView.EndUpdate();
          }
    
          public void Undo()
          {
             _treeView.BeginUpdate();
             _backup.Restore();
             _treeView.EndUpdate();
          }
      }
    

-1
如果你从叶子节点向父节点循环,你可以找到那些根节点中不包含任何匹配字符串的叶子节点。

这个问题不是关于树的遍历或如何匹配字符串,而是关于以高效的方式过滤/恢复TreeView的状态。 - Termit

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接