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

C# Discussion :

Treeview et Thread


Sujet :

C#

Vue hybride

Message précédent Message précédent   Message suivant Message suivant
  1. #1
    Membre averti
    Profil pro
    Inscrit en
    Novembre 2012
    Messages
    12
    Détails du profil
    Informations personnelles :
    Localisation : Suisse

    Informations forums :
    Inscription : Novembre 2012
    Messages : 12
    Par défaut Treeview et Thread
    Bonjour/soir,

    J'essaie dernièrement d’utiliser l’élément treeview de WPF et je rencontre un problème avec son utilisation.

    Je m'entraîne en récupérant l'arborescence de mon ordinateur depuis un dossier, et avec de la récursivité je récupère les sous-dossiers et les fichiers présents.
    Ça sa marche très bien, l'inconvénient est que ça prend quelques secondes à tout charger et donc j'ai mis mon code dans un thread.

    Le problème survient quand je veux repasser toute l'arborescence créée avec des TreeViewItem à mon treeView. A ce moment là j'obtient un InvalidOperationException "Le thread appelant ne peut pas accéder à cet objet parce qu'un autre thread en est propriétaire."

    Ce problème je l'ai déjà eu dans d'autres programmes et je l'ai résolu en utilisant un Dispatcher.BeginInvoke

    Mon code appelé depuis le thread pour mettre à jour le treeview se présente comme suit.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    public void MiseAJourNode(TreeViewItem node)
            {
                treeViewMain.Dispatcher.BeginInvoke(System.Windows.Threading.DispatcherPriority.Normal, new Action(
                    delegate()
                    {
                        treeViewMain.Items.Add(node);
                    }));
            }
    Le node reçu comporte tous les nodes et sous nodes de mon arborescence et j'essaie ensuite simplement de l'ajouter à mon treeview. Habituellement le bout de code au-dessus me permet de régler les problèmes de communications d'un thread à un autre. Mais là même avec, je n'arrive pas à éviter une exception.

    Une expérience à partager à propos des treeview et des threads?

    Je vous remercie d'avance et vous souhaite un bon week-end.

  2. #2
    Membre Expert


    Homme Profil pro
    Développeur informatique
    Inscrit en
    Avril 2006
    Messages
    970
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 48
    Localisation : Belgique

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

    Informations forums :
    Inscription : Avril 2006
    Messages : 970
    Par défaut
    Bonjour,

    Comme tu l'as surement déjà compris au vu du message d'erreur, ton thread ne peut pas accéder à un objet de ton interface vu que celle-ci fait partie de ton programme et donc d'un autre thread.

    Je te propose donc l'utilisation du BackgroundWorker qui rend plus simple la création et l'utilisation des threads. Voici ci dessous un exemple de code d'une application avec une progressbar qui évolue grâce à celui-ci

    Code Xaml :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
     
    <Window x:Class="TestBackgroundWorker.MainWindow"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            Title="Test BackgroundWorker" Height="146" Width="525">
        <Grid>
            <ProgressBar Height="28" HorizontalAlignment="Left" Margin="12,12,0,0" Name="Progress" VerticalAlignment="Top" Width="480" />
            <Button Content="Début" Height="23" HorizontalAlignment="Left" Margin="12,46,0,0" Name="btnStart" VerticalAlignment="Top" Width="75" Click="btnStart_Click" />
            <Button Content="Fin" Height="23" HorizontalAlignment="Left" Margin="93,46,0,0" Name="btnEnd" VerticalAlignment="Top" Width="75" Click="btnEnd_Click" />
            <Label Content="0/0" Height="32" HorizontalAlignment="Right" Margin="0,46,11,0" Name="lblAvancement" VerticalAlignment="Top" Width="133" HorizontalContentAlignment="Right" VerticalContentAlignment="Center" />
            <Label Content="Action en cours : " Height="28" HorizontalAlignment="Left" Margin="12,75,0,0" Name="label1" VerticalAlignment="Top" Width="101" />
            <Label Height="28" HorizontalAlignment="Left" Margin="119,75,0,0" Name="lblAction" VerticalAlignment="Top" Width="372" Content="En attente" />
        </Grid>
    </Window>
    Le code behind :

    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
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
     
    public partial class MainWindow : Window
        {
            private BackgroundWorker work;
            int nMaxValue = 500;
     
            /* MainWindow
             * 
             * Initialisation de la fenêtre
             */
            public MainWindow()
            {
                InitializeComponent();
     
                // Création du BackgroundWorker
                work = new BackgroundWorker();
     
                // Indique la fonction de traitement lors du déclenchement de l'événement DoWork
                work.DoWork += new DoWorkEventHandler(Traitement);
                // Indique la fonction de traitement lors du déclenchement de l'événement indiquant un changement de progression
                work.ProgressChanged += new ProgressChangedEventHandler(work_ProgressChanged);
                // Indique au BackgroundWorker qu'il peut etre interrompu 
                work.WorkerSupportsCancellation = true;
                // Indique au BackgroundWorker qu'il doit traiter l'événement de progression
                work.WorkerReportsProgress = true;
                // Indique la fonction exécutée à la fin du traitement 
                work.RunWorkerCompleted += new RunWorkerCompletedEventHandler(work_RunWorkerCompleted);
     
                // Initialisation de la progressbar
                Progress.Minimum = 0;
                Progress.Maximum = nMaxValue;
                Progress.Value = 0;
     
                btnEnd.IsEnabled = false; 
            }
     
            // Fonction appelée lors du changement de la progression du traitement 
            private void work_ProgressChanged(object sender, ProgressChangedEventArgs e)
            {
                // Mise à jour de la progress bar
                Progress.Value = e.ProgressPercentage;
     
                // et du label
                lblAvancement.Content = e.ProgressPercentage.ToString() + " / " + Progress.Maximum;
     
                // et le label action
                lblAction.Content = e.UserState.ToString();
            }
     
            // Fonction de traitement 
            private void Traitement(object data, DoWorkEventArgs e)
            {
                // On vérifie si la fin du traitement a été demandée
                int i = 0;
                while (!work.CancellationPending && i < int.Parse(e.Argument.ToString()))
                {
                    System.Threading.Thread.Sleep(100);
     
                    i++;
     
                    // Indique au BackgroundWorker qu'il y a eu progression avec le pourcentage en paramètre
                    work.ReportProgress(i,"En cours de traitement");
                }
     
                // A la fin du traitement on peut renvoyer un résultat via la propriété result de l'objet DoWorkEventArgs
                e.Result = "Traitement terminé";
            }
     
            // Fonction exécutée lorsque l'événement de fin de traitement est déclenchée
            void work_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
            {
                // L'objet RunWorkerCompletedEventArgs peut renvoyer une propriété (Error) contenant une information en cas d'erreur
                if (e.Error != null)
                {
                    MessageBox.Show(e.Error.ToString());
                }
                else
                {
                    // Ou un résultat via la propriété Result
                    lblAction.Content = e.Result.ToString();
                }
                btnStart.IsEnabled = true;
                btnEnd.IsEnabled = false;
            }
     
            // Bouton Start, lance le BackgroundWorker
            private void btnStart_Click(object sender, RoutedEventArgs e)
            {
                // nMaxValue est un argument pour la fonction de traitement
                work.RunWorkerAsync(nMaxValue);
                btnStart.IsEnabled = false;
                btnEnd.IsEnabled = true;
            }
     
            // Bouton pour annuler le traitement
            private void btnEnd_Click(object sender, RoutedEventArgs e)
            {
                work.CancelAsync();
                btnStart.IsEnabled = true;
                btnEnd.IsEnabled = false;
            }
        }
    J'espère que le code est suffisamment compréhensif.
    Articles sur les technologies .NET

    Une réponse vous a aidé ? utilisez le bouton

    Votre problème est résolu ? utilisez le bouton

  3. #3
    Membre averti
    Profil pro
    Inscrit en
    Novembre 2012
    Messages
    12
    Détails du profil
    Informations personnelles :
    Localisation : Suisse

    Informations forums :
    Inscription : Novembre 2012
    Messages : 12
    Par défaut
    Merci de la réponse rapide.

    J'ai essayé ta solution et j'obtient une exception InvalidOperationException "Le thread appelant doit être en mode STA, comme l'exigent de nombreux composants de l'interface utilisateur."

    Ça arrive quand j'essaie de créer un contrôle dans le code... D'après ce que j'ai lu ce type de thread n'est pas adapté pour travailler avec du GUI.

    Une idée?

  4. #4
    Membre Expert


    Homme Profil pro
    Développeur informatique
    Inscrit en
    Avril 2006
    Messages
    970
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 48
    Localisation : Belgique

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

    Informations forums :
    Inscription : Avril 2006
    Messages : 970
    Par défaut
    Dans quelle fonction crée tu ton contrôle?
    Articles sur les technologies .NET

    Une réponse vous a aidé ? utilisez le bouton

    Votre problème est résolu ? utilisez le bouton

  5. #5
    Membre Expert


    Homme Profil pro
    Développeur informatique
    Inscrit en
    Avril 2006
    Messages
    970
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 48
    Localisation : Belgique

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

    Informations forums :
    Inscription : Avril 2006
    Messages : 970
    Par défaut
    A mon avis , tu essaies de créer ton contrôle dans la fonction de traitement du thread. Dans le bout de code suivant je crée un simple label sur ma fenêtre (n'importe où ce n'est qu'un test ;-) ) :

    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
     
    // Fonction exécutée lorsque l'événement de fin de traitement est déclenchée
            void work_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
            {
                // L'objet RunWorkerCompletedEventArgs peut renvoyer une propriété (Error) contenant une information en cas d'erreur
                if (e.Error != null)
                {
                    MessageBox.Show(e.Error.ToString());
                }
                else
                {
                    // Ou un résultat via la propriété Result
                    lblAction.Content = e.Result.ToString();
                }
                btnStart.IsEnabled = true;
                btnEnd.IsEnabled = false;
     
                Label lbl = new Label();
                lbl.Content = "Je suis la ";
     
                grdPrinc.Children.Add(lbl);
            }
    Cette fonction étant celle appelée par l'événement "RunWorkerCompleted" du thread principal, elle fait partie de celui ci et donc pas de problème pour créer tes contrôles.

    Sinon j'ai trouvé cette discutions qui traite d'un problème similaire :

    http://www.developpez.net/forums/d96...stathread-wpf/

    J'espère que cela va t'aider
    Articles sur les technologies .NET

    Une réponse vous a aidé ? utilisez le bouton

    Votre problème est résolu ? utilisez le bouton

  6. #6
    Membre averti
    Profil pro
    Inscrit en
    Novembre 2012
    Messages
    12
    Détails du profil
    Informations personnelles :
    Localisation : Suisse

    Informations forums :
    Inscription : Novembre 2012
    Messages : 12
    Par défaut
    Après avoir relu et encore relu le code que tu as proposé dans ta première réponse je crois surtout que je n'arrive pas à comprendre qu'est-ce qui fait quoi...

    entre
    work_ProgressChanged
    Traitement
    work_RunWorkerCompleted

    je ne vois pas du tout où je suis censé mettre mon code...

Discussions similaires

  1. Mise à jour TreeView à partir de différents thread
    Par 1234567890 dans le forum Windows Forms
    Réponses: 2
    Dernier message: 17/07/2009, 10h40
  2. Remplir une treeview dans un nouveau thread
    Par vaxxx dans le forum VB.NET
    Réponses: 1
    Dernier message: 18/12/2008, 15h44
  3. [RCP] Treeviewer non thread-safe ?
    Par Guildux dans le forum Eclipse Platform
    Réponses: 4
    Dernier message: 09/01/2007, 13h00
  4. [C#] Thread et treeviews
    Par bart64 dans le forum Windows Forms
    Réponses: 6
    Dernier message: 08/11/2006, 21h06
  5. [C#] Thread et Treeview
    Par Royd938 dans le forum Windows Forms
    Réponses: 13
    Dernier message: 02/12/2005, 10h40

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