Delegate combine [Résolu]

Messages postés
35
Date d'inscription
samedi 13 janvier 2018
Statut
Membre
Dernière intervention
24 mai 2019
- - Dernière réponse : Whismeril
Messages postés
13619
Date d'inscription
mardi 11 mars 2003
Statut
Contributeur
Dernière intervention
18 août 2019
- 14 mai 2019 à 21:15
Bonjour,

Je ne maitrise pas encore complètement la notion de delegate mais dans mon bout de code,
je fais appel à deux invoke pour mettre à jour 2 objets dans mon IHM.

J'ai vu qu'il y a la possibilité de combiner ces deux appels en un seul.
Comment faire pour combiner ces deux appels, lorsque la méthode SetText doit avoir deux paramètres ?

Merci d'avance.

Private Delegate Sub SetText(obj As Object, txt As String)

Private Sub ProtectedWriteText(obj As Object, txt As String)
    If TypeOf obj Is TextBox Then
        CType(obj , TextBox).Text = txt
    ElseIf TypeOf obj Is Label Then
        CType(obj , Label).Text = txt
    End If
End Sub

Private Sub BtnHomeX_Click(sender As Object, e As EventArgs) Handles BtnHomeX.Click
    Dim thread As New Thread(
        Sub()
            Try
                OperationEnCours = True
                StatusBarLabel1.Text = "Prise d'origine du plateau... axe X"
                Carte.Voie(1).Origine()
                Invoke(New SetText(AddressOf ProtectedWriteText), TxbPosX, "0")  ' TxbPosX.Text = "0"
                Invoke(New SetText(AddressOf ProtectedWriteText), LblConsX, "0") ' LblConsX.Text = TxbPosX.Text
            Catch ex As Exception
                If Carte.PortName = Nothing Then
                    LogTrace.WriteLine("#" + GetCurrentMethod().Name + " : Perte de com avec la carte")
                    MessageBox.Show(Me, " Perte de com avec la carte", "Prise d'origine", MessageBoxButtons.OK, MessageBoxIcon.Error)
                Else
                    LogTrace.WriteLine("#" + GetCurrentMethod().Name + " : " + ex.Message)
                    MessageBox.Show(Me, ex.Message, "Prise d'origine", MessageBoxButtons.OK, MessageBoxIcon.Error)
                End If
            Finally
                OperationEnCours = False
            End Try
        End Sub)
    thread.Start()
End Sub

Afficher la suite 

6 réponses

Meilleure réponse
Messages postés
13619
Date d'inscription
mardi 11 mars 2003
Statut
Contributeur
Dernière intervention
18 août 2019
295
1
Merci
De rien

Dire « Merci » 1

Heureux de vous avoir aidé ! Vous nous appréciez ? Donnez votre avis sur nous ! Evaluez CodeS-SourceS

Codes Sources 121 internautes nous ont dit merci ce mois-ci

Commenter la réponse de Whismeril
Messages postés
13619
Date d'inscription
mardi 11 mars 2003
Statut
Contributeur
Dernière intervention
18 août 2019
295
0
Merci
Bonsoir,
Comment faire pour combiner ces deux appels, lorsque la méthode SetText doit avoir deux paramètres ?

ton délégué a déjà 2 paramètres, du coup je ne comprends pas ta question
Nardo26
Messages postés
35
Date d'inscription
samedi 13 janvier 2018
Statut
Membre
Dernière intervention
24 mai 2019
-
Bonsoir,

A la place de ces deux appels consécutifs, n'existe t-il pas un moyen de faire un seul appel ?

Invoke(New SetText(AddressOf ProtectedWriteText), TxbPosX, "0") 
Invoke(New SetText(AddressOf ProtectedWriteText), LblConsX, "0")
Nardo26
Messages postés
35
Date d'inscription
samedi 13 janvier 2018
Statut
Membre
Dernière intervention
24 mai 2019
-
En fait je suis tombé sur Delegate.combine mais je ne vois pas comment l'appliquer pour des Delegate avec passage de paramètres. En effet ma méthode ProtectedWriteText a besoin de 2 paramètres (l'objet et le texte qui doit y être appliqué)

Ceci est en C# mais l'idée est là :
https://stackoverflow.com/questions/38300776/referencing-more-than-one-method-using-one-delegate
Commenter la réponse de Whismeril
Messages postés
35
Date d'inscription
samedi 13 janvier 2018
Statut
Membre
Dernière intervention
24 mai 2019
0
Merci
J'ai un peu remanier mon code et je constate, que le champs du dernier appel à mon délégué n'est pas rafraichit.

   
Private Delegate Sub SetText(sender As Object, s As String)
 
