static void

DirTreeViewHelper (for Windows.Forms)

A lazy-loaded directory that will display in an existing treeview. It's like FolderBrowserDialog when you don't want a dialog. See DirectoryView for a UserControl that uses it.. You'll need to hook your treeView to an imageList (image indexes from 0: folder image; drive; disabled drive; computer/desktop).

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Windows.Forms;
 
namespace Library.Forms
{
    /// <summary>
    /// Helper class to update a treeview with a directory (insert path via <see cref="PopulateTreeView"/>)
    /// </summary>
    /// <remarks>
    /// <list type="table">
    /// <listheader>
    /// <term>TreeView event</term>
    /// <description>Call this method</description>
    /// </listheader>
    /// <item>
    /// <term>Constructor</term>
    /// <description>Ctor with Treeview reference, then <see cref="PopulateTreeView"/></description> with directoryInfo
    /// </item>
    /// <item>
    /// <term>NodeMouseClick</term>
    /// <description><see cref="GetDirectory"/></description>
    /// </item>
    /// <item>
    /// <term>BeforeExpand</term>
    /// <description><see cref="ExpandNode"/></description>
    /// </item>
    /// </list>
    /// </remarks>
    public class DirTreeViewHelper
    {
        #region Public methods
        public DirTreeViewHelper(TreeView tv)
        {
            TreeView = tv;
            TreeView.HideSelection = false; //show selection if control does not have focus
        }
 
        public TreeView TreeView { get; set; }
 
        /// <summary>
        /// Populates the tree view for a specific directory
        /// </summary>
        /// <param name="dir">The directory.</param>
        public void PopulateTreeView(DirectoryInfo dir)
        {
            // Suppress repainting the TreeView until all the objects have been created.
            TreeView.BeginUpdate();
 
            if (TreeView.Nodes.Count == 0)
                InitTreeView(dir);
            else
                UpdateTree(dir);
            TreeView.EndUpdate();
        }
 
        /// <summary>
        /// Gets the directory from a treeNode. Forces a lazy load of child subdirectories
        /// </summary>
        /// <param name="node">The node.</param>
        /// <returns></returns>
        public DirectoryInfo GetDirectory(TreeNode node)
        {
            DirectoryInfo dir = node.Tag as DirectoryInfo;
            if (dir == null)
            {
                DriveInfo drive = node.Tag as DriveInfo;
                if (drive != null && drive.IsReady)
                    dir = drive.RootDirectory;
            }
            if (dir != null)
            {
                dir.Refresh();
                if (!dir.Exists)
                {
                    node.Remove();
                    return null;
                }
                //lazy load
                DirectoryInfo[] subSubDirs = dir.GetDirectories();
                if (subSubDirs.Length > 0 && node.Nodes.Count == 0)
                {
                    GetDirectories(subSubDirs, node);
                }
            }
            return dir;
        }
 
        /// <summary>
        /// Expands the TreeNode. Forces a lazy load of the grandchild nodes.
        /// </summary>
        /// <param name="node">The node.</param>
        public void ExpandNode(TreeNode node)
        {
            DirectoryInfo di = node.Tag as DirectoryInfo;
            di.Refresh();
            if (!di.Exists)
            {
                node.Remove();
                return;
            }
            Debug.WriteLine("Expanding " + ((di != null) ? di.FullName : null));
            foreach (TreeNode child in node.Nodes)
            {
                //check all child nodes are populated
                if (child.Nodes.Count > 0) continue;
                DirectoryInfo dir = child.Tag as DirectoryInfo;
                if (dir == null) continue;
                dir.Refresh();
                if (!dir.Exists)
                {
                    child.Remove();
                    continue;
                }
                Debug.WriteLine("Expanding " + dir.FullName);
                DirectoryInfo[] subDirs = dir.GetDirectories();
                if (subDirs.Length > 0)
                {
                    GetDirectories(subDirs, child);
                }
            }
        }
        #endregion
 
        /// <summary>
        /// Updates the treeview (nodes already exist).
        /// Add the new folders (if doesn't already exist)
        /// Expand treenodes down to the directory
        /// </summary>
        /// <param name="dir">The directory.</param>
        private void UpdateTree(DirectoryInfo dir)
        {
            TreeNode treeRoot = TreeView.Nodes[0];
            treeRoot.Expand(); //in case the root node isn't expanded
            DirectoryInfo driveRoot = dir.Root;
            TreeNode driveRootNode = FindSubNode(treeRoot, driveRoot.Name);
            if (driveRootNode == null)
            {
                AddFolderInNewDrive(dir, treeRoot, driveRoot);
                return;
            }
            UpdateTreeFromRoot(dir, driveRootNode);
        }
 
