Ваше приложение имеет основной поток пользовательского интерфейса (обычно ManagedThreadId==1
).Как правило, в приложении чата ваши события поступают в другие потоки (либо выделенные потоки прослушивания сокетов, либо потоки пула потоков из кода прослушивания).Если вы хотите обновить пользовательский интерфейс из события, которое получает другой поток, вы должны использовать диспетчер.Полезным тестом здесь является метод Dispatcher.CheckAccess()
, который возвращает true, если код находится в потоке пользовательского интерфейса, и false, если в каком-то другом потоке.Типичный вызов выглядит примерно так:
using System.Windows.Threading; // For Dispatcher.
if (Application.Current.Dispatcher.CheckAccess()) {
network_links.Add(new NetworkLinkVM(link, start_node, end_node));
}
else {
Application.Current.Dispatcher.BeginInvoke(DispatcherPriority.Normal, new Action(()=>{
network_links.Add(new NetworkLinkVM(link, start_node, end_node));
}));
}
Если вы находитесь в главном окне, вы можете использовать:
Dispatcher.BeginInvoke(...
Если вы находитесь в каком-либо ином контексте, например модели представления, используйте:
Application.Current.Dispatcher.BeginInvoke(
Invoke vs BeginInvoke
Используйте Invoke
, если хотите, чтобы текущий поток ожидал, пока поток пользовательского интерфейса не обработал код отправки, или BeginInvoke
, если хотитетекущий поток продолжается без ожидания завершения операции в потоке пользовательского интерфейса.
MessageBox, Dispatchers и Invoke / BeginInvoke:
Dispatcher.Invoke
будет блокировать ваш поток до тех пор, пока MessageBox не будет удален.
Dispatcher.BeginInvoke
позволит продолжить выполнение кода вашего потока, в то время как поток пользовательского интерфейса будет блокировать вызов MessageBox до тех пор, пока он не будет отменен.
CurrentDispatcher против Current.Dispatcher!
Остерегайтесь Dispatcher.CurrentDispatcher
, так как я понимаю, что это вернет Dispatcher для текущего потока, а не потока пользовательского интерфейса.Обычно вас интересует диспетчер в потоке пользовательского интерфейса - Application.Current.Dispatcher
всегда возвращает это.
Дополнительные примечания:
Если вы обнаружите, что вам приходится часто проверять диспетчер CheckAccess, тополезный вспомогательный метод:
public void DispatchIfNecessary(Action action) {
if (!Dispatcher.CheckAccess())
Dispatcher.Invoke(action);
else
action.Invoke();
}
, который можно назвать:
DispatchIfNecessary(() => {
network_links.Add(new NetworkLinkVM(link, start_node, end_node));
});