Précédent   Forum du club des développeurs et IT Pro > Dotnet > Langages > VB.NET
VB.NET Forum d'entraide sur la programmation Visual Basic .NET. Avant de poster -> FAQ VB.NET, Articles VB.NET, Sources VB.NET
Partagez cette discussion sur d'autres réseaux sociaux : Viadeo Twitter Google Facebook Digg Delicious MySpace Yahoo
Réponse
 
Outils de la discussion
Publicité
'
Vieux 07/01/2013, 17h35   #1
prologic
Membre régulier
 
Homme
Technicien Help Desk
Inscription : novembre 2006
Messages : 110
Détails du profil
Informations personnelles :
Sexe : Homme
Localisation : France

Informations professionnelles :
Activité : Technicien Help Desk
Secteur : Bâtiment Travaux Publics

Informations forums :
Inscription : novembre 2006
Messages : 110
Points : 82
Points : 82
Par défaut Modifier ListView dans un thread secondaire

Bonjour,

Je travaille actuellement sur VB 2010 Express à créer un logiciel de données qui remplit une ListView.

L'interrogation est cyclique dans un thread secondaire et même si le programme fonctionne la solution ne me satisfait pas. Globalement, à chaque itération de la boucle, le programme efface le contenu du ListView via un invoke.

Code :
Me.TableLecture.Invoke(New Action(Sub() Me.TableLecture.Items.Clear()))
Et par la suite, les valeurs sont générées par la lecture.

Code :
1
2
3
4
5
6
Dim item As ListViewItem
item = New ListViewItem({Id, Nom, Valeur, Unite})
 
If Me.TableLecture.InvokeRequired Then
Me.TableLecture.Invoke(New Action(Sub() Me.TableLecture.Items.Add(item)))
End If
Du coup, on obtient un effet clignotant fortement désagréable pour celui qui surveille les informations affichées (le fichier texte est généré via un programme de communication indépendant).

Je cherche une solution pour modifier les lignes sans les effacer et gagner en lisibilité. Je pense qu'il faut passer par un Invoke ou Delegate ou les deux (je vois pas très bien la différence et je peine à trouver des tuto là dessus).

A savoir que dans le tableau {Id, Nom, Valeur, Unite} la valeur de Nom est unique et que je cherche à modifier Valeur. Id et Unite n'ont d'importance que pour le lecteur.

Merci d'avance pour l'attention accordé à ce message, n'hésitez pas a me demandé plus d'informations.
prologic est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 07/01/2013, 18h44   #2
sinople
Membre Expert
 
Homme Marco Guignard
Ingénieur développement logiciels
Inscription : avril 2009
Messages : 729
Détails du profil
Informations personnelles :
Nom : Homme Marco Guignard
Âge : 30
Localisation : Suisse

Informations professionnelles :
Activité : Ingénieur développement logiciels
Secteur : Finance

Informations forums :
Inscription : avril 2009
Messages : 729
Points : 1 385
Points : 1 385
Envoyer un message via MSN à sinople
Je suis pas sur d'avoir tout compris à ta problématique mais voila mon conseille.

Il faut créer une fonction UpdateMyListview(uneDataSource As uneCollectionQuiVabien). Cette fonction s'occupe de synchroniser les éléments de ton listview avec les info contenues dans uneDataSource. La version la plus simple étant de vider le listview puis le remplir à partir de uneDataSource. La version la plus élaborée serait d'identifier les différence entre uneDataSource et les items de Listview puis d'effectuer des opérations d'ajout, suppression et modification pour synchroniser les 2.

Dans ton thread secondaire, tu fais un invoke sur UpdateMyListview en passant toutes les données des items en paramètre (la variable uneDataSource).

Ainsi il n'y aura plus de clignotement sur ton affichage (si tu implémentes la version complexe de UpdateMyListview, il n'y aura même plus de problème de remise à zéro de la sélection du listview) pour autant que tu appelles de manière modérée la procédure de mise à jour !

