Как запустить что-то в потоке STA? - PullRequest
38 голосов
/ 04 марта 2010

В моем приложении WPF я выполняю асинхронную связь (с сервером). В функции обратного вызова я в итоге создаю объекты InkPresenter из результата с сервера. Для этого требуется, чтобы запущенный поток был STA, чего, по-видимому, в настоящее время нет. Поэтому я получаю следующее исключение:

Невозможно создать экземпляр InkPresenter, определенный в сборке [..] Вызывающим потоком должен быть 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
    [..]
}

Ответы [ 5 ]

58 голосов
/ 04 марта 2010

Вы можете запускать потоки 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, чтобы изменить представление чернил!

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;
}

Надеюсь, это поможет!

11 голосов
/ 04 марта 2010

Вы можете использовать класс Dipatcher для выполнения вызова метода в потоке пользовательского интерфейса. Dispatcher предоставляет статическое свойство CurrentDispatcher для получения диспетчера потока.

Если ваш объект класса, который создает InkPresenter, создается в UI-потоке, то метод CurrentDispatcher возвращает Dispatcher UI-Thread.

В Dispatcher вы можете вызвать метод BeginInvoke для асинхронного вызова указанного делегата в потоке.

3 голосов
/ 04 марта 2010

Это должно быть достаточно хорошо, чтобы вызвать его в потоке пользовательского интерфейса. Поэтому используйте BackgroundWorker, а на RunWorkerAsyncCompleted вы можете затем создать inkPresenter.

1 голос
/ 16 августа 2013

Это что-то вроде хака, но я бы использовал 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){
}
0 голосов
/ 18 июня 2019

Я только что использовал следующее, чтобы получить содержимое буфера обмена из потока STA. Думал бы опубликовать, возможно, чтобы помочь кому-то в будущем ...

string clipContent = null;
Thread t = new Thread(
    () =>
    {
        clipContent = Clipboard.GetText();
    });
t.SetApartmentState(ApartmentState.STA);
t.Start();
t.Join();

// do stuff with clipContent

t.Abort();
...