        /// <summary>
        /// Updates the tree from node that contains the drive.
        /// </summary>
        /// <param name="dir">The directory.</param>
        /// <param name="driveRootNode">The drive root node.</param>
        private void UpdateTreeFromRoot(FileSystemInfo dir, TreeNode driveRootNode)
        {
            driveRootNode.Expand(); //expand the root eg C:\
            //from root go up the parents until we find one that exists, and join it up
            string[] folders = dir.FullName.Split(Path.DirectorySeparatorChar);
            TreeNode node = driveRootNode; //keep track of the node level
            for (int i = 1; i < folders.Length; i++) //loop down each subnode
            {
                string folder = folders[i];
                TreeNode dirNode = FindSubNode(node, folder);
                if (dirNode != null) //already exists, ensure expanded
                {
                    node = dirNode;
                    node.Expand();
                    continue;
                }
                //create new node
                Debug.WriteLine("Creating new subNode");
                DirectoryInfo subDir =
                    new DirectoryInfo(string.Join(Path.DirectorySeparatorChar.ToString(), folders, 0, i));
                node = new TreeNode(subDir.Name, 0, 0);
                node.Tag = subDir;
                node.Expand();
            }
            TreeView.SelectedNode = node;
        }
 
        /// <summary>
        /// Finds the sub TreeNode which has the specified name string.
        /// </summary>
        /// <param name="root">The TreeNode root.</param>
        /// <param name="name">The name (folder name in this case.</param>
        /// <returns></returns>
        private static TreeNode FindSubNode(TreeNode root, string name)
        {
            TreeNode found = null;
            foreach (TreeNode subNode in root.Nodes)
            {
                if (subNode.Text.Equals(name, StringComparison.OrdinalIgnoreCase))
                {
                    found = subNode;
                    break;
                }
            }
            return found;
        }
 
        /// <summary>
        /// Adds the folder into a new drive that has been added (eg a USB drive)
        /// </summary>
        /// <param name="dir">The directory.</param>
        /// <param name="rootNode">The root node.</param>
        /// <param name="rootDir">The root directory.</param>
        private void AddFolderInNewDrive(DirectoryInfo dir, TreeNode rootNode, FileSystemInfo rootDir)
        {
            //add this drive
            TreeNode node = new TreeNode(rootDir.Name, 1, 1);
            rootNode.Nodes.Add(node);
            node.Tag = rootDir;
            if (!IsSame(rootDir, dir)) PopulateToPath(node, dir);
            node.Expand();
        }
 
        /// <summary>
        /// Initialize the tree view.
        /// The structure is desktop/drives/ and then expands down to specified directory
        /// </summary>
        /// <param name="dir">The directory.</param>
        private void InitTreeView(DirectoryInfo dir)
        {
            DirectoryInfo desktop = new DirectoryInfo(Environment.GetFolderPath(Environment.SpecialFolder.Desktop));
            TreeNode treeRoot = new TreeNode(desktop.Name, 3, 3);
            treeRoot.Tag = desktop;
            TreeView.Nodes.Add(treeRoot);
            foreach (DriveInfo drive in DriveInfo.GetDrives())
            {
                int img = drive.IsReady ? 1 : 2;
                TreeNode node = new TreeNode(drive.Name, img, img);
                treeRoot.Nodes.Add(node);
                if (!drive.IsReady)
                {
                    node.Tag = drive;
                    continue;
                }
                node.Tag = drive.RootDirectory;
                //if this drive is the root of the directory we're showing
                if (IsSame(drive.RootDirectory, dir.Root))
                {
                    //if the drive root is the directory we want, no need to populate down
                    if (!IsSame(drive.RootDirectory, dir))
                        PopulateToPath(node, dir);
                    else
                        GetDirectories(drive.RootDirectory.GetDirectories(), node);
                }
                else
                {
                    GetDirectories(drive.RootDirectory.GetDirectories(), node);
                }
            }
            treeRoot.Expand();
        }
 