Bref en conclusion:

Une seul fonction de callback (invoke) s'occupant de la mise à jour de l'affichage pour laquelle on fournit l'entier des données.

En complément:

Vérifier que le multithreading soit bien nécessaire en utilisant un timer avec une fonction récupérant les données + mise à jour du listview. S'il n'y a pas de freez de l'interface, c'est que le deuxième thread n'est pas nécessaire. Et si le multithread n'est pas nécessaire, faut mieux pas l'utiliser (vu que ca apporte une complexité importante pour la maintenance et le debuggage de l'application)

Il peut exister d'autres solutions (multi threadées) plus élégantes, voir efficaces en fonction de ce que tu fais réellement avec ton programme. N'hésite pas à donner plus d'info si tu ne trouves pas ton bonheur dans ce message.
sinople est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 07/01/2013, 19h31   #3
wallace1
Membre habitué
 
Homme
Administrateur systèmes et réseaux
Inscription : octobre 2008
Messages : 67
Détails du profil
Informations personnelles :
Sexe : Homme
Localisation : France

Informations professionnelles :
Activité : Administrateur systèmes et réseaux
Secteur : Aéronautique - Marine - Espace - Armement

Informations forums :
Inscription : octobre 2008
Messages : 67
Points : 146
Points : 146
@Prologic : Le backgroundWorker permet aisément de faire ce que tu souhaites. Regardes ici : http://www.developpez.net/forums/d12...e/#post7054121

N'hésites pas si tu as des questions, je te donnerais des explications détaillées.
wallace1 est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 07/01/2013, 20h46   #4
Pol63
Expert Confirmé Sénior
 
Avatar de Pol63
 
Homme Sébastien
Développeur .NET / DBA SQL SERVER
Inscription : avril 2007
Messages : 10 279
Détails du profil
Informations personnelles :
Nom : Homme Sébastien
Âge : 31
Localisation : France, Puy de Dôme (Auvergne)

Informations professionnelles :
Activité : Développeur .NET / DBA SQL SERVER
Secteur : Industrie

Informations forums :
Inscription : avril 2007
Messages : 10 279
Points : 17 216
Points : 17 216
Envoyer un message via Skype™ à Pol63
ca scintille car tu ajoutes un item, le redessin de l'interface est fait, tu ajoutes un item etc...

il y a plusieurs solutions pour évite le scintillement

tu peux créer tous les items dans ton thread puis à la fin de la lecture des données repasser par l'invoke et faire tous les .add d'un coup

si la lecture des données est longue et que tu veux quand meme un remplissage au fur et à mesure, il faut soit ajouter les items 10 par 10 par exemple ou préciser que tu ne veux un rafraichissement de l'interface de temps en temps (il doit y avoir beginedit et endedit (ou beginupdate) sur le listview, entre les 2 appels il n'y a pas d'ajout visuel, ca oblige à jongler un peu avec un thread en parallèle)

dans le 1er cas il faut aussi utiliser begin edit et end edit, ca gagne un peu de temps
Pol63 est actuellement connecté   Envoyer un message privé Réponse avec citation 00
Vieux 08/01/2013, 09h20   #5
prologic
Membre régulier
 
Homme
Technicien Help Desk
Inscription : novembre 2006
Messages : 110
Détails du profil
Informations personnelles :
Sexe : Homme
Localisation : France

Informations professionnelles :
Activité : Technicien Help Desk
Secteur : Bâtiment Travaux Publics

Informations forums :
Inscription : novembre 2006
Messages : 110
Points : 82
Points : 82
Bonjour, et merci pour les réponses.

Le second thread est necessaire pour la mise à jour de la ListView car c'est une boucle infinie (jusqu'à l'interruption demandé par l'utilisateur).

Globalement, j'ai besoin d'un Invoke sur un Subitems(i), mais forcément c'est pas simple. Le plus facile serait de déclarer la ListView comme appartenant au second thread, ce qui me permettrait toutes les modifications de manière beaucoup plus simple, mais malheureusement je ne pense pas que ce soit possible.

