Search code examples
c#algorithmc#-3.0directory-structure

What algorithm can I use to recursively load an entire directory, starting from a specified path?


I'm building a custom file dialog, and my problem is its taking too long to load.

The dialog begins with an InitialDirectory property, and I am looking to find a way to load the directory tree for the InitialDirectory first, followed by the rest of the directories in a background thread.

For example, if the InitialDirectory was C:\Users\User12345\MyDocuments, then it should load all folders in

C:\
C:\Users
C:\User12345
C:\Users\User12345\MyDocuments

then kick off a background thread to load all the remaining directories.

Is there an fast and easy way to use recursion to load first the InitialDirectory, than everything else, without duplicating any items?

I'm struggling to find a high-performing way to do this, since checking for the existance of a folder with code like if (!Directory.Contains(f => f.FullName == folder.FullName)) slows down the load by quite a bit.

My current code to load the full directory looks something like this:

private void LoadDirectory()
{
    string root = @"C:\";
    var rootNode = new DirectoryModel() { Name = root, FullName = root };
    this.Directory.Add(rootNode);

    DirectoryInfo info = new DirectoryInfo(root);
    IEnumerable<DirectoryInfo> subDirectories = info.GetDirectories()
            .Where(d => ((d.Attributes & FileAttributes.Hidden) != FileAttributes.Hidden)
                && ((d.Attributes & FileAttributes.System) != FileAttributes.System));

    LoadDirectories(subDirectories, root);
}

private void LoadDirectories(IEnumerable<DirectoryInfo> subDirs, string parentName)
{
    IEnumerable<DirectoryInfo> subdirectories;
    foreach (DirectoryInfo folder in subDirs)
    {
        var node = new DirectoryModel() { Name = folder.Name, FullName = folder.FullName, ParentName = parentName };

        Directory.Add(node);

        try
        {
            subdirectories = folder.GetDirectories("*", SearchOption.TopDirectoryOnly)
                .Where(d => ((d.Attributes & FileAttributes.Hidden) != FileAttributes.Hidden)
                    && ((d.Attributes & FileAttributes.System) != FileAttributes.System));
        }
        catch (UnauthorizedAccessException e)
        {
            continue;
        }
        catch (System.IO.DirectoryNotFoundException e)
        {
            continue;
        }

        if (subdirectories.Count() != 0)
            LoadDirectories(subdirectories, folder.FullName);
    }
}

Note that the Directory collection is a flat collection - there is no hierarchy in the data models.


Solution

  • It depends on how you present to the user your data, imo.

    If you use a TreeView control, you can easily avoid to load all data in background, but just load only the root of your tree (like you already do as much as I understood).

    In this way, having a procedure that loads all sub-directory names and files of a specified directory you just need to wait until user clicks on the [+] in order to see the directory content interesting to him. So you run your procedure and populate the tree.

    This is actually highest performant way known to me to deal with this kind of stuff, and actually you will find this pattern almost in all "explorer like" products. The performance here achieved not by perfectly refined algorithm, but just by defining more convenient behavioral model.

    Hope this helps.