Search code examples
c#winformstreeview

Filter TreeView with all nodes and childs


I have a parent node which has a child, and this child has another child etc.. and they are all in a TreeView

So I create a global variable to save all my nodes as:

private TreeNodeCollection ProjectTreeView { get; set; }

Then I set data of the tree node into my global variable:

ProjectTreeView = this.tvProjectList.Nodes[0].Nodes;

And When I click a button I want to filter my TreeView, so first I clear the TreeView, then I iterate over the collection and only show the nodes that meet my condition:

private void rdoIssued_Click(object sender, EventArgs e)
{
    //blocks repainting tree till all objects loaded
    this.tvProjectList.BeginUpdate();
    this.tvProjectList.Nodes.Clear();

    foreach (TreeNode projectNode in ProjectTreeView)
    {
        if (bool.Parse(projectNode.Tag.ToString().Split('|')[8]) == true)
        {
            this.tvProjectList.Nodes.Add((TreeNode)projectNode.Clone());
        }

    }

    //enables redrawing tree after all objects have been added
    this.tvProjectList.EndUpdate();
}

The problem is it only clone the first Node but not the children. How can I clone a node with all children?


Solution

  • Doing the opposite is much easier. You could iterate through all the nodes to keep the filtered nodes and remove the rest. First, create a backup for the 0-level nodes.

    public partial class YourForm : Form
    {
        public YourForm()
        {
            InitializeComponent();
            //...
    
            BackUpTree();
        }
    
        private List<TreeNode> ProjectTreeView = new List<TreeNode>();
    
        private void BackUpTree()
        {
            if (ProjectTreeView.Count == 0)
                foreach (TreeNode tn in tvProjectList.Nodes)
                    ProjectTreeView.Add(tn.Clone() as TreeNode);
        }
    

    Create a method to reset the original tree:

        private void ResetTree(bool expandAll = false)
        {
            tvProjectList.BeginUpdate();
            tvProjectList.Nodes.Clear();
    
            foreach (var tn in ProjectTreeView)
                tvProjectList.Nodes.Add(tn.Clone() as TreeNode);
    
            if (expandAll) tvProjectList.ExpandAll();
            tvProjectList.EndUpdate();
        }
    

    Iterator function to get all the nodes:

        private IEnumerable<TreeNode> GetAllNodes(TreeNodeCollection Nodes)
        {
            foreach (TreeNode tn in Nodes)
            {
                yield return tn;
    
                foreach (TreeNode child in GetAllNodes(tn.Nodes))
                    yield return child;
            }
        }
    

    ... and a method to do the filter part:

        private void FilterTree(bool expandAll = false)
        {
            ResetTree(); // <- comment if you are doing multiple filters...
            tvProjectList.BeginUpdate();
    
            //.Reverse() is required here to iterate backward because the collections
            //are modified when removing nodes. You can call .ToList() instead to 
            //iterate forward.
            foreach (var node in GetAllNodes(tvProjectList.Nodes).Reverse())
            {
                if (bool.Parse(projectNode.Tag.ToString().Split('|')[8]) == false)
                    if (node.Parent is null)
                        tvProjectList.Nodes.Remove(node);
                    else
                        node.Parent.Nodes.Remove(node);
            }
    
            if (expandAll) tvProjectList.ExpandAll();
            tvProjectList.EndUpdate();
        }
    }