Обновление (много) элементов графического интерфейса с использованием результатов фонового потока - PullRequest
2 голосов
/ 04 июля 2011

У меня есть элемент управления TreeView, который заполняется элементами (по одному для каждого файла в определенном каталоге), и поток, работающий в фоновом режиме, который медленно заполняет каждый узел в дереве дополнительными данными.

Затем мне нужно TreeViewNode, чтобы обновить его текстовое поле на основе результатов.

Я пытался использовать BeginInvoke на рабочем для каждого элемента после его обработки, но это приводит к тому, что поток GUI перестает отвечать на запросы из-за количества узлов, которое обрабатывает фоновый работник. C # - Обновление графического интерфейса с использованием неосновной темы

public static void InvokeIfRequired(this System.Windows.Forms.Control c,
                                    Action action) {
    if (c.InvokeRequired) {
        c.Invoke((Action)(() => action()));
    }
    else {
        action();
    }
}

Я попытался объединить несколько обновлений узла в один рабочий элемент для графического интерфейса и использовать BeginInvoke для одновременного обновления нескольких элементов, во время которого фоновый поток ожидает сигнала для продолжения своей работы, но это также вызывает графический интерфейс перестать отвечать на запросы во время вызова метода (кажется, что обновление TreeViewNode - довольно дорогая операция?)

Наконец, я создал очередь в потоке графического интерфейса, который заполняется рабочим потоком и опрашивается, когда приложение находится в режиме ожидания, что хорошо работает, но похоже на полный взлом.

using System;
using System.Runtime.InteropServices;

namespace System
{
    struct PointAPI
    {
        public Int32 x;
        public Int32 y;
    }

    struct WindowsMessage
    {
        public Int32 hwnd;
        public Int32 message;
        public Int32 wParam;
        public Int32 lParam;
        public Int32 time;
        public PointAPI pt;
    }

    static class IdlePolling
    {
        [DllImport("user32.dll", SetLastError = true)]
        public static extern bool PeekMessage(ref WindowsMessage lpMsg,
                                                Int32 hwnd,
                                                Int32 wMsgFilterMin,
                                                Int32 wMsgFilterMax,
                                                Int32 wRemoveMsg);
        static public bool HasEventsPending()
        {
            WindowsMessage msg = new WindowsMessage();
            return PeekMessage(ref msg, 0, 0, 0, 0);
        }
    }
}

...
Application.Idle += mainForm.OnIdle;
...

    public partial class MainWindow : Form
    {
        ...
        public void OnIdle(object sender, EventArgs e)
        {
            while (IdlePolling.HasEventsPending() == false)
            {
                ConsumeGUIUpdateItem();
                Thread.Sleep(50);
            }
        }
    }

Итак, каков «правильный» способ обновления большого количества элементов графического интерфейса, который не приведет к зависанию потока GUI в процессе.

1 Ответ

1 голос
/ 11 июля 2011

русс, хороших попыток нет. в итоге: я думаю, что исправление заключается в вызове пакета вместо begininvoke (). позвольте мне объяснить:

У меня была похожая проблема, когда я добавлял много элементов в сетку данных в WPF, где рабочий начинает вызывать (или эквивалент в WPF) в потоке пользовательского интерфейса. я также обнаружил, что при высоких вызовах новые элементы были вставлены не так эффективно, как следовало бы. вызов asnc представлял высокий процент накладных расходов для этой конкретной установки, которая была интересна.

В конце я прибег к более простому методу и просто вставляю пакеты (как вы сделали), а вместо этого вставляю их с помощью асинхронной сортировки потоков, вместо этого я просто сделал WPF, эквивалентный синхронному Invoke () вместо async BeginInvoke ().

Как вы, вероятно, знаете, каждый раз, когда вы begininvoke (), .net помещает сообщение в Windows Message Pump для обработки вызова. однако, если ваше приложение занято другими сообщениями, вставка может занять некоторое время. Точно так же, слишком много begininvokes () препятствует обработке других сообщений Windows. в конечном итоге, если отправлено слишком много окон, они могут отбросить лишние, поэтому ваш метод никогда не будет вызван. это верно независимо от того, как вы знаете, c ++ или .net.

в конечном счете, окна могут выполнять только одну вещь в потоке пользовательского интерфейса, независимо от того, выполняет ли она типичный графический интерфейс или обрабатывает ваш асинхронный метод.

ура

...