Search code examples
c#treeview

How to remove sub folders without any files then parent folders if their sub folders and themselves do not contain any files


I have a slightly modified codes to populate my treeView1 with only .PDF files. It works well, except it leaves with many empty folders. Is it a way to remove these folders?

  private void Begin()
    {
        treeView1.Nodes.Clear();
        if (Directory.Exists(FilePath))
            LoadDirectory(FilePath);
    }

    public void LoadDirectory(string Dir)
    {
        DirectoryInfo di = new DirectoryInfo(Dir);
        TreeNode tds = treeView1.Nodes.Add(di.Name);
        tds.Tag = di.FullName;
        tds.StateImageIndex = 0;
        LoadFiles(Dir, tds);
        LoadSubDirectories(Dir, tds);
    }

    private void LoadSubDirectories(string dir, TreeNode td)
    {
        // Get all subdirectories  
        string[] subdirectoryEntries = Directory.GetDirectories(dir);
        // Loop through them to see if they have any other subdirectories  
        foreach (string subdirectory in subdirectoryEntries)
        {
            DirectoryInfo di = new DirectoryInfo(subdirectory);
            TreeNode tds = td.Nodes.Add(di.Name);
            tds.StateImageIndex = 0;
            tds.Tag = di.FullName;
            LoadFiles(subdirectory, tds);
            LoadSubDirectories(subdirectory, tds);
        }
    }


    private void LoadFiles(string dir, TreeNode td)
    {
        string[] Files = Directory.GetFiles(dir, "*.PDF");
        // Loop through them to see files
        foreach (string file in Files)
        {
            FileInfo fi = new FileInfo(file);
            TreeNode tds = td.Nodes.Add(fi.Name);
            tds.Tag = fi.FullName;
            tds.StateImageIndex = 1;
        }
    }

Solution

  • I am assuming that you are being left with many empty folders in the TreeView control, and not trying to cleanup the file system. The way that I would do this would be to write a recursive method that performs this cleanup. Recursion allows us to traverse the entire tree and take action at the lowest level first, then work our way back up to the root node(s) to ensure 100% coverage with as little additional processing as possible.

    This is one way to implement such a solution:

     private void CleanupEmptyNodes(TreeView thisTreeView)
        {
            List<TreeNode> emptyChildren = new List<TreeNode>();
            foreach (TreeNode topLevelNode in thisTreeView.Nodes)
            {
                var isEmpty = CleanupEmptyNodesRecursive(topLevelNode);
                if (isEmpty)
                {
                    emptyChildren.Add(topLevelNode);
                }
            }
            foreach (TreeNode emptyChild in emptyChildren)
            {
                emptyChild.Remove();
            }
        }
        private bool CleanupEmptyNodesRecursive(TreeNode thisTreeNode)
        {
            List<TreeNode> emptyChildren = new List<TreeNode>();
    
            foreach(TreeNode node in thisTreeNode.Nodes)
            {
                var isEmpty = CleanupEmptyNodesRecursive(node);
                if (isEmpty)
                {
                    emptyChildren.Add(node);
                }
            }
    
            //you can't delete the nodes from within the foreach
            foreach(TreeNode emptyChild in emptyChildren)
            {
                emptyChild.Remove();
            }
            
            //were all the nodes removed?
            if (thisTreeNode.Nodes.Count == 0)
            {
                if(/*perform test here to tell whether this node IS the file or not*/)
                {
                    //is a file, so do not delete
                    return false;
                }
                else
                {
                    //this is a folder, and has no contents. Delete it!
                    return true;
                }
            }
            else
            {
                //has files within it so do not delete
                return false;
            }
        }
    

    Then you can just call it like this:

    CleanupEmptyNodes(treeView1);
    

    Personally, I would like it to be an extension method so I could just do treeView1.CleanupEmptyNodes(); but to each their own.