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 :

[C#et WPF] Utilisation de progressbar dans une autre form


Sujet :

C#

  1. #1
    Membre à l'essai
    Profil pro
    Inscrit en
    Septembre 2013
    Messages
    17
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Septembre 2013
    Messages : 17
    Points : 14
    Points
    14
    Par défaut [C#et WPF] Utilisation de progressbar dans une autre form
    Bonjour à tous.

    Je viens solliciter votre aide Je développe actuellement une application (en C#, et WPF) qui est en fait un "installeur" pour un bouquet d'applications. L'idée étant que lorsque plusieurs applications sont sélectionnées, les installations peuvent prendre un certain temps. Pour cela j'avais dans l'idée de fermer (ou désactiver) ma MainWindow, pour laisser place à une autre form contenant 2 progressbars : l'installation en cours, et l'avancement total.

    Et je n'arrive pas du tout à y parvenir ...

    Ceci est ce qui se produit lorsque l'on appuie sur le bouton "installer". Ca fonctionne, l'application s'installe correctement.
    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
     
            BackgroundWorker backgroundWorker1 = new BackgroundWorker();
     
            private void btn_install_Click(object sender, RoutedEventArgs e)
            {
                backgroundWorker1.DoWork += backgroundWorker1_DoWork;
                backgroundWorker1.ProgressChanged += backgroundWorker1_ProgressChanged;
                backgroundWorker1.WorkerReportsProgress = true;
                backgroundWorker1.WorkerSupportsCancellation = false;
     
                backgroundWorker1.RunWorkerAsync();
                backgroundWorker1.RunWorkerCompleted += backgroundworker1_Completed;
            }
     
            private void backgroundWorker1_DoWork(object sender, System.ComponentModel.DoWorkEventArgs e)
            {
                foreach (string p in lv_selection.Items)
                {
                    Process scriptProc = new Process();
     
                    scriptProc.StartInfo.FileName = @"C:\Windows\System32\wscript.exe";
                    scriptProc.StartInfo.WorkingDirectory = repository + p + "\\";
                    scriptProc.StartInfo.Arguments = repository + p + "\\" + "install_silent.vbs";
                    scriptProc.StartInfo.Verb = "runas";
                    scriptProc.Start();
                    scriptProc.WaitForExit();
                    scriptProc.Close();
                }
            }
     
            private void backgroundWorker1_ProgressChanged(object sender, System.ComponentModel.ProgressChangedEventArgs e)
            {
                //progressForm.progress1.Value = e.ProgressPercentage;
            }
     
            private void backgroundworker1_Completed(object sender, RunWorkerCompletedEventArgs e)
            {
                MainWindow main = new MainWindow();
                if (!(e.Error == null))
                {
                    Console.WriteLine("Error");
                    main.IsEnabled = true;
                }
                else
                {
                    Console.WriteLine("Terminé");
                    main.IsEnabled = true;
                }
     
            }
    Ma seconde form j'arrive bien à l'afficher, mais pas à travailler sur les progressbars ...

    Une aide, explication, ou démonstration serai la bienvenue

    Merci beaucoup par avance.

  2. #2
    Membre averti
    Homme Profil pro
    Développeur .NET
    Inscrit en
    Décembre 2004
    Messages
    304
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 42
    Localisation : Tunisie

    Informations professionnelles :
    Activité : Développeur .NET
    Secteur : High Tech - Électronique et micro-électronique

    Informations forums :
    Inscription : Décembre 2004
    Messages : 304
    Points : 405
    Points
    405
    Par défaut
    il faudra utiliser la méthode ReportProgress.
    Elle permet de définir l'avancement de ta tache.
    Un appel à la méthode ReportProgress fera appel à l’événement ProgressChanged. C'est ici qu'il faudra mettre à jour tes ProgressBar.

  3. #3
    Membre chevronné
    Homme Profil pro
    edi
    Inscrit en
    Juin 2007
    Messages
    905
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Gironde (Aquitaine)

    Informations professionnelles :
    Activité : edi

    Informations forums :
    Inscription : Juin 2007
    Messages : 905
    Points : 1 923
    Points
    1 923
    Par défaut
    Quelques remarques en vrac :

    • l'ajout des listeners sur les événements fait partie de l'initialisation il vaudrait mieux l'avoir au plus près de la création de l'objet BackgroundWorker, que ce soit dans le code du bouton ou dans l'initialisation de la fenêtre ;
    • le delegate pour RunWorkerCompleted devrait être placé avant l'appel à RunWorkerAsync, sinon il pourrait ne jamais être appelé, si le traitement prend fin (par exemple une exception dès le lancement) avant que le delegate ne soit attaché ;
    • le OnProgressChanged ne se déclenche pas magiquement, comme le mentionne zehle il faut appeler régulièrement la méthode ReportProgress pour lever l'événement (à noter que ce ne sera pas sur le thread du DoWork) ; d'où l'intérêt d'avoir un traitement découpé en micro-opérations dans une boucle (typiquement : traiter tous les fichiers d'un répertoire, ou toutes les lignes d'un fichier) ; si tout ce que fait le worker lui-même est de lancer des scripts, la granularité la plus fine que tu pourras reporter sera les débuts et fins de script, pas malheureusement l'exécution des scripts eux-mêmes ;
    • l'événement RunWorkerCompleted ne sera appelé qu'à la toute fin des traitements, ce sera donc un peu tard pour faire l'affichage de la fenêtre de la progressbar ; à ce sujet la fenêtre avec la progress bar peut tout à fait fournir au BackgroundWorker un delegate pour OnProgressChanged afin de mettre à jour sa progressbar ;
    • tu pourrais envisager de désactiver le bouton "Install" quand on clique dessus, afin d'éviter un double clic accidentel, en le réactivant éventuellement quand l'installation a pris fin, d'une manière ou d'une autre ;
    • backgroundWorker1.WorkerSupportsCancellation = false; : pourquoi vouloir interdire à l'utilisateur d'annuler l'installation ?
    • d'ordre plus général, il existe déjà des outils pour créer des installers, est-il nécessaire de faire les choses à la main (j'ai en tête WiX ToolSet qui permet justement de créer un installer par le biais d'un projet Visual Studio (un plugin VS est d'ailleurs disponible) mais il y en a d'autres ?


    Bonne continuation

  4. #4
    Membre à l'essai
    Profil pro
    Inscrit en
    Septembre 2013
    Messages
    17
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Septembre 2013
    Messages : 17
    Points : 14
    Points
    14
    Par défaut
    Merci pour toutes ces remarques

    Certains points sont prévus lorsque j'aurais quelque chose de fonctionnel, tels que la désactivation des contrôles lorsque le bouton est cliqué.
    Je ne connaissais pas Wix Toolset, je vais regardé. Mais le but ici est d'une part que mon client souhaite quelque chose de très personnalisé à terme, et évolutif dans le temps, et d'autre part, ça me permet de m'améliorer en c# un peu à la manière dont on s'entraine dans une langue étrangère.

    L'annulation interdite est pour le moment volontaire.

    Sinon, j'ai effectivement implémenté le ReportProgress. Ca fonctionne, à ceci prêt, qu'elle recommence plusieurs fois son job... Alors c'est surement un problème de débutant... mais mon niveau de fatigue ces jours-ci m'empêche de réfléchir correctement... Je reposte une version simplifiée du code :

    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
     
            private void backgroundWorker1_DoWork(object sender, System.ComponentModel.DoWorkEventArgs e)
            {
                int totalSteps = lv_selection.Items.Count;
     
                foreach (string p in lv_selection.Items)
                {
                    for (int i = 0; i < totalSteps; i++)
                    {
                        Thread.Sleep(500);
                    /*
                     * Ici est positionné la routine d'installation
                        **/
     
                        (sender as BackgroundWorker).ReportProgress((int)(100 / totalSteps) * i, null);
     
                        // Mise à jour du texte de la progressbar
                        this.Dispatcher.Invoke(() =>
                        {
                            progressbarForm.Progress(p);
                        });
                    }
                }
    Je comprends bien mon soucis... La boucle "For" est surement mal positionnée. Ce qui fait que pour chaque item trouvés, et bien la progressbar recommence. Donc pour 4 items, elle va se remplir 4 fois de suite, et de même, réaliser 4 fois les actions demandées ... J'ai essayé, d'intervertir les boucles, ou même de la positionner ailleurs, mais sans résultat.

    Vous pourriez m'aiguiller ?

  5. #5
    Membre chevronné
    Homme Profil pro
    edi
    Inscrit en
    Juin 2007
    Messages
    905
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Gironde (Aquitaine)

    Informations professionnelles :
    Activité : edi

    Informations forums :
    Inscription : Juin 2007
    Messages : 905
    Points : 1 923
    Points
    1 923
    Par défaut
    C'est normal, tu as mis deux boucles, donc pour chaque item tu exécutes le job items fois. Il te faut choisir soit le for, soit le foreach.

    Quand tu appelles ReportProgress et que tu calcules un pourcentage, il te faut te souvenir que la division entière donne le quotient sans le reste, donc tu auras souvent un calcul tronqué, et si le nombre de steps est supérieur à 100 tu auras un progress de 0% jusqu'à la fin.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    for(int i = 1; i <= 200; ++i)
        Console.WriteLine(100 / 200 * i); // 100 / 200 => 0 ; 0 * 1 => 0 ; 0 * 200 => 0
    Pour résoudre le problème (sans utiliser de float) il suffit de mettre la multiplication en premier :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    for(int i = 1; i <= 200; ++i)
        Console.WriteLine(i * 100 / 200); // augmente de 1 tous les 2 tours de boucle (200/100 => 2)
    Tu ne devais pas avoir ce code :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    // Mise à jour du texte de la progressbar
                        this.Dispatcher.Invoke(() =>
                        {
                            progressbarForm.Progress(p);
                        });
    dans le DoWork. Le rôle du DoWork est uniquement de faire le travail en tâche de fond et de signaler son avancement (et éventuellement vérifier l'annulation du processus), pas de gérer de l'affichage. Afficher la progression c'est le rôle du delegate attaché au ProgressChanged. C'est d'autant plus important que ces deux méthodes ne seront pas exécutées sur le même Thread. Si tu crées par exemple un BackgroundWorker dans le Thread de ton UI, le handler du DoWork sera exécuté dans un Thread à part, mais les handlers de ProgressChanged et de RunWorkerCompleted seront exécuté dans le Thread de l'UI. Ce qui signifie que tu n'as pas besoin du Dispatcher dans ces deux dernières méthode, mais que tu ne dois surtout pas accéder à l'UI depuis la méthode du DoWork. D'ailleurs si tu dois passer des paramètres de l'UI (ex. le chemin d'un répertoire à traiter) dans le DoWork, il faut le faire par le biais de l'argument de RunWorkAsync.

  6. #6
    Membre à l'essai
    Profil pro
    Inscrit en
    Septembre 2013
    Messages
    17
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Septembre 2013
    Messages : 17
    Points : 14
    Points
    14
    Par défaut
    Alors effectivement, je m’apprêtais justement à poster ici car j'ai trouvé une solution toute simple à mon problème. Le but étant de créer une variable setée à 0 avant le foreach, et de l'incrémenter à chaque passage.

    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
     
    private void backgroundWorker1_DoWork(object sender, System.ComponentModel.DoWorkEventArgs e)
            {
                int totalSteps = lv_selection.Items.Count;
                int currentStep = 0;
     
    foreach (string p in lv_selection.Items)
                {
                    current++;
     
                    Process scriptProc = new Process();
     
                    /*
                     * Ici est positionné la routine d'installation
                        **/
     
                    this.Dispatcher.Invoke(() =>
                    {
                        progressbarForm.Progress("Installation de " + p + " en cours...");
                    });
     
                    (sender as BackgroundWorker).ReportProgress((int)(100 / totalSteps) * current, null);
                    }
            }
    Ensuite, pour le dispatcher, ce que tu m'explique est très logique, et j'aurais dû y penser. Désolé, c'est la première fois que j'utilise les backgroundworker, donc pas très à l'aise avec pour le moment
    Je vais essayer de faire au mieux pour virer ce dispatcher. Est-ce que je peux te demander des conseils pour avoir ce qu'il convient le mieux de faire pour gérer mon UI pendant l'exécution de la tâche, en dehors du DoWork ?

    Dois-je gérer ça dans la seconde form qui accueille la progressbar ? Dans le backgroundworker_progresschanged ?

    Encore merci pour tes explications.

  7. #7
    Membre à l'essai
    Profil pro
    Inscrit en
    Septembre 2013
    Messages
    17
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Septembre 2013
    Messages : 17
    Points : 14
    Points
    14
    Par défaut
    Décidément, les solutions me viennent évidentes avec un temps de retard.

    J'ai inclus tout simplement la mise à jour de l'affichage dans le progresschanged.

    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
     
            public string pck = null;
     
            private void backgroundWorker1_DoWork(object sender, System.ComponentModel.DoWorkEventArgs e)
            {
                int totalSteps = lv_selection.Items.Count;
                int current = 0;
     
                foreach (string p in lv_selection.Items)
                {
                    current++;
     
                    pck = p;
     
                    // ...
                }
            }
     
            private void backgroundWorker1_ProgressChanged(object sender, System.ComponentModel.ProgressChangedEventArgs e)
            {
                progressbarForm.progressbar1.Value = e.ProgressPercentage;
                progressbarForm.Progress("Installation de " + pck + " en cours...");
            }
    Merci

  8. #8
    Membre chevronné
    Homme Profil pro
    edi
    Inscrit en
    Juin 2007
    Messages
    905
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Gironde (Aquitaine)

    Informations professionnelles :
    Activité : edi

    Informations forums :
    Inscription : Juin 2007
    Messages : 905
    Points : 1 923
    Points
    1 923
    Par défaut
    ReportProgress peut également prendre un deuxième argument de ton choix, qui sera disponible dans l'événement par la propriété UserState de l'objet ProgressChangedEventArgs. Tu peux par exemple donner le nom du programme en cours d'installation, ou préciser un message.

  9. #9
    Membre à l'essai
    Profil pro
    Inscrit en
    Septembre 2013
    Messages
    17
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Septembre 2013
    Messages : 17
    Points : 14
    Points
    14
    Par défaut
    Oui, du coup c'est exactement ce que j'ai fait.

    L'application en cours d'installation s'affiche dans la barre de progression.

Discussions similaires

  1. Charger ProgressBar dans une autre Activity
    Par Raz-X dans le forum Android
    Réponses: 3
    Dernier message: 23/11/2011, 16h48
  2. Réponses: 2
    Dernier message: 16/08/2009, 13h20
  3. Utiliser un tableau dans une autre frame
    Par reureu dans le forum VB.NET
    Réponses: 2
    Dernier message: 03/08/2007, 14h23
  4. Réponses: 1
    Dernier message: 24/03/2007, 18h50
  5. [VB.NET] Utilisation d'une variable dans une autre form (!)
    Par neuropathie dans le forum Windows Forms
    Réponses: 5
    Dernier message: 08/12/2005, 13h09

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