C# 当最小化到下一级文件夹时,TreeView展开仍需要一段时间

C# 当最小化到下一级文件夹时,TreeView展开仍需要一段时间,c#,winforms,treeview,C#,Winforms,Treeview,我有以下代码,当用户展开treenode时,使用系统中的文件夹构建TreeView protected override void OnBeforeExpand(TreeViewCancelEventArgs e) { if (!_expandedCache.Contains((string)e.Node.Tag)) { BeginUpdate(); ShellFileGetInfo.FolderIcons fi; IEnumer

我有以下代码,当用户展开treenode时,使用系统中的文件夹构建TreeView

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);
}
为了让节点显示它是可扩展的,我必须以至少1个额外级别的深度加载到treeview中。因此,对于“c:\windows\”我需要加载system32,等等。当用户展开“windows”时,在OnBeforeExpand中,我需要填充所有子文件夹的子文件夹,使它们看起来是可展开的。为了防止在重新展开时加载树内容(当用户折叠窗口并重新展开窗口时),我缓存了在某个点已经展开的树节点列表。(这显然会对未来产生影响,比如如果用户在windows资源管理器中添加一个文件夹,它不会显示出来,但这是另一回事)

在获取特殊图标时,我发现最便宜的方法是对包含名为“desktop.ini”的文件的文件夹只使用
SHGetFileInfo
p/invoke

我的问题是,无论我做什么,我都不能让这个树视图在任何合理的标准内运行。展开“Windows”树需要9秒,展开“inetpub”需要4秒,而“inetpub”只有4个文件夹。我相信速度减慢是由于
未经授权的访问异常
造成的,我无法在不遇到该异常的情况下枚举目录

正如您所看到的,从视觉上看,它的操作与预期相符,但扩展某些文件夹需要很长的时间(比windows资源管理器长得多)。如您所见,初始视图的加载时间约为200毫秒(仍然很慢,但可以承受)

填充10个测试最多需要8秒,最差14秒-windows资源管理器是即时的


我可以做些什么来进一步提高此功能的性能。。。因此,这是可行的使用,不比Windows资源管理器的版本差

文件系统中存在试图在树中动态添加的数据。 根据操作系统和处理器的活动,以文件模式访问数据有时可能会减慢速度。 若您可以在某个数据库中移动数据,并通过从数据库中提取数据在树中添加节点,那个么速度会加快

您可以在SQLSERVER中的varbinary数据类型中将图像添加到数据库中


甚至Windows资源管理器也使用相同的技术。

我更喜欢在最低级别访问数据,即Windows资源管理器用于获取图标的相同机制。我实际上能够将其缩小到此处详述的范围:。文件访问最慢的部分是出于任何原因访问文件系统。你建议建立我自己的目录是不可行的,因为以目前的速度需要数百小时(使用目前我能找到的最快的pinvoke+文件检查方法)-考虑到文件夹图标可能会改变,这是不可行的长期。理想的解决方案是以与windows在explorer.exe中完全相同的方式访问这些信息(不像!)。
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();
}