Ответ 1
В зависимости от ситуации существуют различные варианты:
Доступ к элементу управления из другого потока
например. обновление TextBlock с информацией о ходе работы.
-
В этом случае самая простая вещь, которую вы можете сделать, - это избегать прямого взаимодействия с элементом управления. Вы можете просто привязать свойство, к которому хотите получить доступ или изменить объект, класс реализует
INotifyPropertyChanged
, а затем вместо этого установите свойство на этом объекте. Структура будет обрабатывать остальное для вас. (В общем, вам редко приходится взаимодействовать с элементами UI напрямую, вы можете почти всегда связывать соответствующие свойства и работать с источником привязки, а один случай, когда прямой доступ к управлению может быть необходим, - это авторинг управления.)В некоторых случаях, когда привязки данных недостаточно, например, при попытке изменить привязанную
ObservableCollection<T>
, для этого вам нужно... -
Вы можете отправить свой код доступа в поток, владеющий объектом, это можно сделать, вызвав
Invoke
илиBeginInvoke
наDispatcher
, которому принадлежит доступ к объекту (получение этогоDispatcher
возможно в другом потоке).например.
new Thread(ThisThreadStart).Start();
void ThisThreadStart() { textBlock.Dispatcher.Invoke(new Action(() => textBlock.Text = "Test")); }
Если не понятно, в каком потоке выполняется метод, вы можете использовать
Dispatcher.CheckAccess
для отправки или выполнения действия напрямую.например.
void Update() { Action action = () => myTextBlock.Text = "Test"; var dispatcher = myTextBlock.Dispatcher; if (dispatcher.CheckAccess()) action(); else dispatcher.Invoke(action); }
Если объект не является
DispatcherObject
, и вам все еще нужен связанныйDispatcher
, вы можете использоватьDispatcher.CurrentDispatcher
в потоке, создающем объект (так что это в методе, выполняемом потоком, не принесет вам пользы). Для удобства, поскольку вы обычно создаете объекты в главном потоке пользовательского интерфейса приложения; вы можете получить этот потокDispatcher
из любого места, используяApplication.Current.Dispatcher
.
Специальные случаи:
-
Переместите любой доступ к управлению
ProgressChanged
, как это происходит в потоке, который создал экземпляр (который, конечно же, должен быть UI-потоком) -
Таймеры
В WPF вы можете использовать
DispatcherTimer
для удобства, он отправляет вам, поэтому любой код вTick
вызывается в соответствующем диспетчере. Если вы можете делегировать отправку в систему привязки данных, вы, конечно же, можете использовать обычный таймер.
Вы можете узнать больше о том, как работает очередь Dispatcher
и потоки WPF вообще в MSDN.
Доступ к объекту, созданному в другом потоке
например. Загрузка изображения в фоновом режиме.
Если объект, о котором идет речь, не Freezable
, вы должны, как правило, просто не создавать его в другом потоке или ограничивать доступ к создающему потоку. Если это Freezable
, вам просто нужно вызвать Freeze
, чтобы сделать его доступным для других потоков.
Доступ к объекту данных из другого потока
То есть тип, экземпляр которого обновляется, - это пользовательский код. Если возникает исключение, эта ситуация, вероятно, возникла у кого-то, использующего DependencyObject
в качестве базового типа для класса данных.
Эта ситуация такая же, как доступ к элементу управления, и могут применяться одни и те же подходы, но обычно их следует избегать в первую очередь. Разумеется, это позволяет получать уведомления об изменении свойств с помощью свойств свойств зависимостей, и эти свойства также могут быть связаны, но достаточно часто это просто не стоит отказываться от ните-независимости. Вы можете получать уведомления об изменениях из INotifyPropertyChanged
, а система привязки в WPF по своей природе асимметрична, всегда есть свойство, которое связано (цель) и что-то, что является источником для этой привязки. Обычно пользовательский интерфейс является целью, а данные являются источником, а это означает, что только компоненты пользовательского интерфейса должны нуждаться в свойствах зависимостей.