@sinople : il me semblait qu'il n'était pas possible de passer des paramètres via un invoke ?

@pol63 : le remplissage des valeurs est immédiat. La ListView contient une vingtaine de ligne dont seulement une colonne sur trois est a mettre à jour en temps réel.

@wallace1 : j'avais creusé cette possibilité mais ca fait très compliqué surtout que mon programme déclare un thread secondaire de lui même que j'utilise pour les mise à jour.

Je vais bosser sur l'histoire de l'invoke, je pense que c'est la solution la plus simple compte tenu de mon programme actuel.
prologic est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 08/01/2013, 09h44   #6
Pol63
Expert Confirmé Sénior
 
Avatar de Pol63
 
Homme Sébastien
Développeur .NET / DBA SQL SERVER
Inscription : avril 2007
Messages : 10 279
Détails du profil
Informations personnelles :
Nom : Homme Sébastien
Âge : 31
Localisation : France, Puy de Dôme (Auvergne)

Informations professionnelles :
Activité : Développeur .NET / DBA SQL SERVER
Secteur : Industrie

Informations forums :
Inscription : avril 2007
Messages : 10 279
Points : 17 216
Points : 17 216
Envoyer un message via Skype™ à Pol63
il est possible de passer des paramètres via un invoke

sinon maintenant qu'on a plus de détails, on sait que tu as peu de données, donc le mieux serait dans ton thread de remplir une variable privée avec les données du listview, à la fin de la boucle, tu fais un invoke sur une méthode qui va faire la suspension de rafraichissement et mettre les données dans le listview à partir de la variable privée
comme ca pas de scintillement et pas de passage de paramètre, une variable privée peut etre utilisée depuis tous les threads

si la lecture des données prend moins de 100ms et qu'elle ne peut pas bloquer, un timer aurait suffit d'ailleurs
Pol63 est actuellement connecté   Envoyer un message privé Réponse avec citation 00
Vieux 08/01/2013, 11h12   #7
prologic
Membre régulier
 
Homme
Technicien Help Desk
Inscription : novembre 2006
Messages : 110
Détails du profil
Informations personnelles :
Sexe : Homme
Localisation : France

Informations professionnelles :
Activité : Technicien Help Desk
Secteur : Bâtiment Travaux Publics

Informations forums :
Inscription : novembre 2006
Messages : 110
Points : 82
Points : 82
Bon, je vois très bien le chemin que je dois prendre, mais il y a un rocher énorme sur la route, qui s'apelle Invoke.

Voici la fonction que je cherche... globalement

Code :
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
Public Function AddList(ByVal Addr As Integer, ByVal Name As String, ByVal Value As String, ByVal Unit As String)
        Dim item As ListViewItem = New ListViewItem({Addr, Name, Value, Unit})
        Dim trouve As Boolean = False
 
        'Si Invoke requis
        If Me.TableLecture.InvokeRequired Then
 
            'Boucle test en invoke que je n'ai pas encore trouvé
 
            'On insert les données
            If trouve = False Then Me.TableLecture.Invoke(New Action(Sub() Me.TableLecture.Items.Add(item)))
 
        Else 'Si Invoke non requis
 
            'Boucle test sur les entrées
            For Each ligne In TableLecture.Items
 
                'Si l'entrée est déjà présente
                If ligne.SubItems(1).Text = Name Then
                    trouve = True
                    ligne.SubItems(2).Text = Value 'Mise à jour de l'entrée
                End If
            Next
 
            'On ajoute une nouvelle entrée si elle n'existe pas
            If trouve = False Then TableLecture.Items.Add(item)
 
        End If
        Return 0
    End Function
prologic est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 08/01/2013, 13h08   #8
wallace1
Membre habitué
 
Homme
Administrateur systèmes et réseaux
Inscription : octobre 2008
Messages : 67
Détails du profil
Informations personnelles :
Sexe : Homme
Localisation : France

