Как запустить что-то в потоке STA?
в моем приложении WPF я делаю некоторую асинхронную связь (с сервером). В функции обратного вызова я в конечном итоге создаю объекты InkPresenter из результата с сервера. Это требует, чтобы запущенный поток был STA, которого, по-видимому, в настоящее время нет. Поэтому я получаю следующее исключение:
не удается создать экземпляр "InkPresenter", определенный в assembly [..] Вызывающий поток должен быть STA, поскольку этого требуют многие компоненты пользовательского интерфейса.
в настоящее время мой вызов асинхронной функции выглядит так:
public void SearchForFooAsync(string searchString)
{
var caller = new Func<string, Foo>(_patientProxy.SearchForFoo);
caller.BeginInvoke(searchString, new AsyncCallback(SearchForFooCallbackMethod), null);
}
Как я могу сделать обратный вызов-который будет делать создание InkPresenter-STA? Или вызовите синтаксический анализ XamlReader в новом потоке STA.
public void SearchForFooCallbackMethod(IAsyncResult ar)
{
var foo = GetFooFromAsyncResult(ar);
var inkPresenter = XamlReader.Parse(foo.Xaml) as InkPresenter; // <!-- Requires STA
[..]
}
4 ответов
Вы можете запустить потоки STA следующим образом:
Thread thread = new Thread(MethodWhichRequiresSTA);
thread.SetApartmentState(ApartmentState.STA); //Set the thread to STA
thread.Start();
thread.Join(); //Wait for the thread to end
единственная проблема заключается в том, что ваш объект результата должен быть передан каким-то образом.. Для этого вы можете использовать частное поле или погрузиться в передачу параметров в потоки. Здесь я устанавливаю данные foo в частном поле и запускаю поток STA, чтобы мутировать inkpresenter!
private var foo;
public void SearchForFooCallbackMethod(IAsyncResult ar)
{
foo = GetFooFromAsyncResult(ar);
Thread thread = new Thread(ProcessInkPresenter);
thread.SetApartmentState(ApartmentState.STA);
thread.Start();
thread.Join();
}
private void ProcessInkPresenter()
{
var inkPresenter = XamlReader.Parse(foo.Xaml) as InkPresenter;
}
надеюсь, что это помогает!
можно использовать Dipatcher класс для выполнения вызова метода в UI-потоке. Диспетчер предоставляет статическое свойство CurrentDispatcher для получения диспетчера потока.
Если ваш объект класса, который создает InkPresenter, создается в UI-Thread, то метод CurrentDispatcher возвращает диспетчер UI-Thread.
на Диспетчере можно вызвать метод BeginInvoke - для асинхронного вызова указанного делегата на резьбу.
это должно быть достаточно хорошо, чтобы вызвать его в потоке пользовательского интерфейса. Поэтому используйте BackgroundWorker
и RunWorkerAsyncCompleted
, затем вы можете создать inkPresenter.
это немного взломать, но я бы использовал XTATestRunner так что ваш код будет выглядеть так:
public void SearchForFooAsync(string searchString)
{
var caller = new Func<string, Foo>(_patientProxy.SearchForFoo);
caller.BeginInvoke(searchString, new AsyncCallback(SearchForFooCallbackMethod), null);
}
public void SearchForFooCallbackMethod(IAsyncResult ar)
{
var foo = GetFooFromAsyncResult(ar);
InkPresenter inkPresenter;
new XTATestRunner().RunSTA(() => {
inkPresenter = XamlReader.Parse(foo.Xaml) as InkPresenter;
});
}
в качестве бонуса можно поймать исключения, брошенные в поток STA (или MTA), как это:
try{
new XTATestRunner().RunSTA(() => {
throw new InvalidOperationException();
});
}
catch(InvalidOperationException ex){
}