Изменение значения узла в асинхронном TreeView

У меня есть древовидное представление, использующее BackGround worker для добавления узлов при их расширении. Я показываю сообщение «Загрузка..» после расширения и удаляю его после загрузки узлов. Работает нормально и все. Теперь я хочу изменить сообщение о загрузке на «Загрузка... узел n/n». Я смог это сделать, но проблема в том, что это сообщение не отображается (обновляется) во время выполнения операции добавления узла, а после ее завершения. Я не мог понять, что я делаю неправильно, и я надеюсь, что кто-то может пролить свет на это.

Вот мой код. Я отладил метод SetValue, и он правильно обновляет текст узла, но не отображается до конца операции.

private void t_AfterExpand(object sender, NodeEventArgs e)
{
    t.AppendNode(new object[] { "Loading.." }, e.Node);
    bw.RunWorkerAsync(new object[] { e.Node });
}

private void bw_DoWork(object sender, DoWorkEventArgs e)
{
    t.Invoke(new MethodInvoker( () => AddSubNodes(e.Argument) ));
    e.Result = e.Argument;
}

private void AddSubNodes(object arg)
{
    object[] args = arg as object[];
    TreeListNode parentNode = args[0] as TreeListNode;

    int nodeCount = 10;
    for (int i = 0; i < nodeCount; i++)
    {
        t.AppendNode(new object[] { "node cell text" }, parentNode);
        bw.ReportProgress(i, new object[]{ parentNode, "node: " + i.ToString() + "/" + nodeCount.ToString()});
    }
}

private void bw_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
    object[] args = e.UserState as object[];
    TreeListNode parentNode = args[0] as TreeListNode;
    string percentMsg = args[1].ToString(); //node: n/n message

    t.Invoke(new MethodInvoker(() => parentNode.Nodes[0].SetValue(0, percentMsg))); //change "Loading.." to "node: n/n"
    //parentNode.Nodes[0].SetValue(0, mesaj);
}

private void bw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
    object[] result = e.Result as object[];
    TreeListNode node = result[0] as TreeListNode;

    node.Nodes.RemoveAt(0); //remove loading text
}

person dstr    schedule 15.09.2009    source источник


Ответы (1)


(Основная) проблема связана с вашим bw_ProgressChanged. Ему не нужно ничего вызывать, потому что работа Bgw заключается в синхронизации ProgressChanged. Мне не больно, но я все равно потеряю Invoke.

Причина, по которой вы не видите никаких изменений, заключается в отсутствии функции Update().

private void bw_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
    object[] args = e.UserState as object[];
    TreeListNode parentNode = args[0] as TreeListNode;
    string percentMsg = args[1].ToString(); //node: n/n message

    parentNode.Nodes[0].SetValue(0, percentMsg); //change "Loading.." to "node: n/n"
    parentNode.TreeView.Update(); // or Form.Update

}

В bw_DoWork() есть еще одна проблема: вы используете Invoke в методе AddSubNodes(). В результате 99% вашего кода полностью выполняется в основном потоке, а ваше решение вообще не является многопоточным.

Я бы сделал что-то вроде:

//untested
private void bw_DoWork(object sender, DoWorkEventArgs e)
{
//    t.Invoke(new MethodInvoker( () => AddSubNodes(e.Argument) ));
    object[] args = arg as object[];
    TreeListNode parentNode = e;

    var newNodes = new List<TreeNode>();

    int nodeCount = 10;
    for (int i = 0; i < nodeCount; i++)
    {
       // t.AppendNode(new object[] { "node cell text" }, parentNode);
        newNodes.Add(new object[] { "node cell text" }); // ???
        bw.ReportProgress(i, new object[]{ parentNode, "node: " + i.ToString() + "/" + nodeCount.ToString()});
    }

    // e.Result = e.Argument;
    e.Result = newNodes;
}

А затем в bw_RunWorkerCompleted быстро добавьте элементы newNodes к 't'.

person Henk Holterman    schedule 15.09.2009
comment
Большое спасибо. Как человек, который только начал использовать BackgroundWorker, я хотел бы спросить две вещи: 1. DoWork не работает в отдельном потоке? Таким образом, не должны ли мои вызовы AddSubNodes() работать в этом отдельном потоке? 2. Если мне нужно выполнять работу в RunWorkerCompleted, какой смысл в DoWork? Я думаю, что я что-то упускаю с BackGroundWorker.. - person dstr; 15.09.2009
comment
1) Да, DoWork является асинхронным, но Invoke отправляет (всю) работу обратно в основной поток. 2) Вы должны использовать BGW для выполнения большей части работы, не связанной с графическим интерфейсом (извлечение данных для узлов), и касаться графического интерфейса только через Invoke, UpdateProgress или Completed. - person Henk Holterman; 15.09.2009
comment
Я переместил свой вызов AddSubNodes() из DoWork в RunWorkerCompleted, дерево все еще зависает во время работы. Пробовал вызывать, тот же сброс. Что не так с этим? - person dstr; 16.09.2009
comment
Вы должны разделить AddSubNodes (GUI и не-GUI). - person Henk Holterman; 16.09.2009