Informations professionnelles :
Activité : Administrateur systèmes et réseaux
Secteur : Aéronautique - Marine - Espace - Armement

Informations forums :
Inscription : octobre 2008
Messages : 67
Points : 146
Points : 146
@prologic :
il faut que tu decoupes ta procedure addlist (diviser pour mieux regner), tu peux isoler une fonction portant le nom existItem() dans laquelle tu pourrais mettre 1 argument itemName.
En ce qui concerne le passage de parametre pour l invoke tu peux utiliser 1 delegate que tu declares dans la partie globale et lors de l appel de l invoke il te sera propose en tant qu argument. si tu veux passer plusieurs arguments tu peux declarer 1 new object(){param1,param2,param3} et tu les recuperes avec 1 trycast pour redefinir le type sous forme d array :
obj(0) --> c'est le param1
obj(1)--> c est le param2
...etc...
wallace1 est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 08/01/2013, 13h26   #9
Pol63
Expert Confirmé Sénior
 
Avatar de Pol63
 
Homme Sébastien
Développeur .NET / DBA SQL SERVER
Inscription : avril 2007
Messages : 10 279
Détails du profil
Informations personnelles :
Nom : Homme Sébastien
Âge : 31
Localisation : France, Puy de Dôme (Auvergne)

Informations professionnelles :
Activité : Développeur .NET / DBA SQL SERVER
Secteur : Industrie

Informations forums :
Inscription : avril 2007
Messages : 10 279
Points : 17 216
Points : 17 216
Envoyer un message via Skype™ à Pol63
pour l'invoke avec paramètres :


Code :
1
2
3
.invoke(new action(of string)(sub (s as string) mth(s)))
 
sub mth(s as string)
Pol63 est actuellement connecté   Envoyer un message privé Réponse avec citation 10
Vieux 08/01/2013, 14h55   #10
prologic
Membre régulier
 
Homme
Technicien Help Desk
Inscription : novembre 2006
Messages : 110
Détails du profil
Informations personnelles :
Sexe : Homme
Localisation : France

Informations professionnelles :
Activité : Technicien Help Desk
Secteur : Bâtiment Travaux Publics

Informations forums :
Inscription : novembre 2006
Messages : 110
Points : 82
Points : 82
Citation:
Envoyé par Pol63 Voir le message
pour l'invoke avec paramètres :


Code :
1
2
3
.invoke(new action(of string)(sub (s as string) mth("Valeur 1")))
 
sub mth(s as string)
Ok merci pour le code.

Je l'ai adapté à mon formulaire et complété

Code :
1
2
3
4
5
6
7
8
9
10
11
12
13
14
Me.TableLecture.Invoke(New Action(Of String)(Sub(s As String) VerifList(s)))
 
'Test
    Sub VerifList(ByVal s As String)
        Dim trouve As Boolean = False
        For Each ligne In TableLecture.Items
 
            'Si l'entrée est déjà présente
            If ligne.SubItems(1).Text = s Then
                trouve = True
            End If
        Next
        MsgBox(trouve)
    End Sub
Mais ca me retourne une erreur sur le nombre de paramètres.

Citation:
Nombre de paramètres incorrects.

A first chance exception of type 'System.Reflection.TargetParameterCountException' occurred in System.Windows.Forms.dll
Ha oui et je subodore une erreur toute pourrie... et super simple à résoudre
prologic est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 08/01/2013, 15h22   #11
Pol63
Expert Confirmé Sénior
 
Avatar de Pol63
 
Homme Sébastien
Développeur .NET / DBA SQL SERVER
Inscription : avril 2007
Messages : 10 279
Détails du profil
Informations personnelles :
Nom : Homme Sébastien
Âge : 31
Localisation : France, Puy de Dôme (Auvergne)

Informations professionnelles :
Activité : Développeur .NET / DBA SQL SERVER
Secteur : Industrie

