43 Stimmen

Wie kann man etwas im STA-Thread ausführen?

In meiner WPF-Anwendung mache ich einige asynchrone Kommunikation (mit Server). In der Callback-Funktion erzeuge ich am Ende InkPresenter-Objekte aus dem Ergebnis vom Server. Dies erfordert, dass der laufende Thread STA sein, die anscheinend ist es derzeit nicht. Daher erhalte ich die folgende Ausnahme:

Cannot create instance of 'InkPresenter' defined in assembly [ ] Der aufrufende Thread muss STA sein, da viele UI-Komponenten dies erfordern.

Derzeit ist mein asynchroner Funktionsaufruf wie folgt:

public void SearchForFooAsync(string searchString)
{
    var caller = new Func<string, Foo>(_patientProxy.SearchForFoo);
    caller.BeginInvoke(searchString, new AsyncCallback(SearchForFooCallbackMethod), null);
}

Wie kann ich den Callback - der die InkPresenter-Erstellung vornimmt - zu STA machen? Oder das XamlReader-Parsing in einem neuen STA-Thread aufrufen.

public void SearchForFooCallbackMethod(IAsyncResult ar)
{
    var foo = GetFooFromAsyncResult(ar); 
    var inkPresenter = XamlReader.Parse(foo.Xaml) as InkPresenter; // <!-- Requires STA
    [..]
}

59voto

Arcturus Punkte 26057

Sie können STA Threads wie folgt beginnen:

    Thread thread = new Thread(MethodWhichRequiresSTA);
    thread.SetApartmentState(ApartmentState.STA); //Set the thread to STA
    thread.Start(); 
    thread.Join(); //Wait for the thread to end

Das einzige Problem ist, dass Ihr Ergebnisobjekt irgendwie weitergegeben werden muss. Sie können dafür ein privates Feld verwenden oder sich mit der Weitergabe von Parametern in Threads beschäftigen. Hier setze ich die foo-Daten in ein privates Feld und starte den STA-Thread, um den Inkpresenter zu verändern!

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

Ich hoffe, das hilft!

13voto

Jehof Punkte 33506

Sie können die Fahrdienstleiter Klasse, um den Methodenaufruf auf dem UI-Thread auszuführen. Der Dispatcher bietet die statische Eigenschaft CurrentDispatcher, um den Dispatcher eines Threads zu erhalten.

Wenn Ihr Objekt der Klasse, die den InkPresenter erstellt, auf dem UI-Thread erstellt wird, dann gibt die CurrentDispatcher-Methode den Dispatcher des UI-Threads zurück.

Auf dem Dispatcher können Sie die BeginInvoke-Methode aufrufen, um den angegebenen Delegaten asynchron auf dem Thread aufzurufen.

3voto

Kyle Rosendo Punkte 24351

Es sollte ausreichen, ihn im UI-Thread aufzurufen. Verwenden Sie daher eine BackgroundWorker und am RunWorkerAsyncCompleted können Sie dann die Erstellung des inkPresenters vornehmen.

1voto

alex Punkte 11

Es ist zwar ein wenig umständlich, aber ich würde die XTATestRunner Ihr Code wird also wie folgt aussehen:

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

Als Bonus ist es möglich, Ausnahmen abzufangen, die in einem STA- (oder MTA-) Thread wie diesem geworfen werden:

try
{
    new XTATestRunner().RunSTA(() => {
        throw new InvalidOperationException();
    });
}
catch (InvalidOperationException ex)
{
}

1voto

mouldycurryness Punkte 85

Ich habe soeben das Folgende verwendet, um den Inhalt der Zwischenablage aus dem STA-Thread abzurufen. Ich dachte, ich würde posten, um vielleicht jemandem in der Zukunft zu helfen...

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

// do stuff with clipContent

t.Abort();

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X