Ускорить загрузку списка строк в Treeview

#c# #multithreading #winforms #treeview

#c# #многопоточность #winforms #древовидное представление

Вопрос:

Stackflow и новичок в C # здесь!

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

Таким образом, примером может быть список строк, подобных этому (но намного больше):

c:driverstest1.txt
c:driverstest2.txt
c:driversfoldertest1.txt
c:brothertestertext1.zip
c:brotheranothertext2.zip
c:datacompany1accounts.rar
c:datacompany2accounts.rar

Treeview разделяет строки с помощью маркера обратной косой черты и аккуратно помещает их в вид проводника — это хорошо !.

tvRestore — это элемент управления Treeview в примере.

 foreach (string path in lstKeys)
        {

            lastNode = null;
            subPathAgg = string.Empty;
            foreach (string subPath in path.Split(new string[] { "\" }, StringSplitOptions.None))
            {
                foreach (string item in subPath.Split(new string[] { "\" }, StringSplitOptions.None))
                {
                    if (item == "" || item == null)
                    {
                        continue;
                    }
                    subPathAgg  = item   "\";

                    TreeNode[] n = tvRestore.Nodes.Find(subPathAgg, true);

                    if (n.Length > 0)
                    {
                        lastNode = n[0];

                        continue;
                    }
                    else
                    {
                        // lastNode = null;
                    }

                    TreeNode[] nodes = tvRestore.Nodes.Find(subPathAgg, true);
                    if (nodes.Length == 0)
                        if (lastNode == null)
                            lastNode = tvRestore.Nodes.Add(subPathAgg, item);
                        else
                            lastNode = lastNode.Nodes.Add(subPathAgg, item);
                    else
                        lastNode = nodes[0];
                }
            }
        }
  

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

В идеале код начнет заполнять treeview при запуске приложения, хотя я не хочу, чтобы приложение зависало на 30-40 секунд или дольше для больших списков.

Каков наилучший способ ускорить этот процесс?

Комментарии:

1. Попробуйте отключить рисование перед добавлением узлов с treeView1.BeginUpdate(); помощью, а затем снова включить его с помощью EndUpdate();

Ответ №1:

Есть несколько вещей, которые вы можете сделать, в том числе запустить это в фоновом режиме — мне нравится использовать Task.Run() для этого.

 private void Form1_Load(object sender, EventArgs e)
{
    // Show the user something
    treeView1.Nodes.Add("Loading...");
    // Run the tree load in the background
    Task.Run(() => LoadTree());
}
  

Затем ваша задача может создать TreeNode, содержащий все ваши новые узлы, и вызвать TreeView для добавления новых узлов в качестве диапазона, а также использовать BeginUpdate … EndUpdate для предотвращения визуальных обновлений до тех пор, пока не будут загружены все узлы.

 private void LoadTree()
{
    // Get a list of everything under the users' temp folder as an example
    string[] fileList;
    DirectoryInfo df = new DirectoryInfo(Path.GetTempPath());
    fileList = df.GetFiles("*.*",SearchOption.AllDirectories).Select<FileInfo, string>((f) => f.FullName).ToArray();

    // Parse the file list into a TreeNode collection
    TreeNode node = GetNodes(new TreeNode(), fileList);

    // Copy the new nodes to an array
    int nodeCount = node.Nodes.Count;
    TreeNode[] nodes = new TreeNode[nodeCount];
    node.Nodes.CopyTo(nodes, 0);

    // Invoke the treeview to add the nodes
    treeView1.Invoke((Action)delegate ()
    {
        treeView1.BeginUpdate(); // No visual updates until we say 
        treeView1.Nodes.Clear(); // Remove existing nodes
        treeView1.Nodes.AddRange(nodes); // Add the new nodes
        treeView1.EndUpdate(); // Allow the treeview to update visually
    });
}
  

Вот как я бы построил список древовидных узлов.

 private TreeNode GetNodes(TreeNode parent, string[] fileList)
{
    // build a TreeNode collection from the file list
    foreach (string strPath in fileList)
    {
        // Every time we parse a new file path, we start at the top level again
        TreeNode thisParent = parent;

        // split the file path into pieces at every backslash
        foreach (string pathPart in strPath.Split('\'))
        {
            // check if we already have a node for this
            TreeNode[] tn = thisParent.Nodes.Find(pathPart, false);

            if (tn.Length == 0)
            {
                // no node found, so add one
                thisParent = thisParent.Nodes.Add(pathPart,pathPart);
            }
            else
            {
                // we already have this node, so use it as the parent of the next part of the path
                thisParent = tn[0];
            }
        }

    }
    return parent;
}
  

На моей машине загрузка 56 000 узлов в treeview таким образом занимает около 1,5 секунд, и пользовательский интерфейс не блокируется.