IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Windows Presentation Foundation Discussion :

MVVM: Erreur thread appelant doit être en mode STA


Sujet :

Windows Presentation Foundation

  1. #1
    Membre actif
    Profil pro
    Inscrit en
    Juillet 2004
    Messages
    849
    Détails du profil
    Informations personnelles :
    Localisation : France, Paris (Île de France)

    Informations forums :
    Inscription : Juillet 2004
    Messages : 849
    Points : 295
    Points
    295
    Par défaut MVVM: Erreur thread appelant doit être en mode STA
    Salut!
    Je fais une application en WPF en MVVM.

    J'utilise le même systeme de naviguation que sur cet article:
    http://badger.developpez.com/tutorie...-le-meme-code/

    Code C# : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
     public class NavigationService : INavigationService
        {
            private readonly Frame _frame;
     
            public NavigationService(Frame frame)
            {
                if (frame == null) throw new ArgumentNullException("frame");
                _frame = frame;
            }
     
            public void Navigate(UIElement newPage)
            {
                _frame.Navigate(newPage);
            }
        }

    Pour naviguer:

    Code C# : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
     NavigationService navigationService = new NavigationService(NavigationFrame);
                ApplicationServiceProvider.ApplicationService.AddService(typeof(INavigationService), navigationService);
     
                //affichage de la page de login
                LoginViewModel loginViewModel = new LoginViewModel();
                LoginView loginView = new LoginView(loginViewModel);
     
                ApplicationServiceProvider.ApplicationService.NavigationService.Navigate(loginView);

    Je change les pages sans aucun problème. Sauf à un moment, où j'utilise un objet qui a un évenement et où je change de page de la même manière.

    Code C# : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
     
    void PeripheralService_OnButtonPressed(object sender, BuzzIO.BuzzOnButtonPressedEventArgs args)
            {
     PageViewModel viewModel = new PageViewModel();
                PageView view = new LoginView(viewModel);
     
                ApplicationServiceProvider.ApplicationService.NavigationService.Navigate(view);  
    }

    Or j'obiens cette erreure lors de l'appel au constructeur du PageViewModel
    Le thread appelant doit être en mode STA, comme l'exigent de nombreux composants de l'interface utilisateur.

    System.InvalidOperationException
    Avez-vous des pistes, s'il vous plait ?
    Merci.

  2. #2
    Rédacteur
    Avatar de The_badger_man
    Profil pro
    Développeur .NET
    Inscrit en
    Janvier 2005
    Messages
    2 745
    Détails du profil
    Informations personnelles :
    Âge : 41
    Localisation : France, Yvelines (Île de France)

    Informations professionnelles :
    Activité : Développeur .NET

    Informations forums :
    Inscription : Janvier 2005
    Messages : 2 745
    Points : 8 538
    Points
    8 538
    Par défaut
    D'où vient PeripheralService_OnButtonPressed ? Ne s'exécuterait-il pas sur un autre thread ?

  3. #3
    Rédacteur/Modérateur


    Homme Profil pro
    Développeur .NET
    Inscrit en
    Février 2004
    Messages
    19 875
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 43
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Développeur .NET
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Février 2004
    Messages : 19 875
    Points : 39 753
    Points
    39 753
    Par défaut
    Apparemment PeripheralService_OnButtonPressed s'exécute sur un autre thread. Comme il crée une vue, il faut que ce thread soit en STA. Mais de toutes façons, ce ne serait même pas suffisant, car la vue doit être créée sur le thread principal de l'application

    Tu peux faire quelque chose comme ça :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    void PeripheralService_OnButtonPressed(object sender, BuzzIO.BuzzOnButtonPressedEventArgs args)
    {
        if (Application.Current.Dispatcher.CheckAccess())
        {
            PageViewModel viewModel = new PageViewModel();
            PageView view;
            PageView view = new LoginView(viewModel);
            ApplicationServiceProvider.ApplicationService.NavigationService.Navigate(view);
        }
        else
        {
            Application.Current.Dispatcher.Invoke(new BuzzOnButtonPressedEventHandler(PeripheralService_OnButtonPressed), sender, args);
        }
    }

  4. #4
    Membre actif
    Profil pro
    Inscrit en
    Juillet 2004
    Messages
    849
    Détails du profil
    Informations personnelles :
    Localisation : France, Paris (Île de France)

    Informations forums :
    Inscription : Juillet 2004
    Messages : 849
    Points : 295
    Points
    295
    Par défaut
    Merci ca marche.

    Mais je ne comprends pas le code. Qu'est qu'application est qu'est ce qui se passe ?

  5. #5
    Rédacteur/Modérateur


    Homme Profil pro
    Développeur .NET
    Inscrit en
    Février 2004
    Messages
    19 875
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 43
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Développeur .NET
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Février 2004
    Messages : 19 875
    Points : 39 753
    Points
    39 753
    Par défaut
    Citation Envoyé par Oberown Voir le message
    Mais je ne comprends pas le code. Qu'est qu'application est qu'est ce qui se passe ?
    Application.Current renvoie un objet qui représente l'application en cours d'exécution. Le Dispatcher est un composant qui gère les tâches à exécuter sur un thread.

    En WPF (comme dans la plupart des technos d'interface graphique), seul le code qui s'exécute dans le thread de l'interface graphique (thread "principal") peut accéder aux composants graphiques, et ce thread doit être en mode STA (single thread appartment). Par défaut, les nouveaux threads créés sont en mode MTA (multiple thread appartment). La méthode Dispatcher.Invoke permet de faire exécuter du code sur le thread du dispatcher (i.e. le thread principal pour le dispatcher de l'application).

    Si je comprends bien, ton évènement OnButtonPressed vient d'un périphérique externe, et il est donc déclenché sur un autre thread que celui de l'interface. Donc 3 problèmes se posent :
    - ce thread ne peut pas créer de contrôles car il est en mode MTA
    - même s'il le pouvait, ces contrôles seraient créés sur un thread différent du thread principal, et ne pourraient donc pas être utilisés dans l'interface.
    - l'appel à Navigate fait appel à une méthode d'un contrôle, il doit donc aussi s'exécuter sur le thread principal

    L'appel à la méthode CheckAccess vérifie qu'on est bien sur le thread du dispatcher de l'application (thread principal). Si c'est le cas, on exécute le code qui crée la vue et on navigue vers cette vue. Sinon, on demande au dispatcher d'exécuter la même méthode sur son propre thread, à l'aide de la méthode Invoke

    Je sais pas si c'est plus clair, mais je sais pas trop comment l'expliquer autrement

  6. #6
    Rédacteur
    Avatar de Nathanael Marchand
    Homme Profil pro
    Expert .Net So@t
    Inscrit en
    Octobre 2008
    Messages
    3 615
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 38
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Expert .Net So@t
    Secteur : Conseil

    Informations forums :
    Inscription : Octobre 2008
    Messages : 3 615
    Points : 8 082
    Points
    8 082
    Par défaut
    J'ai pour habitude cette petite méthode d'extension :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    		public static void ExecOnUiThread(this Application app, Action action)
    		{
    			var dispatcher = app.Dispatcher;
    			if (dispatcher.CheckAccess())
    				action();
    			else
    				dispatcher.BeginInvoke(action);
    		}
    Qui s'utilise comme ceci:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    void PeripheralService_OnButtonPressed(object sender, BuzzIO.BuzzOnButtonPressedEventArgs args)
    {
     
        Application.Current.ExecOnUiThread(()=>
        {
            PageViewModel viewModel = new PageViewModel();
            PageView view = new LoginView(viewModel);
         ApplicationServiceProvider.ApplicationService.NavigationService.Navigate(view);
        });
    }

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. Erreur La Variable doit être déclarée
    Par tibofo dans le forum Développement
    Réponses: 1
    Dernier message: 29/12/2009, 17h25
  2. [WD10] Erreur :le code doit être recompilé
    Par lerorodu51 dans le forum WinDev
    Réponses: 8
    Dernier message: 10/03/2009, 11h03
  3. Réponses: 1
    Dernier message: 02/06/2008, 19h35
  4. Réponses: 1
    Dernier message: 23/05/2008, 12h45
  5. Réponses: 2
    Dernier message: 06/07/2007, 20h08

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo