2013-07-06 2 views
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 
    } 
} 

tv - мой контроль. Он работает очень медленно. Он указывается, когда я удаляю выбранную строку, для исключения IOException потребуется больше 10 секунд. Помогите мне, как улучшить перечисление. Это за исключением использования потока и частичного обновления. Если позже не исправить, скажите мне, почему.

+0

запустить его в потоке. – Hogan

+0

'Перечисление каталога слишком медленно' - Купить жесткий диск. –

+10

Err..yeah. Вот почему вы не перечисляете все каталоги. Вы просто показываете те, которые вам нужны, когда они запрашиваются. – aquinas

ответ

5

Помимо времени, необходимого для перечисления всех каталогов вашей системы (вы, вероятно, увидите лучшую производительность, если вы реализуете lazy loading), вставка элементов в TreeView может занять значительное количество времени.

TreeView.BeginUpdate От:

Для поддержания производительности в то время как элементы добавляются по одному к TreeView, вызовите метод BeginUpdate. Метод BeginUpdate предотвращает рисование элемента управления до тех пор, пока не будет вызван метод EndUpdate. Предпочтительным способом добавления элементов в элемент управления древовидной структурой является использование метода AddRange для добавления массива элементов дерева дерева в древовидное представление.

...

Чтобы разрешить управление возобновить картину, вызовите метод EndUpdate, когда все узлы дерева, которые были добавлены в дерево.

Хотя он отходит от .NET, в блоге Реймонд Чен How to insert a large number of items into a treeview efficiently имеет больше информации, которая может помочь вам структурировать ваш код таким образом, что приводит к более высокой производительности для вставки элемента.

Если вам нужно вставить большое количество предметов в древовидную структуру, например десятки тысяч, то гораздо эффективнее вставить их «назад».

EDIT

Вот пример, который помещает перечисление каталогов на волоске. Соблюдайте удобство управления 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.

+0

- это TreeView.BeginUpdate() отличается от Form.SuspendLayout? –

+1

Да, поскольку это влияет на живопись, а не на макет. – siride

+1

Почему мы не читаем прекрасное руководство (http://msdn.microsoft.com/en-AU/library/system.windows.forms.control.suspendlayout (v = vs.110) .aspx), чтобы найти вне? * 'Control.SuspendLayout' Временно приостанавливает логику компоновки для элемента управления * vs. *' TreeView.BeginUpdate' Отключает перерисовку древовидного вида *. –

5

В дополнении к предыдущему ответу об улучшении вашего TreeView вызовов населения, вы должны прочитать страницу MSDN How to: Enumerate Directories and Files

В первом пункте упоминается некоторые улучшения производительности (с использованием перечисляемых коллекций DirectoryInfo вместо строк) - обратите внимание на последнюю строку :

Вы можете перечислить каталоги и файлы, используя методы, которые возвращают перечислимой коллекцию строк их имен. Вы также можете использовать методы , которые возвращают перечислимую коллекцию объектов DirectoryInfo, FileInfo или FileSystemInfo.Переменные коллекции предоставляют лучшую производительность, чем массивы при работе с большими наборами каталогов и файлов .

Даже с этим улучшение, однако, вы действительно должны не рекурсивно спускаться все поддерево внутри ApplyNodes. Просто прочитайте один уровень, добавив записи для своего текущего узла, чтобы значительно уменьшить количество подкаталогов, которые вам нужно пройти (это, безусловно, то, что делает File Explorer). Это точка упомянутой техники «ленивой загрузки» выше ta.speot.is

Если эти два улучшения по-прежнему не дают вам требуемой производительности, тогда вы можете добавить дополнительную сложность (например, запустить фоновый поток для выполнения вашего обхода), но вы будете хотите иметь представление о том, какая именно часть вашего кода является вашим узким местом в первую очередь (что означает, что вы хотите добавить временные коды и протоколирование)

+0

Использование foreach? Я пытался это сделать. Но узким местом является 'Directory.EnumerateDirectories()' –

+0

. Вы используете статические методы класса Directory. Попробуйте использовать класс DirectoryInfo вместо этого для перечисления, например. http://stackoverflow.com/questions/6239544/c-sharp-how-to-populate-treeview-with-file-system-directory-structure Это может помочь некоторым, но лучше всего удалить полностью рекурсивный обход поддерево директорий и только перемещаться, насколько это необходимо для текущего состояния TreeView (т. е. только пересекать другой уровень поддерева в ответ на пользователя, расширяющего узел) – holtavolt

Смежные вопросы