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 Forms Discussion :

Chargement d'une ListView à l'aide d'un BackgroundWorker - SQL.


Sujet :

Windows Forms

  1. #1
    Membre du Club Avatar de mathisdu42
    Homme Profil pro
    Développeur .NET
    Inscrit en
    Mars 2013
    Messages
    168
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Développeur .NET

    Informations forums :
    Inscription : Mars 2013
    Messages : 168
    Points : 64
    Points
    64
    Par défaut Chargement d'une ListView à l'aide d'un BackgroundWorker - SQL.
    Bonjour, bonsoir à tous.

    Depuis peu de temps j'essaye d'intégrer des BackgroundWorker à mon programme pour éviter les freeze intempestifs de l'application. Actuellement j'ai une ListView qui récupère quelques 9000 lignes de ma base de données pour que le BgW soit utile et pour des tests. J'ai donc mis en place le BgW et fait fonctionné ce dernier correctement. Seulement, j'aimerais rajouter une ProgressBar pour afficher la progression du chargement de la liste, seulement, j'ai un peu de mal à la faire fonctionner.

    Voici le code :

    Lancement du formulaire :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
     private void userList_Load(object sender, EventArgs e)
            {
                if (!backgroundWorker1.IsBusy)
                {
     
                    metroListView1.Visible = false;
                    progressBar1.Visible = true;
                    metroLabel2.Visible = true;
                    backgroundWorker1.RunWorkerAsync();
     
                }
            }
    Le DoWork du BackgroundWorker AVEC le code de la ProgressBar que j'ai tenté d'essayer, sans succès :

    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
    private void bgw1_DoWork(object sender, DoWorkEventArgs e)
            {
     
                try
                {
     
                    using (var cnx = new MySqlConnection("ConnectionString"))
                    {
     
                        cnx.Open();
                        var cmd = new MySqlCommand("SELECT * FROM Account", cnx);
                        using (var dr = cmd.ExecuteReader())
                        {
     
                            while (dr.Read())
                            {
                                metroListView1.Invoke((MethodInvoker)delegate
                                {
     
     
                                    ListViewItem listitem = new ListViewItem(dr["Username"].ToString());
                                    listitem.SubItems.Add(dr["Rang"].ToString());
                                    listitem.SubItems.Add(dr["Mail"].ToString());
                                    metroListView1.Items.Add(listitem);
                                });
     
     
     
                                progressBar1.Invoke((MethodInvoker)delegate
                                {
     
                                    progressBar1.Maximum = metroListView1.Items.Count;//Dt is your DataTable
                                    for (int i = 0; i < metroListView1.Items.Count; i++)
                                    {
                                        //Do Work
                                        progressBar1.Value = i + 1;
                                    }
     
                                });
     
                            }
                        }
     
                    }
                }
     
                catch (Exception ex)
                {
                    var eb = new ErreurBdd();
                    eb.ShowDialog();
                    LogException("errorLog", "Error", ex.Message + " - | " + DateTime.Now + " | Build: " + Application.ProductVersion + Environment.NewLine);
     
     
                }
            }
    Le ProgressChanged du BackgroundWorker :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    private void bgw1_ProgressChanged(object sender, ProgressChangedEventArgs e)
            {
                progressBar1.Value = e.ProgressPercentage;
                metroLabel2.Text = string.Format("Chargement en cours, veuillez patienter...{0}%", e.ProgressPercentage);
            }

    Donc voilà, en ce qui me concerne je pense qu'il est logique que le code suivant ne fonctionne pas étant donné qu'il se situe dans la boucle "While" mais alors où le placer ?

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
     progressBar1.Invoke((MethodInvoker)delegate
                                {
     
                                    progressBar1.Maximum = metroListView1.Items.Count;//Dt is your DataTable
                                    for (int i = 0; i < metroListView1.Items.Count; i++)
                                    {
                                        //Do Work
                                        progressBar1.Value = i + 1;
                                    }
     
                                });
    Merci,
    Cordialement.

  2. #2
    Membre confirmé
    Avatar de nouanda
    Homme Profil pro
    Hobbyist
    Inscrit en
    Mai 2002
    Messages
    246
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Australie

    Informations professionnelles :
    Activité : Hobbyist

    Informations forums :
    Inscription : Mai 2002
    Messages : 246
    Points : 627
    Points
    627
    Par défaut
    En effet, il y a quelques erreurs de logique ici.

    Tout d'abord, la façon dont tu fais avancer la progress bar: tu le fais une fois que ta listview est remplie, donc cela n'a pas d’intérêt. Visuellement, je ne suis même pas sur que Windows ait le temps de rafraîchir l'affichage le temsp de finir la boucle while, donc tu ne vois rien du tout.
    [Edit] Je ne m’étais pas rendu compte que tu faisais cela a chaque itération du dr.Read(). Sans vouloir être méchant, c'est encore plus un non-sens. Mais je ne vais rien dire, je suis sur que mes projets en contiennent plein aussi...

    Ensuite, tu n'appelles jamais la méthode ReportProgress(Int32), qui lance l’événement ProgressChanged, et c'est cela qu'il te manque.

    Ce que je ferais:
    • enlever la partie progressbar/boucle while dans la méthode DoWork.
    • dans le DoWork, effectuer une première étape qui consiste a récupérer le compte d'items et définir cela comme le maximum de ta progressbar
      Par exemple:
      Code : Sélectionner tout - Visualiser dans une fenêtre à part
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
       using (var cnx = new MySqlConnection("ConnectionString"))
                      { 
                          cnx.Open();
                          var cmd = new MySqlCommand("SELECT COUNT(*) FROM Account", cnx);
                          int itemscount = cmd.ExecuteScalar(); // il faut que modifie cela pour verifier que le ExecuteScalar ne provoque pas d'InvalidCastException.
                          cmd.CommandText = "SELECT * FROM Account";
                          using (var dr = cmd.ExecuteReader())
                          { 
                              while (dr.Read())
                               etc...
    • Ajouter un compteur dans la méthode DoWork que tu incrémentes dans la boucle while dr.read()
    • Appeler backgroundWorker1.ReportProgress(Math.Floor(compteur / itemscount)) a la fin de la boucle while dr.read()
    • Et dans le ProgressChanged, simplement faire progressBar1.Value = e.ProgressPercentage;
    " Entre le Savoir et le Pouvoir, il y a le Vouloir "

    Desole pour les accents, je suis en QWERTY...

  3. #3
    Membre du Club Avatar de mathisdu42
    Homme Profil pro
    Développeur .NET
    Inscrit en
    Mars 2013
    Messages
    168
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Développeur .NET

    Informations forums :
    Inscription : Mars 2013
    Messages : 168
    Points : 64
    Points
    64
    Par défaut
    Bonsoir à toi

    Merci pour ta réponse,

    Tout d'abord ne t'en fait pas je ne prends pas mal ce que tu me dis, bien au contraire, ça me permettra de m'améliorer et d'apprendre de mes erreurs Donc en effet, avec du recul, je me rends bien compte que ce que je faisais n'avait aucun sens. J'ai donc étudié et mis en pratique les explications que tu m'as fournies mais je ne comprends pas tout.. Je vais peut être passer pour je-ne-sais-quoi mais bon, faut bien passer par là un jour ou l'autre.
    Ce que je comprends pas trop c'est les deux points suivants :
    • Ajouter un compteur dans la méthode DoWork que tu incrémentes dans la boucle while dr.read()
    • Et ce code : backgroundWorker1.ReportProgress(Math.Floor(Compteur / itemscount))


    Je ne comprends pas pourquoi il faut incrémenter un compteur ? Pour la progressBar ? Et ce compteur, il compte quoi ? les Items de la listView ?
    Et pourquoi diviser le résultat du compteur par le nombre d'Items récupérés dans la base de données ?

    Désolé si je te bombarde de questions qui peuvent paraitre bête mais c'est simplement pour y voir plus clair

    Merci !

  4. #4
    Membre confirmé
    Avatar de nouanda
    Homme Profil pro
    Hobbyist
    Inscrit en
    Mai 2002
    Messages
    246
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Australie

    Informations professionnelles :
    Activité : Hobbyist

    Informations forums :
    Inscription : Mai 2002
    Messages : 246
    Points : 627
    Points
    627
    Par défaut
    Il faut un compteur, car la méthode ReportProgress demande un le pourcentage de complétion en paramètre. Et comme le dit si gentiment la documentation:
    Citation Envoyé par MSDN
    It is up to you to implement a meaningful way of measuring your background operation's progress as a percentage of the total task completed.
    Il faut donc un moyen de calculer ce pourcentage de complétion. Et en l’occurrence, il faut connaitre le nombre de lignes a traiter (récupérée avec le select count(*)), et où tu en es de la lecture. D’où l’idée d'avoir un compteur.
    Dans mon idée, tu l'initialises a zéro avant d'ouvrir la connexion, et tu l'incrémentes juste avant la fin de la boucle while. Comme ça, tu sais exactement combien de lignes du reader ont été lues.
    Tu pourrais également l'initialiser en dehors du try/catch, ce qui te permets, en cas d'exception a la lecture, de savoir combien de lignes ont été lues avant de planter.
    Il est possible en effet utiliser comme compteur metroListView1.Items.Count (et donc écrire backgroundWorker1.ReportProgress(Math.Floor(metroListView1.Items.Count/ itemscount))), juste après avoir fait metroListView1.Items.Add(listitem); mais si jamais un jour, tu reprends ton code, et décide que certaines entrée ne doivent pas être affichées dans la ListView, tu perds l'information.

    Au passage, il faut vérifier que ton backgroundworker a sa propriété WorkerReportsProgress = true, sinon ReportProgress va générer une InvalidOperationException
    " Entre le Savoir et le Pouvoir, il y a le Vouloir "

    Desole pour les accents, je suis en QWERTY...

  5. #5
    Membre du Club Avatar de mathisdu42
    Homme Profil pro
    Développeur .NET
    Inscrit en
    Mars 2013
    Messages
    168
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Développeur .NET

    Informations forums :
    Inscription : Mars 2013
    Messages : 168
    Points : 64
    Points
    64
    Par défaut
    Je pense avoir saisi la chose. J'ai fait ce que tu as dit, une requête SELECT Count(*) avec un ExecuteScalar que j'ai converti en int itemscount = (Convert.ToInt32(cmd.ExecuteScalar())); ensuite j'ai créé une variable int compteur = 0; que j'ai placé avant l'ouverture de la connexion. Ensuite dans la boucle While (dr.Read()) j'ai dans un premier temps incrémenté le compteur par compteur++ (je doute fort que ce soit ce qu'il faut faire.. J'ai pensé à une éventuelle boucle for que j'aurai incrémenté puis ajouté un calcul de pourcentage ?). Dans un second temps j'ai mis le code pour ajouter les Items de la BDD dans la listView (à l'intérieur de la boucle While) puis en dernier inscrit backgroundWorker1.ReportProgress(Math.Floor(compteur / itemscount));Voilà ce que j'ai fait. Évidemment je pense fort que ce code est faux puisque premièrement, la listView duplique tous les Items (par 6), ensuite j'ai dû faire une erreur de conversion étant donné que VS m'indique du Floor de Math.Floor que
    L'appel est ambigu entre les méthodes ou les propriétés : 'Math.Floor(decimal)' et 'Math.Floor(double)'
    Et pour finir je pensais ajouter dans la boucle While un if (listView1.Items.Count == itemscount) break; ?

    Merci.

  6. #6
    Membre confirmé
    Avatar de nouanda
    Homme Profil pro
    Hobbyist
    Inscrit en
    Mai 2002
    Messages
    246
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Australie

    Informations professionnelles :
    Activité : Hobbyist

    Informations forums :
    Inscription : Mai 2002
    Messages : 246
    Points : 627
    Points
    627
    Par défaut
    Tu peux envoyer ton code? Il n'y a pas de raison d'avoir des items en double...
    Pour l'ambiguite, c'est de ma faute... Essaye de faire directement backgroundWorker1.ReportProgress((compteur *100 / itemscount)); Apparemment pas besoin de passer par Math.Floor avec les integers (je l'ignorais totalement...).
    " Entre le Savoir et le Pouvoir, il y a le Vouloir "

    Desole pour les accents, je suis en QWERTY...

  7. #7
    Membre du Club Avatar de mathisdu42
    Homme Profil pro
    Développeur .NET
    Inscrit en
    Mars 2013
    Messages
    168
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Développeur .NET

    Informations forums :
    Inscription : Mars 2013
    Messages : 168
    Points : 64
    Points
    64
    Par défaut
    En effet c'est bizarre, peut-être que cela vient du fait qu'on fasse deux SELECT avec la même variable ?

    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
    try
                {
                    using (var cnx = new MySqlConnection("bahfghdgf"))
                    {
                        int compteur = 0;
     
                        cnx.Open();
                        var cmd = new MySqlCommand("SELECT COUNT(*) FROM Account", cnx);
                        int itemscount = (Convert.ToInt32(cmd.ExecuteScalar()));
     
                        progressBar1.Invoke((MethodInvoker)delegate
                        { progressBar1.Minimum = 0; progressBar1.Maximum = itemscount; });
     
                        cmd.CommandText = "SELECT * FROM Account";
                        using (var dr = cmd.ExecuteReader())
                        {
     
                            while (dr.Read())
                            {
                                compteur++;
     
                                metroListView1.Invoke((MethodInvoker)delegate
                                {
     
                                    ListViewItem listitem = new ListViewItem(dr["Username"].ToString());
                                    listitem.SubItems.Add(dr["Rang"].ToString());
                                    listitem.SubItems.Add(dr["Mail"].ToString());
                                    metroListView1.Items.Add(listitem);
     
                                });
     
                                backgroundWorker1.ReportProgress((compteur * 100 / itemscount));
     
                            }                        
                        } 
                    }
                }
     
                catch (Exception ex)
                {
                    var eb = new ErreurBdd();
                    eb.ShowDialog();
                    LogException("errorLog", "Error", ex.Message + " - | " + DateTime.Now + " | Build: " + Application.ProductVersion + Environment.NewLine);
     
                }
    Pas de souci, j'ai essayé le code et les résultats sont les suivants : La progressBar avance de 1 cm, se réinitialise à 0, avance une nouvelle fois de 1cm puis fin du BgW.
    Il faudrait d'abord trouver ce problème de duplication, par la suite on verra bien le comportement de la progressBar..

    Merci.

  8. #8
    Membre confirmé
    Avatar de nouanda
    Homme Profil pro
    Hobbyist
    Inscrit en
    Mai 2002
    Messages
    246
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Australie

    Informations professionnelles :
    Activité : Hobbyist

    Informations forums :
    Inscription : Mai 2002
    Messages : 246
    Points : 627
    Points
    627
    Par défaut
    Dans ce code, il n'y a rien qui puisse générer des doublons. Est-ce que tu ne lances pas plusieurs fois le backgroundworker? Ce qui pourrait également expliquer que la progressbar repasse a zéro.
    Au passage, il faut laisser le maximum de la progressbar a 100. Le background worker rapporte un progress en pour-cents, donc entre 0 et 100. Mais c'est accessoire.

    Peux tu envoyer également le bout de code qui lance le background worker, ainsi que la méthode ProgressChanged?

    Avec une exécution pas-a-pas, peux-tu identifier la source?
    " Entre le Savoir et le Pouvoir, il y a le Vouloir "

    Desole pour les accents, je suis en QWERTY...

  9. #9
    Membre du Club Avatar de mathisdu42
    Homme Profil pro
    Développeur .NET
    Inscrit en
    Mars 2013
    Messages
    168
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Développeur .NET

    Informations forums :
    Inscription : Mars 2013
    Messages : 168
    Points : 64
    Points
    64
    Par défaut
    Après vérification le backgroundWorker se lance bien une seule fois au lancement du formulaire. Ah oui bien vu pour la progressBar.

    Voici le bout de code exécutant le backgroundWorker :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    private void userList_Load(object sender, EventArgs e)
            {
                if (!backgroundWorker1.IsBusy)
                {
     
                    metroListView1.Visible = false;
                    progressBar1.Visible = true;
                    labelChargement.Visible = true;
                    backgroundWorker1.RunWorkerAsync();
     
                }
            }
    Et la méthode ProgressChanged :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    private void bgw1_ProgressChanged(object sender, ProgressChangedEventArgs e)
            {
                progressBar1.Value = e.ProgressPercentage;
            }
    J'ai fais quelques tests mais sans succès, j'ai réduis les lignes de ma BDD à 3 lignes pour y voir plus clair et ça donne ça :

    Nom : Sans titre.png
Affichages : 365
Taille : 8,6 Ko

    De plus j'ai exécuté le code pas à pas en ajoutant des points d'arrêts mais j'ai rien trouvé de spécial mis à part .. ? :

    Nom : ghjghj.png
Affichages : 269
Taille : 28,7 Ko

  10. #10
    Membre confirmé
    Avatar de nouanda
    Homme Profil pro
    Hobbyist
    Inscrit en
    Mai 2002
    Messages
    246
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Australie

    Informations professionnelles :
    Activité : Hobbyist

    Informations forums :
    Inscription : Mai 2002
    Messages : 246
    Points : 627
    Points
    627
    Par défaut
    Alors...
    Pour l'enumeration vide, c'est normal, un reader est consomme au fur et a mesure de la lecture. Donc ce n'est pas étonnant.

    Ça m’étonne quand même comme problème, donc je suis allé faire un tour sur stackoverflow.
    Une des raisons pourrait être que l’événement DoWork est enregistre deux fois. Cherche toutes les références a backgroundworker1.DoWork (ou bgw1.DoWork)
    Tu as peut être enregistre l’événement une fois dans la partie design (tu trouveras une ligne du genre this.backgroundWorker1.DoWork += new System.ComponentModel.DoWorkEventHandler(this.backgroundWorker1_DoWork); dans le fichier nom-de-ta-forme.designer.cs, et une fois dans la partie code (dans le fichier nom-de-ta-forme.cs). Il suffira d'en enlever un.
    " Entre le Savoir et le Pouvoir, il y a le Vouloir "

    Desole pour les accents, je suis en QWERTY...

  11. #11
    Membre du Club Avatar de mathisdu42
    Homme Profil pro
    Développeur .NET
    Inscrit en
    Mars 2013
    Messages
    168
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Développeur .NET

    Informations forums :
    Inscription : Mars 2013
    Messages : 168
    Points : 64
    Points
    64
    Par défaut
    Citation Envoyé par nouanda Voir le message
    Alors...
    Pour l'enumeration vide, c'est normal, un reader est consomme au fur et a mesure de la lecture. Donc ce n'est pas étonnant.

    Ça m’étonne quand même comme problème, donc je suis allé faire un tour sur stackoverflow.
    Une des raisons pourrait être que l’événement DoWork est enregistre deux fois. Cherche toutes les références a backgroundworker1.DoWork (ou bgw1.DoWork)
    Tu as peut être enregistre l’événement une fois dans la partie design (tu trouveras une ligne du genre this.backgroundWorker1.DoWork += new System.ComponentModel.DoWorkEventHandler(this.backgroundWorker1_DoWork); dans le fichier nom-de-ta-forme.designer.cs, et une fois dans la partie code (dans le fichier nom-de-ta-forme.cs). Il suffira d'en enlever un.
    En effet tu as raison, mon backgroundWorker était enregistré deux fois ! Du coup j'ai supprimé l'évènement du designer.cs et tout refonctionne. Une maladresse de ma part ! En tout cas un grand merci à toi pour cette aide que tu m'as apportée. Bonne continuation à toi et encore merci, ce fut un plaisir

  12. #12
    Membre confirmé
    Avatar de nouanda
    Homme Profil pro
    Hobbyist
    Inscrit en
    Mai 2002
    Messages
    246
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Australie

    Informations professionnelles :
    Activité : Hobbyist

    Informations forums :
    Inscription : Mai 2002
    Messages : 246
    Points : 627
    Points
    627
    Par défaut
    J'ai beaucoup appris (et continue d'apprendre beaucoup) sur les forums Developpez.
    C'est la moindre des choses que de rendre la pareille!
    " Entre le Savoir et le Pouvoir, il y a le Vouloir "

    Desole pour les accents, je suis en QWERTY...

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

Discussions similaires

  1. Réponses: 3
    Dernier message: 07/10/2015, 17h23
  2. Filtrer une listview à l'aide d'un edittext
    Par redmonster dans le forum Composants graphiques
    Réponses: 7
    Dernier message: 03/06/2012, 21h24
  3. Problème de chargement d'une listView dans un thread
    Par Jérémy Lefevre dans le forum Composants graphiques
    Réponses: 3
    Dernier message: 12/10/2011, 17h27
  4. Aide au chargement d'une DLL.
    Par Flow_75 dans le forum C++
    Réponses: 4
    Dernier message: 24/06/2007, 21h24
  5. chargement dans une listview
    Par k_boy dans le forum VC++ .NET
    Réponses: 3
    Dernier message: 04/12/2006, 13h48

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