2

ファイルエクスプローラーを自作してみたい。すべてのドライブのすべてのディレクトリを列挙するアルゴリズムがあります。しかし、それはあまりにもゆっくりと実行されます。これが私のコードです:

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 秒以上かかります。列挙を改善する方法を教えてください。これは、スレッドと部分更新を使用する場合を除きます。後で修正できない場合は、理由を教えてください。

4

2 に答える 2

5

システム上のすべてのディレクトリを列挙するのにかかる時間は別として (遅延読み込みを実装すると、パフォーマンスが向上する可能性があります)、 への項目の挿入にTreeViewはかなりの時間がかかる場合があります。

からTreeView.BeginUpdate:

項目が一度に 1 つずつ TreeView に追加されている間、パフォーマンスを維持するには、BeginUpdate メソッドを呼び出します。BeginUpdate メソッドは、EndUpdate メソッドが呼び出されるまで、コントロールが描画されないようにします。ツリー ビュー コントロールに項目を追加するための推奨される方法は、AddRange メソッドを使用して、ツリー ノード項目の配列をツリー ビューに追加することです。

...

コントロールが描画を再開できるようにするには、すべてのツリー ノードがツリー ビューに追加されたときに EndUpdate メソッドを呼び出します。

.NET とは異なりますが、Raymond Chen のブログ投稿「ツリービューに多数の項目を効率的に挿入する方法」には、項目挿入のパフォーマンスを向上させる方法でコードを構造化するのに役立つ情報がさらに含まれています。

数万のような多数のアイテムをツリービューに挿入する必要がある場合は、それらを「後方に」挿入する方がはるかに効率的です。

編集

ディレクトリの列挙をスレッドに配置する例を次に示します。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コントロールを提供する必要があります。

于 2013-07-07T04:57:35.843 に答える
5

TreeView ポピュレーション コールの改善に関する以前の回答に加えて、MSDN のページ「方法: ディレクトリとファイルを列挙する」を読む必要があります。

最初の段落では、いくつかのパフォーマンスの向上について言及しています (文字列の代わりに DirectoryInfo の列挙可能なコレクションを使用) - 最後の行に注目してください。

名前の列挙可能な文字列のコレクションを返すメソッドを使用して、ディレクトリとファイルを列挙できます。DirectoryInfo、FileInfo、または FileSystemInfo オブジェクトの列挙可能なコレクションを返すメソッドを使用することもできます。ディレクトリとファイルの大規模なコレクションを操作する場合、列挙可能なコレクションは配列よりも優れたパフォーマンスを提供します。

ただし、この改善があっても、ApplyNodes 内のサブツリー全体を再帰的に下降するべきではありません。1 つのレベルを読み取り、現在のノードのエントリを追加するだけで、トラバースする必要があるサブディレクトリの数を大幅に減らすことができます (これは確かにファイル エクスプローラーが行うことです)。上記のta.speot.is

これら 2 つの改善を行っても必要なパフォーマンスが得られない場合は、さらに複雑にする必要があります (たとえば、トラバーサルを実行するためにバックグラウンド スレッドを実行するなど)。コードが最初のボトルネックです (つまり、タイミング コードとログを追加する必要があります)。

于 2013-07-07T08:21:32.690 に答える