43 votes

Comment exécuter quelque chose dans le fil STA ?

Dans mon application WPF, je fais de la 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 soit STA, ce qui apparemment n'est pas le cas actuellement. J'obtiens donc l'exception suivante :

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

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 en sorte que le callback - qui fera la création de InkPresenter - soit STA ? Ou invoquer l'analyse du 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
    [..]
}

0 votes

Qu'est-il arrivé à [STAThread] avant une méthode ? Pas toujours appropriée mais très facile. Peut-être n'est-il pas sorti avant 2011 ? Je ne l'ai pas utilisé depuis 2011 que je me souvienne....

0 votes

L'ajout de [STAThread] avant la méthode a fonctionné pour moi. Merci, ebyrob ! :-)

59voto

Arcturus Points 14366

Vous pouvez commencer les fils de STA comme ceci :

    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 vous plonger dans la transmission de paramètres dans les threads. Ici, j'ai placé les données de foo dans un champ privé et j'ai démarré le STA Thread pour muter l'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;
}

J'espère que cela vous aidera !

0 votes

Merci :) Faites-moi savoir si cela va résoudre le problème. Nous utilisons cette technique pour générer des images PNG de nos contrôles Xaml sur le serveur !

0 votes

Ça semble régler le problème, mais je viens de rencontrer un autre problème. Sigh .. Je le marquerai comme accepté dès que j'aurai réussi à tout faire fonctionner ici Merci !

0 votes

@Arcturus : ce qui est MethodWhichRequiresSTA juste une méthode ? mon code ne compilera pas si je mets une méthode à cet endroit car elle a besoin de ses arguments

13voto

Jehof Points 14720

Vous pouvez utiliser le Dispatcher pour exécuter l'appel de la méthode sur le fil d'exécution de l'interface utilisateur. Le Dispatcher fournit la propriété statique CurrentDispatcher pour obtenir le dispatcher d'un thread.

Si votre objet de la classe, qui crée l'InkPresenter, est créé sur le UI-Thread, alors la méthode CurrentDispatcher renvoie le Dispatcher du UI-Thread.

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 fil d'exécution.

1 votes

Dispatcher.Invoke ou BeginInvoke est la solution à adopter. Beaucoup plus simple que la solution acceptée

0 votes

Il faut se méfier de l'utilisation de Dispatcher.CurrentDispatcher à moins que vous ne soyez certain de fonctionner sur un thread d'interface utilisateur qui traitera DispatcherOperation s. CurrentDispatcher créera un répartiteur pour le thread actuel s'il n'en a pas déjà un, même si le thread actuel est MTA. Je trouve généralement qu'il est préférable d'utiliser la méthode Dispatcher sur le contrôle que je veux mettre à jour.

3voto

Kyle Rozendo Points 15606

Cela devrait être suffisant pour l'appeler sur le thread de l'interface utilisateur. Par conséquent, utilisez un BackgroundWorker et sur le RunWorkerAsyncCompleted vous pouvez ensuite créer le présentateur d'encre.

0 votes

Vous avez raison. Le problème est que le callback n'est pas exécuté sur le thread UI. Le thread UI est exécuté avec STA, donc l'exécuter sur le thread UI devrait résoudre le problème pour moi.

1voto

mouldycurryness Points 62

Je viens d'utiliser ce qui suit pour obtenir le contenu du presse-papiers à partir du fil de discussion STA. J'ai pensé que je posterais pour peut-être 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();

1voto

alex Points 1

C'est un peu une pirouette, mais je voudrais utiliser XTATestRunner Ainsi 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 prime, il est possible d'attraper les exceptions lancées dans le fil de STA (ou MTA) comme ceci :

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

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X