Отображение индикатора выполнения, который обновляется при создании большого количества элементов управления - PullRequest
0 голосов
/ 01 сентября 2011

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

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

Поскольку это WPF, я также не могу вызвать Application.DoEvents (). Поэтому мой вопрос: как я могу создать много элементов управления, при этом все еще имея возможность периодически обновлять визуальную часть графического интерфейса?

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

Я смотрел другие SO темы, но пока не могу найти хороший ответ

Создание элементов управления в потоке без пользовательского интерфейса

Создание WinForm в главном потоке с использованием фонового работника

Edit:

В соответствии с этой статьей MSDN http://msdn.microsoft.com/en-us/magazine/cc163328.aspx BackgroundWorker должен автоматически вызываться асинхронно в потоке пользовательского интерфейса, если это необходимо, но это не то поведение, которое я наблюдаю, поскольку я все еще вижу исключение между потоками.

Edit2: nvm, это не совсем так: BackgroundWorker все еще нужно вызывать Invoke?

Edit3: После некоторого прочтения и некоторых советов, это решение, к которому я пришел. У кого-нибудь есть советы / подсказки?

        // Events for reporting progress
        public event WorkStarted OnWorkStarted;
        public event WorkStatusChanged OnWorkStatusChanged;
        public event WorkCompleted OnWorkCompleted;


        private BackgroundWorker worker;
        private delegate void GuiThreadWork(object state);
        private PopulatableControlFactory factory = new PopulatableControlFactory();
        public Canvas canvas;

        public void PerformLayout(TreeNode node)
        {
            OnWorkStarted(this, "Testing");

            worker = new BackgroundWorker();
            worker.WorkerReportsProgress = true;
            worker.DoWork += new DoWorkEventHandler(worker_DoWork);            
            worker.ProgressChanged += new ProgressChangedEventHandler(worker_ProgressChanged);
            worker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(worker_RunWorkerCompleted);
            worker.RunWorkerAsync(node);
        }

        private void worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            OnWorkCompleted(this);
        }

        private void worker_ProgressChanged(object sender, ProgressChangedEventArgs e)
        {
            var workTuple = (Tuple<GuiThreadWork, TreeNode>)e.UserState;
            workTuple.First.Invoke(workTuple.Second); //Or begin invoke?

            if (OnWorkStatusChanged != null)
                OnWorkStatusChanged(this, e.ProgressPercentage);
        }

        private void worker_DoWork(object sender, DoWorkEventArgs e)
        {
            TreeNode node = (TreeNode)e.Argument;            
            Thread.Sleep(1000);
            worker.ReportProgress(33, Tuple.New(Place(node), node));
            Thread.Sleep(1000);
            worker.ReportProgress(66, Tuple.New(Place(node.children[0]), node.children[0]));
            Thread.Sleep(1000);
            worker.ReportProgress(100, Tuple.New(Place(node.children[1]), node.children[1]));
        }

        private GuiThreadWork Place(TreeNode node)
        {            
            GuiThreadWork threadWork = delegate(object state)
            {
                PopulatableControl control = factory.GetControl((TreeNode)state);
                Canvas.SetLeft(control, 100);
                Canvas.SetTop(control, 100);
                canvas.Children.Add(control);
            };

            return threadWork;
        }   

Вкратце: я использую событие progressChanged фонового рабочего, потому что оно всегда направляется в поток GUI. Я передаю ему кортеж делегата и некоторый штат. Таким образом, я всегда создаю элемент управления в потоке GUI и выполняю там все действия, оставаясь гибким.

1 Ответ

0 голосов
/ 01 сентября 2011

Обычно я не часто использую BackgroundWorker, но могу предложить следующее:

Логика для DoWork - она ​​выполняется в потоке без пользовательского интерфейса

  1. получить количество узлов, таквы можете сообщить о реальном прогрессе
  2. начать построение дерева (и вызвать Invoke для UI Dispatcher, чтобы поток пользовательского интерфейса добавляет узлы) и сообщить о прогрессе в ReportProgress как (уже добавленные узлы) / (общее количество узлов)перечисляя все узлы

в ProgressChanged, просто обновите некоторый ProgressBar с новым значением

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...