Private Sub UpdateText(obj As Object, txt As String)
    If obj.GetType() Is GetType(TextBox) Then
        Dim t As TextBox = CType(obj, TextBox)
        If t.InvokeRequired Then
            Invoke(New SetText(AddressOf ProtectedWriteText), obj, txt)
        Else
            t.Text = txt
        End If
    ElseIf obj.GetType() Is GetType(Label) Then
        Dim t As Label = CType(obj, Label)
        If t.InvokeRequired Then
            Invoke(New SetText(AddressOf ProtectedWriteText), obj, txt)
        Else
            t.Text = txt
        End If
    End If
End Sub

Private Sub ProtectedWriteText(objAs Object, txt As String)
    If TypeOf sender Is TextBox Then
        CType(sender, TextBox).Text = t
    ElseIf TypeOf sender Is Label Then
        CType(sender, Label).Text = t
    End If
End Sub


Et mes appels dans un thread se font ainsi :

Dim thread As New Thread(
    Sub()
            OperationEnCours = True
            Try
                LogTrace.WriteLine("Mise en position initiale de la machine")
                Carte.SetEnable(True)

                StatusBarLabel1.Text = "Initialisation de la pince..."
                Carte.Voie(3).Origine()
                UpdateText(TxbPosP, "0")
                UpdateText(LblConsP, TxbPosP.Text)


                StatusBarLabel1.Text = "Initialisation de l'altitude du plateau..."
                Carte.Voie(2).Origine() ' Plateau Z
                UpdateText(TxbPosZ, "0")

                StatusBarLabel1.Text = "Initialisation position plateau..."
                Carte.Voie(1).Origine() ' Plateau X            
                UpdateText(TxbPosX, "0")   ' <--- lui est bien rafraichi
                UpdateText(LblConsX, TxbPosX.Text)  ' <--- Mais pas celui-là

                ' … etc, etc...
            Catch ex As Exception
                LogTrace.WriteLine("#" + GetCurrentMethod().Name + " : " + ex.Message)
                MessageBox.Show(ex.Message, "Prise d'origine machine", MessageBoxButtons.OK, MessageBoxIcon.Error)
            End Try
            OperationEnCours = False
        End Sub
        )
        thread.Start()


Le dernier appel à UpdateText() est bien réalisé mais pas l'évènement paint associé au Label LblConsX. Si je sors du TabPage qui contient le label LblConsX et que j'y retourne, la valeur a bien été changée…

Petite remarque : je suis obligé de passer par un délégué pour des Label, des textbox, etc mais curieusement pas pour la barre d'état...
Commenter la réponse de Nardo26
Messages postés
13619
Date d'inscription
mardi 11 mars 2003
Statut
Contributeur
Dernière intervention
18 août 2019
295
0
Merci
Petite remarque : je suis obligé de passer par un délégué pour des Label, des textbox, etc mais curieusement pas pour la barre d'état...

En effet c'est curieux, c'est quoi son type, statusBar ou StatusStrip?

                UpdateText(TxbPosX, "0")   ' <--- lui est bien rafraichi
                UpdateText(LblConsX, TxbPosX.Text)  ' <--- Mais pas celui-là

Là ça me parait logique, tu ne dois pas pouvoir lire TxbPosX.Text, si tu écris
 UpdateText(LblConsX, "Coucou") 
ça devrait marcher.

Enfin, je ne pense pas que tu aies besoin de combiner des délégués.
InvokeRequired est définie dans la classe Control, donc ceci devrait fonctionner que ce soit un label ou un textbox
Private Sub UpdateText(obj As Object, txt As String)
    If obj.GetType() Is GetType(Control) Then
        Dim t As TextBox = CType(obj, Control)
        If t.InvokeRequired Then
            Invoke(New SetText(AddressOf ProtectedWriteText), obj, txt)
        Else
            ProtectedWriteText(obj, txt)
        End If
    End If
End Sub

Nardo26
Messages postés
35
Date d'inscription
samedi 13 janvier 2018
Statut
Membre
Dernière intervention
24 mai 2019
-
C'est un ToolStripStatusLabel… donc il ne possède pas de méthode InvokeRequired.
Je trouve bizarre que pour la plupart des controls on s'assure que l'accès soit réservé au thread propriétaire et que pour d'autres, on n'en tiens pas compte (même dans un backgroundWorker, l'accès direct au ToolStripStatusLabel ne pose pas de problème).

Concernant ton exemple, j'ai l'option 'Strict on' qui m'interdit de faire des conversions implicites de 'Control' en 'TextBox'.


J'avoue que cette série de if/elseif est moche comme tout. Surtout que les blocs de codes sont identiques. C'est seulement l'utilisation du CType (qui me permet d'accéder à la méthode InvokeRequired) qui m'oblige à écrire de cette manière.
Je cherche une feinte...

