У меня есть сомнения относительно того, когда использовать Dispatcher.Invoke для обновления.
что-то на UI из другой темы.
Когда вы находитесь в другом потоке, вам всегда придется использовать диспетчер для обновления компонента пользовательского интерфейса, принадлежащего другому потоку.
Я запускаю новую задачу в другой теме, нужно ли мне использовать
Dispatcher.BeginInvoke для обновления пользовательского интерфейса.
Задачи позволяют выполнять несколько операций без блокировки потока, из которого они вызваны, но это не означает, что они находятся в другом потоке. Однако при обновлении пользовательского интерфейса из Задачи вам необходимо использовать диспетчер.
В данном случае это обновление пользовательского интерфейса, но я видел несколько сценариев, в которых
люди обновляют пользовательский интерфейс с помощью Dispatcher.Invoke или BeginInvoke из
другая тема.
Invoke заблокирует вызывающий поток, пока он выполняет действие, а BeginInvoke - нет. BeginInvoke немедленно вернет управление вызывающей стороне, Invoke может вызвать зависание вызывающего потока, если он выполняет тяжелую операцию.
Это из документации MSDN,
В WPF только поток, создавший DispatcherObject, может получить доступ
этот объект. Например, фоновый поток, который выделяется из
основной поток пользовательского интерфейса не может обновить содержимое кнопки, которая была
создан в потоке пользовательского интерфейса. Для доступа к фоновому потоку
свойство Content кнопки, фоновый поток должен
делегировать работу диспетчеру, связанному с потоком пользовательского интерфейса.
Это достигается с помощью Invoke или BeginInvoke. Вызвать
синхронный и BeginInvoke является асинхронным.
Редактировать: В ответ на ваш комментарий я провел несколько тестов.
При вызове Test () из задачи (без использования диспетчера) я получаю эту ошибку: «Вызывающий поток не может получить доступ к этому объекту, поскольку он принадлежит другому потоку».
Итак, я создал метод с именем PrintThreadID (). Я распечатал поток перед тем, как войти в задачу, а затем изнутри задачи, и она сообщает, что обе они работают с идентичным потоком .
Ошибка вводит в заблуждение, потому что говорит, что вызывающий поток отличается от того, которому принадлежит его, который, как показывает функция PrintThreadID (), не соответствует действительности, на самом деле он находится в одном потоке. Задачи, находящиеся в том же потоке, все еще не могут обновить компонент пользовательского интерфейса без использования Dispather.Invoke ().
Итак, вот рабочий пример, который обновит Grid из задачи.
public partial class MainWindow : Window
{
public List<string> myList { get; private set; }
public MainWindow()
{
InitializeComponent();
myList = new List<string>();
label1.Content = Thread.CurrentThread.ManagedThreadId.ToString();
Task.Factory.StartNew(PrintThreadID);
Task.Factory.StartNew(Test);
}
private void PrintThreadID()
{
label1.Dispatcher.Invoke(new Action(() =>
label1.Content += "..." + Thread.CurrentThread.ManagedThreadId.ToString()));
}
private void Test()
{
myList.Add("abc");
myList.Add("abc");
myList.Add("abc");
// if you do not use the dispatcher you will get the error "The calling thread cannot access this object because a different thread owns it."
dataGrid1.Dispatcher.Invoke(new Action(() =>
{
dataGrid1.ItemsSource = myList.Select(i => new { Item = i });
}));
}
}