Ответ 1
В вашем приложении есть основной поток пользовательского интерфейса (обычно 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, Диспетчеры и Invoke/BeginInvoke: Dispatcher.Invoke
заблокирует ваш поток до тех пор, пока MessageBox не будет уволен. Dispatcher.BeginInvoke
позволит вашему потоковому коду продолжать выполнение, пока поток пользовательского интерфейса блокирует вызов MessageBox до его отклонения.
CurrentDispatcher vs. Current.Dispatcher!
Будь товаром Dispatcher.CurrentDispatcher
, поскольку мое понимание этого - это то, что вернет Диспетчер для текущего потока, а не поток пользовательского интерфейса. Обычно вы интересуетесь диспетчером в потоке пользовательского интерфейса - 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));
});