Измените элементы управления WPF из не основного потока с помощью Dispatcher.Invoke

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

Control.CheckForIllegalCrossThreadCalls = false;

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

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

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

Ответ 1

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

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

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

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

Ответ 2

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 имеет накладные расходы на производительность (накладные расходы за несколько наносекунд). Это только лучше, если вы хотите сохранить эту микросекунду, необходимую для выполнения "вызова" любой ценой. Кроме того, всегда существует возможность создать два метода (с вызовом и другими без) при вызове метода, если он в потоке пользовательского интерфейса или нет. Это только редкий случай, когда вы должны смотреть на этот аспект диспетчера.