Get the all relationships including all Children of children, collapsed up to the point of the child(or parent) that contains the keyword

Viewed 37

I have a treeview, which is populated with multiple nodes based on a directory structure, sometimes single, but often in parent-child-relationships.

My goal is to filter the treeview by keyword.

I accomplished this using

private void Filter()
{
    TreeView tv = new TreeView();
    //Clone backup List<TreeNode> myTreeview into temporary TreeView tv
    foreach (TreeNode n in myTreeview)
        tv.Nodes.Add((TreeNode)n.Clone());
    
    treeViewInForm.Nodes.Clear();
    if (txtKeyword.Text == "")
    {
        treeViewInForm.Nodes.AddRange(myTreeview.ToArray());
    }
    else
    {
        foreach (TreeNode n in tv.Nodes)
        {
            RecursiveFunction(n);
        }
        treeViewInForm.ExpandAll();
    }
}


private void RecursiveFunction(TreeNode treeNode)
{
    if (treeNode.Text.ToLower().Contains(txtKeyword.Text.ToLower()))
    {
        this.Invoke((MethodInvoker)delegate
        {
            TreeNode root = new TreeNode();
            TreeNode node = root;
            bool beginn = true;
            foreach (string pathBits in treeNode.FullPath.Split('\\'))
            {
                //Comnsole.WriteLine("pb: " + pathBits);
                if (beginn)
                {
                    beginn = false;
                    node.Text = pathBits;
                }
                else
                {
                    node = AddNode(node, pathBits);
                }
            }
            treeViewInForm.Nodes.Add(root);
        });
    }

    foreach (TreeNode tn in treeNode.Nodes)
    {
        RecursiveFunction(tn);
    }
}

So let's say, the relationship is Example A

Node
    Child(Contains Keyword)
        ChildOfChild(Does or does not contain Keyword)
            ...

What I get is Example B

Node
    Child(Contains Keyword)

or Example C, if ChildOfChild contains the keyword

Node
    Child(Contains Keyword)
Node
    Child(Contains Keyword)
        ChildOfChild(Does contain Keyword)

How do I get Example A, collapsed up to the point of the first child(or parent) that contains the keyword?

1 Answers

You can adopt the solution posted in Filter TreeView with all nodes and childs which provides a better and faster way to filter a TreeView. Pick (remove) the non-matches instead of creating a new tree. However, you need to modify it a little bit to work for you.

Anyway, based on the idea, here's a suggested version.

// +
using System.Text.RegularExpressions;
// ...

private readonly List<TreeNode> backupNodes = new List<TreeNode>();

private void SomeCaller()
{
    // Clear and rebuild this list whenever the tree changes.
    if (!backupNodes.Any())
    {
        backupNodes.AddRange(treeViewInForm.Nodes
            .Cast<TreeNode>()
            .Select(n => n.Clone() as TreeNode));
    }

    treeViewInForm.Nodes.Clear();
    treeViewInForm.Nodes.AddRange(backupNodes
        .Select(n => n.Clone() as TreeNode)
        .ToArray());

    if (txtKeyword.Text.Trim().Length == 0) return;

    Filter(treeViewInForm, txtKeyword.Text);
}       

private void Filter(TreeView tv, string keyword)
{
    IEnumerable<TreeNode> GetAllNodes(TreeNodeCollection Nodes)
    {
        foreach (TreeNode tn in Nodes)
        {
            yield return tn;

            foreach (TreeNode child in GetAllNodes(tn.Nodes))
                yield return child;
        }
    }

    tv.BeginUpdate();

    // .Reverse() to process the deepest nodes first...
    foreach (var node in GetAllNodes(tv.Nodes).Reverse())
    {
        if (!Regex.IsMatch(node.Text, keyword, RegexOptions.IgnoreCase))
        {
            // This `if` is missing in the original code.
            // Without it, a non-match parent will be removed
            // even if it has child matches at any level.
            if (node.Nodes.Count == 0)
            {
                if (node.Parent != null)
                    node.Parent.Nodes.Remove(node);
                else
                    tv.Nodes.Remove(node);
            }
        }
    }

    tv.ExpandAll();
    tv.EndUpdate();
}
Related