Изменение элементов управления WPF из неосновного потока с помощью Dispatcher.Invoke - PullRequest
67 голосов
/ 29 октября 2009

Я недавно начал программировать в WPF и столкнулся со следующей проблемой. Я не понимаю, как использовать метод Dispatcher.Invoke(). У меня есть опыт работы с потоками, и я создал несколько простых программ для Windows Forms, в которых я только что использовал

Control.CheckForIllegalCrossThreadCalls = false;

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

Дело в том, что сейчас я делаю приложение WPF, которое извлекает данные в фоновом режиме, я запускаю новый поток, чтобы выполнить вызов для извлечения данных (с веб-сервера), теперь я хочу отобразить его в своей форме WPF , Дело в том, что я не могу установить какой-либо контроль из этой темы. Даже не лейбл или что-нибудь. Как это можно решить?

Ответ на комментарий:
@Jalfp:
Итак, я использую этот метод Dispatcher в «новом шаге», когда получаю данные? Или я должен заставить фонового работника получить данные, поместить их в поле и запустить новый поток, который ждет, пока это поле не заполнится, и вызвать диспетчера, чтобы показать полученные данные в элементы управления?

Ответы [ 3 ]

163 голосов
/ 29 октября 2009

Прежде всего следует понимать, что Dispatcher не предназначен для выполнения длительных операций блокировки (таких как извлечение данных из WebServer ...). Диспетчер можно использовать, когда вы хотите запустить операцию, которая будет выполняться в потоке пользовательского интерфейса (например, обновить значение индикатора выполнения).

Что вы можете сделать, это получить ваши данные в фоновом режиме и использовать метод ReportProgress для распространения изменений в потоке пользовательского интерфейса.

Если вам действительно нужно использовать Dispatcher напрямую, это довольно просто:

Application.Current.Dispatcher.BeginInvoke(
  DispatcherPriority.Background,
  new Action(() => this.progressBar.Value = 50));
25 голосов
/ 30 августа 2015

japf имеет ответ правильно. На всякий случай, если вы смотрите на многострочные действия, вы можете написать, как показано ниже.

Application.Current.Dispatcher.BeginInvoke(
  DispatcherPriority.Background,
  new Action(() => { 
    this.progressBar.Value = 50;
  }));

Информация для других пользователей, которые хотят знать о производительности:

Если ваш код ДОЛЖЕН быть написан для высокой производительности, вы можете сначала проверить, требуется ли вызов, используя флаг CheckAccess.

if(Application.Current.Dispatcher.CheckAccess())
{
    this.progressBar.Value = 50;
}
else
{
    Application.Current.Dispatcher.BeginInvoke(
      DispatcherPriority.Background,
      new Action(() => { 
        this.progressBar.Value = 50;
      }));
}

Обратите внимание, что метод CheckAccess () скрыт от Visual Studio 2015, поэтому просто напишите его, не ожидая, что Intellisense покажет его. Обратите внимание, что CheckAccess влияет на производительность (издержки за несколько наносекунд). Лучше всего, когда вы хотите сохранить ту микросекунду, которая требуется для выполнения «вызова» любой ценой. Кроме того, всегда есть возможность создать два метода (on с invoke, а другой без), когда вызывающий метод уверен, находится ли он в потоке пользовательского интерфейса или нет. Это самый редкий из редких случаев, когда вы должны смотреть на этот аспект диспетчера.

1 голос
/ 15 ноября 2018

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

текущая тема:

Dispatcher.CurrentDispatcher.Invoke(MethodName,
    new object[] { parameter1, parameter2 }); // if passing 2 parameters to method.

Основная тема интерфейса:

Application.Current.Dispatcher.BeginInvoke(
    DispatcherPriority.Background, new Action(() => MethodName(parameter)));
...