Informations forums :
Inscription : avril 2007
Messages : 10 279
Points : 17 216
Points : 17 216
Envoyer un message via Skype™ à Pol63
en effet, il faut donner la valeur du paramètre ^^

invoke a une surcharge qui demande la valeur des paramètres (en paramarray je pense)

Code :
Me.TableLecture.Invoke(New Action(Of String)(Sub(s As String) VerifList(s)),"un string")
et j'ai du m'embrouiller dans la syntaxe que je t'ai donné
sinon essaye avec addressof à la place de sub()
Pol63 est actuellement connecté   Envoyer un message privé Réponse avec citation 10
Vieux 08/01/2013, 16h08   #12
prologic
Membre régulier
 
Homme
Technicien Help Desk
Inscription : novembre 2006
Messages : 110
Détails du profil
Informations personnelles :
Sexe : Homme
Localisation : France

Informations professionnelles :
Activité : Technicien Help Desk
Secteur : Bâtiment Travaux Publics

Informations forums :
Inscription : novembre 2006
Messages : 110
Points : 82
Points : 82
Alors là, pas de géant, j'arrive a récupérer les infos dont j'ai besoin. Par contre, il faut que j'entre deux paramètres pour la fonction de correction, la ligne et la valeur. J'ai tenté l'extrapolation mais ca a foiré.

Code :
1
2
3
4
5
6
7
8
Me.TableLecture.Invoke(New Action(Of String)(Sub(s As String, i As Integer) CorrectionValeurName(s, i)), Name, Value)
 
Function CorrectionValeurName(ByVal s As String, ByVal i As Integer)
        For Each ligne In TableLecture.Items
            If ligne.SubItems(1).Text = s Then ligne.SubItems(2).Text = i
        Next
        Return 0
    End Function
Si j'arrive à obtenir ca, je clos une bonne partie de mon programme. Ce doit être possible de passer un array en paramètre, mais j'aimerais passer les deux valeurs directement.
prologic est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 08/01/2013, 16h35   #13
Pol63
Expert Confirmé Sénior
 
Avatar de Pol63
 
Homme Sébastien
Développeur .NET / DBA SQL SERVER
Inscription : avril 2007
Messages : 10 279
Détails du profil
Informations personnelles :
Nom : Homme Sébastien
Âge : 31
Localisation : France, Puy de Dôme (Auvergne)

Informations professionnelles :
Activité : Développeur .NET / DBA SQL SERVER
Secteur : Industrie

Informations forums :
Inscription : avril 2007
Messages : 10 279
Points : 17 216
Points : 17 216
Envoyer un message via Skype™ à Pol63
action(of string, integer)
Pol63 est actuellement connecté   Envoyer un message privé Réponse avec citation 10
Vieux 08/01/2013, 17h04   #14
prologic
Membre régulier
 
Homme
Technicien Help Desk
Inscription : novembre 2006
Messages : 110
Détails du profil
Informations personnelles :
Sexe : Homme
Localisation : France

Informations professionnelles :
Activité : Technicien Help Desk
Secteur : Bâtiment Travaux Publics

Informations forums :
Inscription : novembre 2006
Messages : 110
Points : 82
Points : 82
Je suis pas loin de trouvé la solution, mais il me reste un problème.

Code :
1
2
3
4
5
6
7
8
9
10
Dim PresenceName As Boolean = Me.TableLecture.Invoke(New Action(Of String)(Function(s As String) VerifPresenceName(s)), Name)
 
    Function VerifPresenceName(ByVal s As String)
        For Each ligne In TableLecture.Items
            If ligne.SubItems(1).Text = s Then
                Return True
            End If
        Next
        Return False
    End Function
Quand je test avec une msgbox directement dans la fonction, j'ai bien un retour True. Par contre PresenceName est toujours à False.
prologic est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 08/01/2013, 17h48   #15
prologic
Membre régulier
 
