I have the following code which builds a TreeView using the folders from the system when the user expands a treenode.
protected override void OnBeforeExpand(TreeViewCancelEventArgs e)
{
if (!_expandedCache.Contains((string)e.Node.Tag))
{
BeginUpdate();
ShellFileGetInfo.FolderIcons fi;
IEnumerable<string> dirs;
string path;
string currentPath;
_expandedCache.Add((string)e.Node.Tag);
TreeNode n;
foreach (TreeNode node in e.Node.Nodes)
{
try
{
path = (string)node.Tag;
dirs = Directory.EnumerateDirectories(path).OrderBy(d => d).Select(d => d.Split(Path.DirectorySeparatorChar).Last());
foreach (string dir in dirs)
{
currentPath = Path.Combine(path, dir);
if (File.Exists(Path.Combine(currentPath, "desktop.ini")) == true)
{
fi = ShellFileGetInfo.GetFolderIcon(currentPath, false);
ImageList.Images.Add(fi.closed);
ImageList.Images.Add(fi.open);
n = node.Nodes.Add(currentPath, dir, ImageList.Images.Count - 2, ImageList.Images.Count - 1);
n.Tag = currentPath;
}
else
{
n = node.Nodes.Add(currentPath, dir, 0, 1);
n.Tag = currentPath;
}
}
}
catch (UnauthorizedAccessException)
{
}
}
EndUpdate();
}
base.OnBeforeExpand(e);
}
The code to setup the initial TreeView is done via my SetRoot
method which is as follows :
public void SetRoot(string path)
{
_expandedCache = new List<string>();
Tag = path;
BeginUpdate();
ShellFileGetInfo.FolderIcons fi;
IEnumerable<string> dirs;
string currentPath;
TreeNode n;
dirs = Directory.EnumerateDirectories(path).OrderBy(d => d).Select(d => d.Split(Path.DirectorySeparatorChar).Last());
foreach (string dir in dirs)
{
currentPath = Path.Combine(path, dir);
if (File.Exists(Path.Combine(currentPath, "desktop.ini")) == true)
{
fi = ShellFileGetInfo.GetFolderIcon(currentPath, false);
ImageList.Images.Add(fi.closed);
ImageList.Images.Add(fi.open);
n = Nodes.Add(currentPath, dir, ImageList.Images.Count - 2, ImageList.Images.Count - 1);
n.Tag = currentPath;
}
else
{
n = Nodes.Add(currentPath, dir, 0, 1);
n.Tag = currentPath;
}
}
foreach (TreeNode node in Nodes)
{
path = (string)node.Tag;
try
{
dirs = Directory.EnumerateDirectories(path).OrderBy(d => d).Select(d => d.Split(Path.DirectorySeparatorChar).Last());
foreach (string dir in dirs)
{
currentPath = Path.Combine(path, dir);
if (File.Exists(Path.Combine(currentPath, "desktop.ini")) == true)
{
fi = ShellFileGetInfo.GetFolderIcon(currentPath, false);
ImageList.Images.Add(fi.closed);
ImageList.Images.Add(fi.open);
n = node.Nodes.Add(currentPath, dir, ImageList.Images.Count - 2, ImageList.Images.Count - 1);
n.Tag = currentPath;
}
else
{
n = node.Nodes.Add(currentPath, dir, 0, 1);
n.Tag = currentPath;
}
}
}
catch (UnauthorizedAccessException)
{
}
}
EndUpdate();
}
In order for the node to show it is expandable, I must load into the treeview at a minimum 1 additional level deep. So for "c:\windows\" i need to load system32, etc. When the user expands "Windows", in the OnBeforeExpand, i need to populate all the subfolders' subfolders to make those appear expandable. To prevent loading the tree contents on re-expansion (when the user collapses Windows and re-expands Windows), I cache a list of the TreeNodes which have already been expanded at some point. (this obviously has future repercussions like if the user adds a folder in windows explorer, it not showing, but that is a different story).
When it comes to getting special icons, I found the cheapest way to minimize on this is to only use the SHGetFileInfo
p/invoke for folders that contain a file named 'desktop.ini'.
My problem is, no matter what I do, I can not make this TreeView perform within any reasonable standard. It takes 9 seconds to expand the "Windows" tree, and 4 seconds to expand "inetpub" which only has 4 folders. I believe the slow-down is due to UnauthorizedAccessException
which there is no way that I can see to enumerate directories without encountering that.
as you can see, visually it is operating as expected, but expanding some folders, takes a really long time (much longer than windows explorer). The initial view as you see took about 200ms to load (still slow, but bearable)
took 8 seconds at best, 14 seconds at worst over 10 tests to populate - windows explorer is instant
What can I do to improve the performance of this further ? ... so that this is viable to use, not worse than Windows Explorer's version !