web-dev-qa-db-fra.com

Comment exécuter quelque chose dans le thread STA?

Dans mon application WPF, je fais une communication asynchrone (avec le serveur). Dans la fonction de rappel, je finis par créer des objets InkPresenter à partir du résultat du serveur. Cela nécessite que le thread en cours d'exécution soit STA, ce qui n'est apparemment pas le cas actuellement. Par conséquent, j'obtiens l'exception suivante:

Impossible de créer une instance de 'InkPresenter' définie dans l'assembly [..] Le thread appelant doit être STA, car de nombreux composants d'interface utilisateur l'exigent.

Actuellement, mon appel de fonction asynchrone est comme ceci:

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

Comment puis-je faire le rappel - qui fera la création d'InkPresenter - être STA? Ou appelez l'analyse XamlReader dans un nouveau thread STA.

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

Vous pouvez démarrer les threads STA comme suit:

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

Le seul problème est que votre objet de résultat doit être transmis d'une manière ou d'une autre. Vous pouvez utiliser un champ privé pour cela, ou plonger pour transmettre des paramètres dans des threads. Ici, je définis les données foo dans un champ privé et démarre le thread STA pour muter le présentateur d'encre!

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

J'espère que cela t'aides!

58
Arcturus

Vous pouvez utiliser la classe Dipatcher pour exécuter l'appel de méthode sur le thread d'interface utilisateur. Le répartiteur fournit la propriété statique CurrentDispatcher pour obtenir le répartiteur d'un thread.

Si votre objet de la classe, qui crée InkPresenter, est créé sur le thread d'interface utilisateur, la méthode CurrentDispatcher renvoie le répartiteur du thread d'interface utilisateur.

Sur le répartiteur, vous pouvez appeler la méthode BeginInvoke pour appeler le délégué spécifié de manière asynchrone sur le thread.

11
Jehof

Il devrait être suffisant de l'appeler sur le thread d'interface utilisateur. Par conséquent, utilisez un BackgroundWorker et sur le RunWorkerAsyncCompleted, vous pouvez ensuite faire la création de inkPresenter.

3
Kyle Rozendo

C'est un peu un hack, mais j'utiliserais XTATestRunner Donc votre code ressemblera à:

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

en bonus, il est possible d'attraper des exceptions levées dans le fil STA (ou MTA) comme ceci:

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

Je viens d'utiliser les éléments suivants pour obtenir le contenu du presse-papiers à partir du thread STA. Je pensais que je posterais peut-être pour aider quelqu'un à l'avenir ...

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

// do stuff with clipContent

t.Abort();
1
mouldycurryness