Homme
Technicien Help Desk
Inscription : novembre 2006
Messages : 110
Détails du profil
Informations personnelles :
Sexe : Homme
Localisation : France

Informations professionnelles :
Activité : Technicien Help Desk
Secteur : Bâtiment Travaux Publics

Informations forums :
Inscription : novembre 2006
Messages : 110
Points : 82
Points : 82
Je pense que je vais contourner le problème en déclarant une variable global et fixer la valeur dans la fonction directement.

Je vous tiens au courant
prologic est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 08/01/2013, 17h59   #16
Pol63
Expert Confirmé Sénior
 
Avatar de Pol63
 
Homme Sébastien
Développeur .NET / DBA SQL SERVER
Inscription : avril 2007
Messages : 10 279
Détails du profil
Informations personnelles :
Nom : Homme Sébastien
Âge : 31
Localisation : France, Puy de Dôme (Auvergne)

Informations professionnelles :
Activité : Développeur .NET / DBA SQL SERVER
Secteur : Industrie

Informations forums :
Inscription : avril 2007
Messages : 10 279
Points : 17 216
Points : 17 216
Envoyer un message via Skype™ à Pol63
new action c'est pour une sub, donc ca ne peut pas récupérer le retour
il faudrait que tu mettes option strict on dans les propriétés du projet, le compilateur évitera alors de te laisser écrire des choses potentiellement fausses, comme une fonction non typée

new func(of string, boolean)

le dernier type est le type de retour
Pol63 est actuellement connecté   Envoyer un message privé Réponse avec citation 10
Vieux 09/01/2013, 09h19   #17
prologic
Membre régulier
 
Homme
Technicien Help Desk
Inscription : novembre 2006
Messages : 110
Détails du profil
Informations personnelles :
Sexe : Homme
Localisation : France

Informations professionnelles :
Activité : Technicien Help Desk
Secteur : Bâtiment Travaux Publics

Informations forums :
Inscription : novembre 2006
Messages : 110
Points : 82
Points : 82
C'est parfait, tout fonctionne comme prévu. Je poste la solution en intégralité pour ceux que ca peut interesser :

Code :
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
    'Pour ajouter (ou modifier) la ListView choisie
    Public Function AddList(ByVal ID As Integer, ByVal Name As String, ByVal Value As String, ByVal Unit As String, ByVal Tableau As ListView)
        Dim item As ListViewItem = New ListViewItem({Addr, Name, Value, Unit})
        Dim trouve As Boolean = False
 
        'Si Invoke requis
        If Tableau.InvokeRequired Then
            PresenceName = Tableau.Invoke(New Func(Of String, Boolean)(Function(s As String) VerifPresenceName(s)), Name)
            Debug.Print(PresenceName)
            If PresenceName = False Then
                'On insert les données
                Tableau.Invoke(New Action(Sub() Tableau.Items.Add(item)))
            Else
                'On corrige les données
                Tableau.Invoke(New Action(Of String, String)(Sub(s As String, i As String) CorrectionValeurName(s, i)), Name, Value)
            End If
        End If
        Return 0
    End Function
 
    Function VerifPresenceName(ByVal s As String)
        For Each ligne In TableLecture.Items
            If ligne.SubItems(1).Text = s Then Return True
        Next
        Return False
    End Function
 
    Function CorrectionValeurName(ByVal s As String, ByVal i As String)
        For Each ligne In TableLecture.Items
            If ligne.SubItems(1).Text = s Then ligne.SubItems(2).Text = i
        Next
        Return 0
    End Function
Ca reste une solution bien plus simple et bien plus adaptable que tout ce que j'avais lu sur les forums. Donc gros merci à tous ceux qui ont posté plus haut.
prologic est déconnecté   Envoyer un message privé Réponse avec citation 00
Réponse Cette discussion est résolue.
Outils de la discussion

Navigation rapide


Fuseau horaire GMT +2. Il est actuellement 23h33.


 
 
 
 
Partenaires

Hébergement Web