我想尝试制作自己的文件浏览器。我有一个算法来枚举所有驱动器中的所有目录。但它运行得太慢了。这是我的代码:
public ExplorerForm()
{
InitializeComponent();
this.SuspendLayout();//Without this, it will be far more slow again!
string[] a = System.IO.Directory.GetLogicalDrives();// a is used for drive array
for(int b = 0; b < a.Length; b++)//B is an enumerator. Ussually it is only one letter
{
//Defining the node
TreeNode c = new TreeNode();//c i place for TreeNode
c.Text = a[b].Substring(0,2);
c.Tag = a[b];
ApplyNodes(a[b], ref c);
if(c != null) tv.Nodes.Add(a)
}
this.ResumeLayout(false);
}
private void ApplyNodes(string a, ref TreeNode b)//a=directory, b applied TreeNode
{
try{
List<string> c = new List<string>(Directory.EnumerateDirectories(a);//c = directories
if (c.Count == 0 ) return;
for(int d = 0; d < c.Count; d++)//d = enumerator.
{
TreeNode e = new TreeNode();//e = TreeNode
var z = c[b].Split(Convert.ToChar("/"));
e.Text = z[z.Length-1]
e.Tag = c[b];
ApplyNodes(c[b], e)
if(e != null) b.Nodes.Add(e)
}
}catch (UnauthorizedAccessException){
}catch (IOException){ //For test, It is removed. and my E: is not ready
}
}
电视是我的控制权。
它运行得非常慢。当我删除选择行时会显示,抛出IOException需要花费超过10秒的时间。帮助我如何改进枚举。这是使用线程和部分更新。如果以后不能修复,请告诉我原因。
答案 0 :(得分:5)
除了枚举系统中的所有目录所花费的时间(如果实施lazy loading,您可能会看到更好的性能),将项目插入TreeView
会占用大量的时间。
要在将项目一次添加到TreeView时保持性能,请调用BeginUpdate方法。 BeginUpdate方法阻止控件绘制,直到调用EndUpdate方法。 将项添加到树视图控件的首选方法是使用AddRange方法将树节点项数组添加到树视图中。
...
要允许控件恢复绘制,请在将所有树节点添加到树视图后调用EndUpdate方法。
虽然它脱离了.NET,但Raymond Chen的博客文章How to insert a large number of items into a treeview efficiently提供了更多信息,可以帮助您以更好的项目插入性能的方式构建代码。
如果您需要将大量项目插入到树视图中,例如成千上万,那么将它们“向后”插入它们会更有效。
修改强>
这是一个将目录枚举放到线程上的示例。观察TreeView
控件(或缺少控件)的可用性。如果不出意外,这可能是使用延迟加载的最佳参数。
private void Form1_Load(object sender, EventArgs e)
{
var treeNode = new TreeNode("Sea Drive");
treeView1.Nodes.Add(treeNode);
ThreadPool.QueueUserWorkItem(_ => TraverseDirectory("C:\\", treeNode));
}
private static readonly string DirectorySeparatorString = Path.DirectorySeparatorChar.ToString();
private void TraverseDirectory(string initialDirectoryPath, TreeNode initialTreeNode)
{
var initialTuples = new[] {Tuple.Create(initialDirectoryPath, initialTreeNode)};
var directoryQueue = new Queue<Tuple<string, TreeNode>>(initialTuples);
while (directoryQueue.Any())
{
var tuple = directoryQueue.Dequeue();
var parentDirectoryPath = tuple.Item1;
var parentTreeNode = tuple.Item2;
try
{
var treeNodes = new List<TreeNode>();
var directories = Directory.EnumerateDirectories(parentDirectoryPath);
foreach (var directoryPath in directories)
{
var lastDirectorySeparator = directoryPath.LastIndexOf(DirectorySeparatorString);
var directoryName = directoryPath.Substring(lastDirectorySeparator + 1);
// Add the tree node to our list of child
// nodes, for an eventual call to AddRange
var treeNode = new TreeNode(directoryName);
treeNodes.Add(treeNode);
// We have to go deeper
directoryQueue.Enqueue(Tuple.Create(directoryPath, treeNode));
}
// Run this operation on the main thread
Invoke((Action)(() => parentTreeNode.Nodes.AddRange(treeNodes.ToArray())));
}
catch (Exception exception)
{
Trace.Write(exception);
}
}
}
示例不完整;您需要提供自己的Form
和TreeView
控件。
答案 1 :(得分:5)
除了有关改善TreeView填充调用的先前答案之外,您还应该阅读MSDN页面How to: Enumerate Directories and Files
第一段提到了一些性能改进(使用DirectoryInfo的可枚举集合而不是字符串) - 注意最后一行:
您可以使用返回的方法枚举目录和文件 一个可枚举的名字字符串集合。你也可以使用 返回DirectoryInfo的可枚举集合的方法, FileInfo或FileSystemInfo对象。提供了可枚举的集合 使用大型集合时,性能优于数组 目录和文件。
然而,即使有这种改进,你也应该不以递归方式下降ApplyNodes中的整个子树。只需读取一个级别,添加当前节点的条目,就可以大大减少需要遍历的子目录的数量(这肯定是文件资源管理器所做的。)这就是提到的“延迟加载”技术的要点以上是ta.speot.is
如果这两个改进仍然没有给你你想要的性能,那么你可能想要增加更多的复杂性(例如运行后台线程来执行遍历),但你会想要确切地知道您的代码的哪一部分首先是您的瓶颈(意味着您将要添加时序代码和日志记录)