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 :

Problème de STAThread en WPF


Sujet :

Windows Presentation Foundation

  1. #1
    Membre éclairé Avatar de Nixar
    Profil pro
    Inscrit en
    Mars 2006
    Messages
    302
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mars 2006
    Messages : 302
    Par défaut Problème de STAThread en WPF
    Bonjour,
    Je réalise une première application WPF, notamment pour me familiariser à la "bête". C'est une sorte de Flickr Uploader (pour ceux qui connaissent) c'est-à-dire une application dans laquelle on charge des photos et on édite des tags exif avant de publier le tout sur le site cible.

    Pour afficher les miniatures des images à publier, j'ai réalisé un UserControl qui affiche une miniature de la photo ainsi que le nom du fichier.

    Maintenant je souhaite rendre l'application plus "user-friendly" en faisant en sorte d'informer l'utilisateur de l'état de chargement des photos dans l'interface. Dans d'autres applications Winforms, j'avais utilisé le BackgroundWorker qui fonctionne parfaitement, pour peu que le thread principal ait l'attribut STAThread.

    J'ai implémenté mon BackgroundWorker de la manière suivante :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
     
    // La classe de la fenêtre principale
    public partial class FenetrePrincipale : Window
        {
            private BackgroundWorker _ChargementPhotosDansInterface = new BackgroundWorker();
        }
            public FenetrePrincipale()
            {
                InitializeComponent();                        
                InitialisationBackgroundWorker();
            }
            // Abonnement du BGW aux délégués pour le traitement de l'avancement
            private void InitialisationBackgroundWorker()
            {
                _ChargementPhotosDansInterface.DoWork += new DoWorkEventHandler(_ChargementPhotosDansInterface_DoWork);
                _ChargementPhotosDansInterface.ProgressChanged += new ProgressChangedEventHandler(_ChargementPhotosDansInterface_ProgressChanged);
                _ChargementPhotosDansInterface.RunWorkerCompleted += new RunWorkerCompletedEventHandler(_ChargementPhotosDansInterface_RunWorkerCompleted);
            }        
     
            void _ChargementPhotosDansInterface_DoWork(object sender, DoWorkEventArgs e)
            {
                // Appel de la méthode coûteuse en temps et ressources
                PopulationImages((string[])e.Argument);
            }
     
            void _ChargementPhotosDansInterface_ProgressChanged(object sender, ProgressChangedEventArgs e)
            {
                // Mise à jour de l'avancement pour l'utilisateur
            }
     
            void _ChargementPhotosDansInterface_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
            {
                // Code à exécuter lorsque c'est terminé
            }
     
             // Itère sur les noms de fichiers choisis pour générer le usercontrol correspondant et l'ajouter au WrapPanel qui les affiche.
             private void PopulationImages(string[] NomsFichiers)
            {           
     	        foreach (string Fichier in NomsFichiers)
                {               
                    PhotoAPublier Photo = new PhotoAPublier(Fichier, this);                               
                    Wrp_Photos.Children.Add(Photo);
                }
            }
    J'ai bien vérifié par ailleurs que le thread principal était en mode STA :

    Fichier app.g.cs :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
     
        /// <summary>
        /// App
        /// </summary>
        public partial class App : System.Windows.Application {
     
            /// <summary>
            /// InitializeComponent
            /// </summary>
            [System.Diagnostics.DebuggerNonUserCodeAttribute()]
            public void InitializeComponent() {
     
                #line 4 "..\..\App.xaml"
                this.StartupUri = new System.Uri("FenetrePrincipale.xaml", System.UriKind.Relative);
     
                #line default
                #line hidden
            }
     
            /// <summary>
            /// Application Entry Point.
            /// </summary>
            [System.STAThreadAttribute()]
            [System.Diagnostics.DebuggerNonUserCodeAttribute()]
            public static void Main() {
                Publication_photos.App app = new Publication_photos.App();
                app.InitializeComponent();
                app.Run();
            }
        }
    }
    Tout cela compile correctement. Cependant, à l'exécution, j'obtiens l'erreur suivante lors de la tentative d'instanciation du premier usercontrol :

    L'exception System.InvalidOperationException n'a pas été gérée par le code utilisateur
    Message="Le thread appelant doit être en mode STA, comme l'exigent de nombreux composants de l'interface utilisateur."
    L'état de la pile si ça peut servir :

    Source="PresentationCore"
    StackTrace:
    à System.Windows.Input.InputManager..ctor()
    à System.Windows.Input.InputManager.GetCurrentInputManagerImpl()
    à System.Windows.Input.InputManager.get_Current()
    à System.Windows.Input.KeyboardNavigation..ctor()
    à System.Windows.FrameworkElement.FrameworkServices..ctor()
    à System.Windows.FrameworkElement.EnsureFrameworkServices()
    à System.Windows.FrameworkElement..ctor()
    à System.Windows.Controls.Control..ctor()
    à System.Windows.Controls.UserControl..ctor()
    à Publication_photos.PhotoAPublier..ctor(String CheminFichier, FenetrePrincipale FenetreEnCours) dans I:\Programmation\DotNet\Sources\Client lourd\PublicationPhotos\Publication photos\Publication photos\PhotoAPublier.xaml.cs:ligne 34
    à Publication_photos.FenetrePrincipale.PopulationImages(String[] NomsFichiers) dans I:\Programmation\DotNet\Sources\Client lourd\PublicationPhotos\Publication photos\Publication photos\FenetrePrincipale.xaml.cs:ligne 92
    à Publication_photos.FenetrePrincipale._ChargementPhotosDansInterface_DoWork(Object sender, DoWorkEventArgs e) dans I:\Programmation\DotNet\Sources\Client lourd\PublicationPhotos\Publication photos\Publication photos\FenetrePrincipale.xaml.cs:ligne 46
    à System.ComponentModel.BackgroundWorker.OnDoWork(DoWorkEventArgs e)
    à System.ComponentModel.BackgroundWorker.WorkerThreadStart(Object argument)
    InnerException:
    Pourquoi cette exception? Mon thread principal est pourtant en mode STA... Que dois-je faire pour que cela fonctionne? J'ai commencé à regarder ce qui semble être une solution avec le Dispatcher, mais si tel est le cas je n'ai pas réussi à comprendre comment l'invoquer correctement pour lancer mon thread de travail de façon correcte.

    Toutes vos lumières sont les bienvenues !

    Cordialement,

    Nixar

  2. #2
    Membre Expert
    Profil pro
    Inscrit en
    Décembre 2004
    Messages
    2 210
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Décembre 2004
    Messages : 2 210
    Par défaut
    Salut,

    ça ne va peut-être pas changé grand chose, mais à quel moment tu lances ton backgroundWorker ?
    D'habitude, on utilise la méthode :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    ChargementPhotosDansInterface.RunWorkerAsync();
    Il me semble qu'elle n'est pas appelée.

    Sinon tu peux voir ce lien pour comprendre les bonnes pratiques d'utilisation des threads, dispatcher, backgroundWorker, ... dans WPF :
    http://msdn.microsoft.com/fr-fr/magazine/cc163328.aspx

  3. #3
    Membre éclairé Avatar de Nixar
    Profil pro
    Inscrit en
    Mars 2006
    Messages
    302
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mars 2006
    Messages : 302
    Par défaut
    Salut et merci de ta réponse,
    Oui tu as raison, j'ai omis cette méthode-là . Mais en effet ça ne change pas grand chose, parcequ'en debug tout se lance correctement, donc pas de souci de se côté-là, je pense que mon BGW est bien "câblé".

    Merci pour le lien, je l'avais déjà parcouru en diagonale et cet article mérite d'être lu plus en détail. Ceci dit, je souhaite utiliser les choses au plus simple et le BackgroundWorker devrait se suffire à lui-même d'autant plus qu'ils précise à la section BackgroundWorker :

    Dans WPF, ce modèle est étendu par une classe DispatcherSynchronizationContext. En utilisant BackgroundWorker, le répartiteur est automatiquement employé pour invoquer les appels de méthodes entre threads.
    Si le Dispatcher est automatiquement employé, alors pourquoi ne positionne-t-il pas mon thread en mode STA alors que je le lui demande instamment à l'init de l'application? Comment faire alors pour que mon BackgroundWorker instancie correctement mes userscontrol?

    Bref, j'ai loupé quelque chose... mais quoi?

    Merci d'avance pour vos réponses.

    Nixar

  4. #4
    Membre émérite Avatar de obito
    Homme Profil pro
    Développeur .NET
    Inscrit en
    Janvier 2008
    Messages
    773
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 36
    Localisation : France, Gironde (Aquitaine)

    Informations professionnelles :
    Activité : Développeur .NET

    Informations forums :
    Inscription : Janvier 2008
    Messages : 773
    Par défaut
    Salut,

    J'ai l'impression que c'est un problème récurent moi aussi j'ai eu le même et j'ai du faire ceci pour le résoudre :

    Code C# : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
     
    // Itère sur les noms de fichiers choisis pour générer le usercontrol correspondant et l'ajouter au WrapPanel qui les affiche.
    private void PopulationImages(string[] NomsFichiers)
    {           
        foreach (string Fichier in NomsFichiers)
        {               
            PhotoAPublier Photo = new PhotoAPublier(Fichier, this);
            this.Dispatcher.Invoke(DispatcherPriority.Background, (ThreadStart)delegate
            {                               
                Wrp_Photos.Children.Add(Photo);
            });
         }
    }

    J'ai l'impression que chaque contrôle est dépendant du thread principal et bien sur le backgroundworker crée à un fil à part. Du coup il y a conflit :s

  5. #5
    Membre éclairé Avatar de Nixar
    Profil pro
    Inscrit en
    Mars 2006
    Messages
    302
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mars 2006
    Messages : 302
    Par défaut
    Bonjour Obito,
    MERCI beaucoup pour cette réponse qui m'ôte une sacrée épine du pied... Ca fonctionne parfaitement. J'espère que cela pourra servir à d'autres !

    Taggué résolu !

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

Discussions similaires

  1. Problèmes avec les styles wpf c#
    Par cocobelle89 dans le forum C#
    Réponses: 8
    Dernier message: 30/04/2013, 16h23
  2. Problème avec binding image wpf c#
    Par cocobelle89 dans le forum C#
    Réponses: 2
    Dernier message: 23/04/2013, 11h44
  3. Problème avec les UserControl WPF
    Par helper_moi dans le forum Windows Presentation Foundation
    Réponses: 3
    Dernier message: 16/06/2010, 18h39
  4. Problème Référence System.Web WPF
    Par mathrb dans le forum Général Dotnet
    Réponses: 5
    Dernier message: 06/02/2010, 19h15
  5. [WPF]Problème d'update de binding
    Par guitoux1 dans le forum Windows Presentation Foundation
    Réponses: 11
    Dernier message: 12/06/2007, 17h42

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