насущная проблема
Ваша непосредственная проблема в том, что SynchronizationContext.Current
не устанавливается автоматически для WPF. Чтобы установить его, вам нужно будет сделать что-то подобное в коде TheUISync при работе в WPF:
var context = new DispatcherSynchronizationContext(
Application.Current.Dispatcher);
SynchronizationContext.SetSynchronizationContext(context);
UISync = context;
Более глубокая проблема
SynchronizationContext
связан с поддержкой COM + и предназначен для пересечения потоков. В WPF у вас не может быть Dispatcher, который охватывает несколько потоков, поэтому один SynchronizationContext
действительно не может пересекать потоки. Существует ряд сценариев, в которых SynchronizationContext
может переключиться на новый поток - в частности, все, что вызывает ExecutionContext.Run()
. Поэтому, если вы используете SynchronizationContext
для предоставления событий как WinForms, так и клиентам WPF, вы должны знать, что некоторые сценарии будут нарушены, например, возникнет проблема с веб-запросом к веб-службе или сайту, размещенному в одном и том же процессе.
Как обойти, если нужен SynchronizationContext
В связи с этим я предлагаю использовать механизм Dispatcher
WPF исключительно для этой цели, даже с кодом WinForms. Вы создали одноэлементный класс "TheUISync", в котором хранится синхронизация, так что у вас есть некоторый способ подключиться к верхнему уровню приложения. Как бы вы это ни делали, вы можете добавить код, который создает, добавляет некоторое содержимое WPF в ваше приложение WinForms, чтобы Dispatcher
работал, а затем использовать новый механизм Dispatcher
, который я опишу ниже.
Использование Dispatcher вместо SynchronizationContext
Механизм
Dispatcher
в WPF фактически исключает необходимость в отдельном объекте SynchronizationContext
. Если у вас нет определенных сценариев взаимодействия, таких как совместное использование кода с объектами COM + или пользовательскими интерфейсами WinForms, лучшим решением будет использование Dispatcher
вместо SynchronizationContext
.
Это выглядит так:
public class Foo
{
public event EventHandler FooDoDoneEvent;
public void DoFoo()
{
//stuff
OnFooDoDone();
}
private void OnFooDoDone()
{
if(FooDoDoneEvent!=null)
Application.Current.Dispatcher.BeginInvoke(
DispatcherPriority.Normal, new Action(() =>
{
FooDoDoneEvent(this, new EventArgs());
}));
}
}
Обратите внимание, что вам больше не нужен объект TheUISync - WPF обрабатывает эту деталь для вас.
Если вам удобнее использовать старый синтаксис delegate
, вы можете сделать это следующим образом:
Application.Current.Dispatcher.BeginInvoke(
DispatcherPriority.Normal, new Action(delegate
{
FooDoDoneEvent(this, new EventArgs());
}));
Исправлена несвязанная ошибка
Также обратите внимание, что в вашем исходном коде есть ошибка, которая повторяется здесь. Проблема заключается в том, что FooDoneEvent может быть установлен равным нулю между временем вызова OnFooDoDone и временем, когда BeginInvoke
(или Post
в исходном коде) вызывает делегата. Исправление - это второй тест внутри делегата:
if(FooDoDoneEvent!=null)
Application.Current.Dispatcher.BeginInvoke(
DispatcherPriority.Normal, new Action(() =>
{
if(FooDoDoneEvent!=null)
FooDoDoneEvent(this, new EventArgs());
}));