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


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

Control.CheckForIllegalCrossThreadCalls = false;

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

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

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

2 62

2 ответа:

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

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

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

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

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