Concernant le 2ème appel : Au début du try, j'ai 2 appels consécutifs qui fonctionnent très bien.

UpdateText(TxbPosP, "0")
UpdateText(LblConsP, TxbPosP.Text) ' <- cet accès à .Text fonctionne.


En suivant en pas à pas le code, le dernier appel au délégué se fait bien. C'est juste le redraw/paint de mon LblConsX qui ne se fait pas. Dedans j'ai bien la chaine de caractères de TxbPosX.Text
Commenter la réponse de Whismeril
Messages postés
13619
Date d'inscription
mardi 11 mars 2003
Statut
Contributeur
Dernière intervention
18 août 2019
295
0
Merci
Ha pardon, j’ai fait une erreur en retapant ton code
Private Sub UpdateText(obj As Object, txt As String)
    If obj.GetType() Is GetType(Control) Then
        Dim t As Control = CType(obj, Control)
        If t.InvokeRequired Then
            Invoke(New SetText(AddressOf ProtectedWriteText), obj, txt)
        Else
            ProtectedWriteText(obj, txt)
        End If
    End If
End Sub


C'est un ToolStripStatusLabel… donc il ne possède pas de méthode InvokeRequired

C’est parce que ce n’est pas un contrôle, il ne dérive pas de la classe Control

Pour l’autre point, là je ne sais pas et je suis sur ma tablette, donc je ne peux pas essayer.

Nardo26
Messages postés
35
Date d'inscription
samedi 13 janvier 2018
Statut
Membre
Dernière intervention
24 mai 2019
-
Bonjour Whismeril,

J'ai essayé hier soir, cela ne fonctionne pas mais l'idée est bonne d'y accéder par un cast sur control :

obj.GetType() dans le cas d'un Label va me renvoyer :
{Name ="Label" FullName ="System.Windows.Forms.Label"}

Alors que GetType(Control) lui, contient :
{Name ="Control" FullName="System.Windows.Forms.Control"}



La solution qui fonctionne est celle-ci (et cela me convient ! :) ) :
Private Sub UpdateText(obj As Object, txt As String)
    Dim o As Type = obj.GetType()
    If o Is GetType(Label) Or
       o Is GetType(TextBox) Or
       o Is GetType(cNumBox) Then  ' cNumBox est une classe derivée de TextBox
        Dim t As Control = CType(obj, Control)
        If t.InvokeRequired Then
            Invoke(New SetText(AddressOf ProtectedWriteText), obj, txt)
        Else
            t.Text = txt
        End If
    End If
End Sub

Quand au ToolStripStatusLabel, j'ai eu un plantage sur un accès via un timer (j'ai un 2eme label dans mon StatusStrip qui m'affiche l'heure). Je suppose que, par précaution pour y accéder, il me faudra quand même utiliser UpdateText() dans tous les cas.

Je continue de prospecter sur la cause du non-rafraichissement de mon Label qui est incompréhensible…
Et je garde dans un coin cette histoire de combiner des méthodes dans un délégate (cela risque de me servir plus tard)

Merci de ton aide. :)
Nardo26
Messages postés
35
Date d'inscription
samedi 13 janvier 2018
Statut
Membre
Dernière intervention
24 mai 2019
-
Donc la solution que j'ai retenu en tenant compte du StatusStrip est celle-ci :

Private Sub UpdateText(obj As Object, txt As String)
    Dim o As Type = obj.GetType()
    If o Is GetType(Label) Or
       o Is GetType(TextBox) Or
       o Is GetType(cNumBox) Then  ' cNumBox est une classe derivée de TextBox
        Dim t As Control = CType(obj, Control)
        If t.InvokeRequired Then
            Invoke(New SetText(AddressOf ProtectedWriteText), obj, txt)
        Else
            t.Text = txt
        End If
    ElseIf o Is GetType(ToolStripStatusLabel) Then
        Dim t As ToolStripStatusLabel = CType(obj, ToolStripStatusLabel)
        If t.GetCurrentParent.InvokeRequired Then
            Invoke(New SetText(AddressOf ProtectedWriteText), obj, txt)
        Else
            t.Text = txt
        End If
    End If
End Sub
Commenter la réponse de Whismeril
Messages postés
35
Date d'inscription
samedi 13 janvier 2018
Statut
Membre
Dernière intervention
24 mai 2019
0
Merci
Problème résolu : une erreur de ma part. Je ne mettait pas à jour le bon label. Donc forcément je ne voyais pas le rafraichissement de celui auquel je m'attendais…

Merci Whismeril de t'être penché sur mon pb. :)
Commenter la réponse de Nardo26