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

VB.NET Discussion :

Passer un Sub à un Sub puis à un EventHandler


Sujet :

VB.NET

Vue hybride

Message précédent Message précédent   Message suivant Message suivant
  1. #1
    Membre confirmé
    Profil pro
    Inscrit en
    Mai 2006
    Messages
    158
    Détails du profil
    Informations personnelles :
    Localisation : Belgique

    Informations forums :
    Inscription : Mai 2006
    Messages : 158
    Par défaut Passer un Sub à un Sub puis à un EventHandler
    Bonjour,

    Je voudrais passer l'adresse d'un Sub à un autre Sub puis, à l'intérieur de ce dernier, le passer finalement à un EventHandler.
    Comment puis-je faire svp?

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    Sub2(AddressOf Sub1)
    Private Sub Sub2(ByRef act As Action)
        Dim handl as EventHandler = New EventHandler(AddressOf act)
    End Sub
    Si je place un 'act()' à l'intérieur du Sub2 je constate qu'il est bien appelé...c'est donc ensuite comment passer l'adresse de de 'act' au EventHandler' qui me pose problème!

    Merci!!

  2. #2
    Membre Expert
    Avatar de Sehnsucht
    Homme Profil pro
    Développeur .NET
    Inscrit en
    Octobre 2008
    Messages
    847
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 42
    Localisation : France, Lot et Garonne (Aquitaine)

    Informations professionnelles :
    Activité : Développeur .NET

    Informations forums :
    Inscription : Octobre 2008
    Messages : 847
    Par défaut
    passer l'argument ByRef ne sert à rien
    New EventHandler(AddressOf act.Invoke) "fonctionnera"
    Ceci est dû au fait qu'en VB.Net on peut appeler un délégué en omettant les parenthèses ; du coup pour le compilo il y a ambiguïté entre act la variable et act le "résultat" de l'appel à act() (même dans le cas d'un Sub qui ne renvoie pas de valeur parce que c'est la même syntaxe pour un Function qui renvoie une valeur lui). Du coup en passant par Invoke l'ambiguïté est levée.

    Par contre du coup t'auras une autre erreur, parce que EventHandler attend un délégué à 2 arguments (de type Object et EventArgs) et tu lui passes un délégué sans argument.

  3. #3
    Membre confirmé
    Profil pro
    Inscrit en
    Mai 2006
    Messages
    158
    Détails du profil
    Informations personnelles :
    Localisation : Belgique

    Informations forums :
    Inscription : Mai 2006
    Messages : 158
    Par défaut
    Sehnsucht,

    Merci pour ta réponse qui en effet fonctionne!
    J'ai crue qu'en simplifiant ma question je pourrais m'en sortir..malheureusement c'est un p'tit peu plus compliqué que cela et même si j'arrive à 'passer' une partie du problème grâce à ta contribution, je suis bloqué un peu plus loin.
    Donc si tu (vous) le permettez je vais te (vous) donner l'entièreté de la question:

    Je suis dans un contexte où l'utilisateur doit faire un appui prolongé sur certaines touches pour accéder à une fonction.
    Voila ce que j'ai pour l'instant et qui fonctionne:
    Détection de quelle touche poussée.
    Si c'est une touche nécessitant un appui prolongé, vérification qu'elle est maintenue (If keyData.IsRepeat) et si elle vient d'être pressée attribution de l'évènement qui sera appelé à la fin du temps de 3sec (pour autant que appuyée jusqu'à ce temps)

    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
    Imports System.Windows.Threading
     
    Class MainWindow
        Dim KeyRepeatTimer As DispatcherTimer = New DispatcherTimer()
        Dim krHandler As EventHandler
        Dim KeyRepeat As Boolean
     
        Private Sub MainWindow_Loaded(ByVal sender As Object, ByVal e As System.Windows.RoutedEventArgs) Handles Me.Loaded
            KeyRepeatTimer.Interval = New TimeSpan(0, 0, 3)
        End Sub
     
        Private Sub MainWindow_KeyUp(ByVal sender As Object, ByVal keyData As System.Windows.Input.KeyEventArgs) Handles Me.KeyUp
            RemoveHandler KeyRepeatTimer.Tick, krHandler
            KeyRepeatTimer.Stop()
            KeyRepeat = False
        End Sub
        Private Sub MainWindow_KeyDown(ByVal sender As Object, ByVal keyData As System.Windows.Input.KeyEventArgs) Handles Me.KeyDown
            If keyData.Key = Key.A Then
                ActionA()
            ElseIf keyData.Key = Key.Z Then
                If keyData.IsRepeat Then
                    If Not KeyRepeat Then
                        KeyRepeat = True
                        krHandler = New EventHandler(AddressOf ActionZ)
                        AddHandler KeyRepeatTimer.Tick, krHandler
                        KeyRepeatTimer.Start()
                    End If
                End If
            ElseIf keyData.Key = Key.E Then
                If keyData.IsRepeat Then
                    If Not KeyRepeat Then
                        KeyRepeat = True
                        krHandler = New EventHandler(AddressOf ActionE)
                        AddHandler KeyRepeatTimer.Tick, krHandler
                        KeyRepeatTimer.Start()
                    End If
                End If
            End If
        End Sub
     
        Private Sub ActionA()
            RemoveHandler KeyRepeatTimer.Tick, krHandler
            KeyRepeatTimer.Stop()
            KeyRepeat = False
            Debug.Print("A pressed")
        End Sub
        Private Sub ActionZ()
            RemoveHandler KeyRepeatTimer.Tick, krHandler
            KeyRepeatTimer.Stop()
            KeyRepeat = False
            Debug.Print("Z pressed")
        End Sub
        Private Sub ActionE()
            RemoveHandler KeyRepeatTimer.Tick, krHandler
            KeyRepeatTimer.Stop()
            KeyRepeat = False
            Debug.Print("E pressed")
        End Sub
    End Class
    Comme on le voit, il y a des répétitions de code pour chaque touche (au total j'en ai 20!!)
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    If Not KeyRepeat Then
         KeyRepeat = True
         krHandler = New EventHandler(AddressOf ActionZ)
         AddHandler KeyRepeatTimer.Tick, krHandler
         KeyRepeatTimer.Start()
    End If
    ..et des répétitions de code dans chaque sub (aussi ;-))
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    RemoveHandler KeyRepeatTimer.Tick, krHandler
    KeyRepeatTimer.Stop()
    KeyRepeat = False
    Ce que je voudrais:
    Envoyer à un Sub commun l'adresse du Sub à appeler au final
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    If keyData.Key = Key.Z Then
                If keyData.IsRepeat Then
                    KeyRepeatEnter(AddressOf ActionZ)
                End If
    End If
    Ce Sub attribuerait l'évènement à appeler à la fin des 3 secondes..pour que 'KeyRepeatQuit' puisse au final lancer le Sub 'ActionZ')
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    Private Sub KeyRepeatEnter(ByVal act As Action)
            If Not KeyRepeat Then
                KeyRepeat = True
                krHandler = New EventHandler(passer l'adresse de act au sub 'KeyRepeatQuit')
                AddHandler KeyRepeatTimer.Tick, krHandler
                KeyRepeatTimer.Start()
            End If
    End Sub
    Private Sub KeyRepeatQuit(ByVal sender As Object, ByVal e As EventArgs, ByVal act As Action)
            RemoveHandler KeyRepeatTimer.Tick, krHandler
            KeyRepeatTimer.Stop()
            KeyRepeat = False
            act()
    End Sub
    J'espère que mon explication est clair..j'avoue que je ne vois pas trop comment mieux expliquer

  4. #4
    Membre Expert
    Avatar de Sehnsucht
    Homme Profil pro
    Développeur .NET
    Inscrit en
    Octobre 2008
    Messages
    847
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 42
    Localisation : France, Lot et Garonne (Aquitaine)

    Informations professionnelles :
    Activité : Développeur .NET

    Informations forums :
    Inscription : Octobre 2008
    Messages : 847
    Par défaut
    Comme je n'était pas sûr d'avoir tout compris, et qu'au final je me suis éloigné de la demande pour accomplir ce qu'il me semble être équivalent mais différemment
    je poste le code que j'ai réalisé ; qui reprend pour l'essentiel ton code (j'ai juste rajouté un TextBlock [pour nom Output] pour simplifier la visualisation des résultats).

    Je donne donc le truc tel quel en espérant qu'il colle à ce qui était souhaité

    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
    Imports System.Windows.Threading
     
    Class MainWindow
        Private keyRepeat As Boolean
        Private keyRepeatHandler As EventHandler
        Private keyRepeatTimer As New DispatcherTimer With {.Interval = TimeSpan.FromSeconds(3)}
     
        Private ReadOnly Actions As IReadOnlyDictionary(Of Key, Action) =
            New Dictionary(Of Key, Action) From {
                {Key.A, AddressOf PerformAKey},
                {Key.E, AddressOf PerformEKey},
                {Key.S, Sub() Output.Text &= "S pressed" & Environment.NewLine},
                {Key.Z, AddressOf PerformZKey}
            }
     
        Private Sub MainWindow_KeyDown(sender As Object, e As KeyEventArgs) Handles Me.KeyDown
            Select Case e.Key
                Case Key.A, Key.B
                    Dim performKey As Action = Nothing
                    If Actions.TryGetValue(e.Key, performKey) Then
                        performKey()
                    End If
                Case Key.E, Key.S, Key.Z, Key.Q
                    If Not keyRepeat AndAlso e.IsRepeat Then
                        keyRepeat = True
                        keyRepeatHandler = Sub()
                                               Dim performKey As Action = Nothing
                                               If Actions.TryGetValue(e.Key, performKey) Then
                                                   performKey()
                                                   MainWindow_KeyUp()
                                               End If
                                           End Sub
                        AddHandler keyRepeatTimer.Tick, keyRepeatHandler
                        keyRepeatTimer.Start()
                    End If
            End Select
        End Sub
     
        Private Sub MainWindow_KeyUp() Handles Me.KeyUp
            keyRepeat = False
            keyRepeatTimer.Stop()
            RemoveHandler keyRepeatTimer.Tick, keyRepeatHandler
        End Sub
     
        Private Sub PerformAKey()
            Output.Text &= "A pressed" & Environment.NewLine
        End Sub
     
        Private Sub PerformEKey()
            Output.Text &= "E pressed" & Environment.NewLine
        End Sub
     
        Private Sub PerformZKey()
            Output.Text &= "Z pressed" & Environment.NewLine
        End Sub
    End Class
    Au niveau des changements, j'ai viré l'évènement Loaded et directement initialisé l'Interval ;
    j'ai rajouté un Dictionnaire (en lecture seule) qui fait le lien entre une touche et l'action (la Sub) associée (en montrant au passage qu'on peut directement y mettre une lambda (la Sub associée à Key.S), pour peu qu'elle soit courte (sinon ça va devenir illisible).
    Après tout se joue au niveau du KeyDown ; j'utilise un Select Case (beaucoup plus pratique et clair qu'un enchainement de If),
    j'y mets d'abord les cas "sans repeat nécessaire" je cherche dans le dico l'action associée ; si je l'ai trouvée, je l'exécute sinon (les cas Key.B et Key.Q que j'ai rajoutés*) on ne fait rien
    puis les cas "avec repeat" où je stocke dans le Handler une lambda qui fera exactement la même chose que dans le cas classique quand elle sera appelée (plus le "nettoyage" en appelant KeyUp)

    (* normalement on ne mettra pas dans le Select Case les cas inutilisés, mais je montre que si ça arrivait [oubli ou autre] le code est suffisamment robuste pour en tenir compte plutôt que planter [parce que performKey serait Nothing])

    Ça peut paraitre contradictoire que ce coup-ci on puisse assigner une Sub sans argument (la lambda) à un Eventhandler qui doit normalement en prendre 2 (Objet et EventArgs) ; en fait il s'agit d'une facilité de syntaxe appelée Relaxed Delegate (dont je profite également en omettant les arguments de KeyUp ) et elle est possible uniquement lors d'une assignation directe (bon c'est un peu plus compliqué que ça mais le lien donné l'expliquera plus en détail); si j'assignais ma Sub à une variable avant puis la donnait à keyRepeatHandler ça ne fonctionnerait plus (en fait on retomberait sur ton problème initial)

    En espérant que je soit pas trop à côté de la plaque

  5. #5
    Membre confirmé
    Profil pro
    Inscrit en
    Mai 2006
    Messages
    158
    Détails du profil
    Informations personnelles :
    Localisation : Belgique

    Informations forums :
    Inscription : Mai 2006
    Messages : 158
    Par défaut
    Sehnsucht,

    Je te remercie énormément pour ta réponse et pour le temps que tu as pris à t'y consacrer!! Vraiment!!
    Cela correspond exactement à ma demande et je vais étudier ça de près ;-)

    Entre-temps, j'avais essayè de continuer à trouver des infos et était tombé sur ceci, qui permet de passer un argument au event handler:
    http://www.codingvision.net/tips-and...with-arguments
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    private void Form1_Load (object sender, EventArgs e)
    {
              string s = "Hello!";
              button.Click += delegate(object sender2, EventArgs e2)
              {
                     show_msg(sender2, e2, s);
              };
    }
     
    private void show_msg(object sender, EventArgs e, string s)
    {
              MessageBox.Show(s);
    }
    Ce qui semblerait donner en VB.net ceci:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    Private Sub Form1_Load(ByVal sender As Object, ByVal e As EventArgs)
            Dim str As String = "Hello!"
            AddHandler Me.button1.Click,  New EventHandler(Sub(sender2 As Object, e2 As EventArgs) Me.show_msg(sender2, e2, str))
    End Sub
    Private Sub show_msg(ByVal sender As Object, ByVal e As EventArgs, ByVal s As String)
            MessageBox.Show(s)
    End Sub
    ...est-ce que cette solution te semble également bonne?

  6. #6
    Membre Expert
    Avatar de Sehnsucht
    Homme Profil pro
    Développeur .NET
    Inscrit en
    Octobre 2008
    Messages
    847
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 42
    Localisation : France, Lot et Garonne (Aquitaine)

    Informations professionnelles :
    Activité : Développeur .NET

    Informations forums :
    Inscription : Octobre 2008
    Messages : 847
    Par défaut
    Dans le principe global ça rejoint ce que j'ai fait
    Queques détails inutiles par contre quand tu as ceci : AddHandler Me.button1.Click, New EventHandler(Sub(sender2 As Object, e2 As EventArgs) Me.show_msg(sender2, e2, str)) avec sender2 et e2 inutilisé dans show_msg tu peux utiliser la Relaxed Delegate Syntax pour déjà simplifier soit [c]AddHandler Me.button1.Click, New EventHandler(Sub() Me.show_msg(str))[c] [Attention cela sous-entend que show_msg ne prend désormais plus qu'un seul argument donc sa signature sera Private Sub show_msg(ByVal s As String) (voir code plus loin)]
    Et on peut même aller plus loin en supprimant l'appel à New EventHandler et on lui donnant directement la lambda (le compilo fera sa tambouille derrière pour typer ça comme EventHandler )
    Donc au final tu aurais ça :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    Private Sub Form1_Load(ByVal sender As Object, ByVal e As EventArgs)
            Dim str As String = "Hello!"
            AddHandler Me.button1.Click,  Sub() Me.show_msg(str))
    End Sub
    Private Sub show_msg(ByVal s As String)
            MessageBox.Show(s)
    End Sub
    Ce qui se rapproche beaucoup de ce que j'avais mis (au nombre d'étapes intermédiaires près)
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    Private keyRepeatHandler As EventHandler
    ' ...
    keyRepeatHandler = Sub() ' ...
    AddHandler keyRepeatTimer.Tick, keyRepeatHandler
    Et la raison de ces étapes c'est de pouvoir à un moment faire RemoveHandler keyRepeatTimer.Tick, keyRepeatHandler parce que si tu ne stockes pas ta lambda tu n'auras rien à mettre en "second argument" de RemoveHandler

Discussions similaires

  1. [XL-2010] Portée des variables: variables locales ou public; sub, private sub et Option Explicit
    Par RicardoBxl dans le forum Macros et VBA Excel
    Réponses: 3
    Dernier message: 04/02/2011, 20h34
  2. reverse engineering, passer du MPD au MLD puis au MCD
    Par tibotibo69 dans le forum PowerAMC
    Réponses: 5
    Dernier message: 22/02/2008, 11h09
  3. Passer un argument a Sub UserForm_Initialize(position As Integer)
    Par k-eisti dans le forum Macros et VBA Excel
    Réponses: 5
    Dernier message: 06/06/2007, 12h47
  4. Sub appelle Sub recup valeur
    Par Hoareau dans le forum Macros et VBA Excel
    Réponses: 2
    Dernier message: 22/05/2007, 10h01
  5. [VB6] Possibilité de passer un nom de sub en parametre
    Par Vesta dans le forum VB 6 et antérieur
    Réponses: 6
    Dernier message: 31/05/2006, 10h17

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