        /// <summary>
        /// Determines whether the specified directories are same by name
        /// </summary>
        /// <param name="d1">The first directory.</param>
        /// <param name="d2">The second directory.</param>
        /// <returns>
        /// <c>true</c> if the specified directory is same; otherwise, <c>false</c>.
        /// </returns>
        private static bool IsSame(FileSystemInfo d1, FileSystemInfo d2)
        {
            return d1.Name.Equals(d2.Name, StringComparison.OrdinalIgnoreCase);
        }
 
        /// <summary>
        /// Populates TreeView down to a specific path.
        /// </summary>
        /// <param name="node">The node.</param>
        /// <param name="dir">The directory.</param>
        private void PopulateToPath(TreeNode node, DirectoryInfo dir)
        {
            if (!dir.Exists) return;
            TreeNode baseNode;
 
            //look up
            DirectoryInfo root = dir.Root;
            if (root == dir)
                baseNode = node;
            else
                baseNode = PopulateParents(dir, root, node);
 
            GetDirectories(dir.GetDirectories(), baseNode);
            baseNode.Expand();
            TreeView.SelectedNode = baseNode;
        }
 
        /// <summary>
        /// Populates the parents of a directory until it joins the root directory (i.e. the drive root)
        /// </summary>
        /// <param name="startDir">The start directory.</param>
        /// <param name="rootDir">The root directory.</param>
        /// <param name="node">The node.</param>
        /// <returns></returns>
        private static TreeNode PopulateParents(DirectoryInfo startDir,
                                                FileSystemInfo rootDir,
                                                TreeNode node)
        {
            TreeNode baseNode = MakeNode(startDir);
            TreeNode child = baseNode;
            DirectoryInfo parent = startDir.Parent;
            while (parent != null && !IsSame(parent, rootDir))
            {
                TreeNode aNode = MakeNode(parent);
                foreach (DirectoryInfo subDir in parent.GetDirectories())
                {
                    if (subDir.Name != child.Text)
                        aNode.Nodes.Add(MakeParentNode(subDir));
                    else
                        aNode.Nodes.Add(child);
                }
                aNode.Expand();
                parent = parent.Parent;
                child = aNode;
            }
            node.Nodes.Add(child);
            node.Expand();
            return baseNode;
        }
 
        /// <summary>
        /// Makes a TreeNode for a folder.
        /// </summary>
        /// <param name="dir">The directory.</param>
        /// <returns></returns>
        private static TreeNode MakeNode(FileSystemInfo dir)
        {
            Debug.WriteLine("Node " + dir.FullName);
            TreeNode node = new TreeNode(dir.Name, 0, 0);
            node.Tag = dir;
            return node;
        }
 
        /// <summary>
        /// Makes the parent TreeNode.
        /// </summary>
        /// <param name="dir">The directory.</param>
        /// <returns></returns>
        private static TreeNode MakeParentNode(DirectoryInfo dir)
        {
            TreeNode node = MakeNode(dir);
            DirectoryInfo[] subSubDirs = dir.GetDirectories();
            if (subSubDirs.Length != 0)
            {
                foreach (DirectoryInfo subDir in subSubDirs)
                {
                    Debug.WriteLine("ParentNode " + subDir.FullName);
                    node.Nodes.Add(MakeNode(subDir));
                }
            }
            return node;
        }
 
        /// <summary>
        /// Add the subdirectories to a TreeNode. Not recursive so we can lazy load.
        /// </summary>
        /// <param name="subDirs">The sub dirs.</param>
        /// <param name="nodeToAddTo">The node to add to.</param>
        private static void GetDirectories(IEnumerable<DirectoryInfo> subDirs, TreeNode nodeToAddTo)
        {
            foreach (DirectoryInfo subDir in subDirs)
            {
                Debug.WriteLine(subDir.FullName);
                if ((subDir.Attributes & FileAttributes.Hidden) != 0) continue;
                TreeNode node = new TreeNode(subDir.Name, 0, 0);
                node.Tag = subDir;
                //recurse with a depth marker
                //DirectoryInfo[] subSubDirs = subDir.GetDirectories();
                //if (subSubDirs.Length != 0 && depth < 3)
                //    GetDirectories(subSubDirs, node, depth + 1);
                nodeToAddTo.Nodes.Add(node);
            }
        }
    }
}