@AngryGrey
Инженер АСУТП

Как обновить TreeView в реальном времени данными из другого потока?

Занимаюсь написанием своего OPC-клиента.
Возникла проблема, которую я пока не могу решить.
После подключения, я хочу построить дерево из содержимого сервера. Для этого я написал метод который рекурсивно бродит по ветвям сервера и получает о них информацию. На основе это информации создаются новые узды для дерева. Проблема в том, что построение идет долго и форма всё это время не активна.
Я переместил построения дерева в отдельный поток, но тогда узлы в дереве вообще не появляются (с потоками я пока не очень дружу). Я хочу добиться эффекта при котором в отдельном потоке будет происходить поиск и создание новых узлов дерева, но при этом узлы которые уже добавлены, сразу отображались бы на форме. Только я не могу понять как это реализовать.
  • Вопрос задан
  • 1249 просмотров
Решения вопроса 1
@AngryGrey Автор вопроса
Инженер АСУТП
Я решил эту проблему.
Для начала запустил метод в новом потоке:

private void FrOpcBrowser_Shown(object sender, EventArgs e)
        {
            try
            {
                currentServer.Connect(serverID);
                Text = serverID;
                treeView1.Nodes.Clear();
                treeView1.Nodes.Add(new TreeNode(serverID));
                treeView1.SelectedNode = treeView1.Nodes[0];
                TreeNode startNode = treeView1.SelectedNode;
                Thread thread = new Thread(delegate () { DoBrowse(startNode, -1); });
                thread.Start();
            }
            catch(Exception)
            {
                MessageBox.Show("Oops");
                Close();
            }
        }


Затем, когда метод DoBrowse проверит все условия, то запускается метод RecureBrowse

private void DoBrowse(TreeNode startNode, int depth)
        {
            try
            {
                OPCNAMESPACETYPE opcOrgi = currentServer.QueryOrganization();

                if (opcOrgi == OPCNAMESPACETYPE.OPC_NS_HIERARCHIAL)
                {
                    //Устанавливем курсор на корневой узел дерева сервера
                    currentServer.ChangeBrowsePosition(OPCBROWSEDIRECTION.OPC_BROWSE_TO, "");
                    //Проверяем не является ли выбранный узел корнем сервера
                    if (startNode.FullPath.Length > serverID.Length)
                    {
                        //Если выбранный узел не корень сервера, то получаем полный путь для данного узла
                        string[] splitpath = startNode.FullPath.Substring(serverID.Length + 1).Split(new char[] { '\t', '\\' });
                        //Имея полный путь, спускаемся до выбранного узла на сервере
                        foreach (string n in splitpath)
                        {
                            currentServer.ChangeBrowsePosition(OPCBROWSEDIRECTION.OPC_BROWSE_DOWN, n);
                        }
                    }
                    RecurBrowse(startNode, depth);
                }
            }
            catch(Exception)
            {
                MessageBox.Show("browse error!", "DoBrowse", MessageBoxButtons.OK, MessageBoxIcon.Warning);
                return;
            }
            return;
        }

        private bool RecurBrowse(TreeNode startNode, int depth)
        {
            try
            {
                if (depth == 0)
                {
                    return true;
                }
                currentServer.Browse(OPCBROWSETYPE.OPC_BRANCH, out ArrayList lst);
                if (lst == null || lst.Count < 1)
                {
                    return true;
                }

                foreach(string branch in lst)
                {
                    TreeNode nextNode = new TreeNode(branch);
                    AddNode(startNode, nextNode);
                    currentServer.ChangeBrowsePosition(OPCBROWSEDIRECTION.OPC_BROWSE_DOWN, branch);
                    RecurBrowse(nextNode, depth - 1);
                    currentServer.ChangeBrowsePosition(OPCBROWSEDIRECTION.OPC_BROWSE_UP, "");
                    
                }
            }
            catch(Exception)
            {
                MessageBox.Show(this, "browse error!", "RecurBrowse", MessageBoxButtons.OK, MessageBoxIcon.Warning);
                return false;
            }
            return true;
        }


В методе RecureBrowse добавление новой ноды происход вызовом метода AddNode, который вызывается (как я понял) в том потоке, которому принадлежит родительская нода.

private void AddNode(TreeNode parentNode, TreeNode childNode)
        {
            if (InvokeRequired)
            {
                Invoke(new Action<TreeNode, TreeNode>(AddNode), new TreeNode[] { parentNode, childNode });
                return;
            }
            parentNode.Nodes.Add(childNode);
        }


Работает медленно, но работает :)
Ответ написан
Комментировать
Пригласить эксперта
Ответы на вопрос 2
yarosroman
@yarosroman Куратор тега C#
C# the best
ну во первых посмотрите, что такое ленивое добавление узлов, и доступ к UI Thread осуществлять через Application.Current.Dispatcher
Ответ написан
jamakasi666
@jamakasi666
Просто IT'шник.
Я конечно не спец в .net но скорее всего нужно пойти по принципу семафоров т.к. будет меньше шансов острелить себе ноги. По аналогии с явой в нэте есть класс монитора и есть аналогии явовской invokeLater только более гибкие.
А код будет примерно следующий.
общая коллекция для нод. Первый поток ждет когда коллекция освободится и поток разбудят или вызовут некий метод обновления из этой коллекции. Второй поток шерудит и наполняет коллекцию и по некоторым значениям(накопил 20 значений например) будит первый поток или вызывает метод обновления в нем. В целом обезопасишь себя от поточной гонки.
Ответ написан
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Войти через центр авторизации
Похожие вопросы