4
votes

Plusieurs événements Worksheet_Change dans le code vba

" J'ai deux événements.

J'ai essayé de copier les deux dans le même Worksheet_Change, mais Excel devient berzerk et plante.

Range 1:

Private Sub Worksheet_Change(ByVal Target As Range)
    Dim acr As Range
    Set acr = Range("C9:G9")
    For Each Cell In acr
    Cell.Value = LCase(Cell)
    Next Cell
End Sub


3 commentaires

Mettez simplement le code dans un seul événement.


Les réponses fournies sont toutes bonnes sauf qu'elles n'ont pas de gestion des erreurs. Donc, si votre code échoue entre les instructions "Application.EnableEvents = False" et "Application.EnableEvents = True", alors vous venez de quitter Excel est un mauvais état où aucun événement ne se déclenchera. Chaque fois que vous utilisez "Application.EnableEvents", "Application.ScreenUpdating", etc. utilisez toujours la gestion des erreurs pour vous assurer qu'en cas d'échec de votre code, ces types de paramètres sont réinitialisés à ce qu'ils doivent être. Je suis un développeur .NET / C # (VBA = Megablocks, C # = Lego Technic) et la gestion des erreurs est un must.


@FrankBall Bon, en a ajouté un dans ma réponse.


4 Réponses :


1
votes

Comme ça, vous pouvez faire les deux choses dans le même événement

Vous devez ajouter Application.EnableEvents = False au début pour éviter la condition de concurrence.

Private Sub Worksheet_Change(ByVal Target As Range)
 Application.EnableEvents = False

    Dim ccr As Range, acr as Range

    Set ccr = Range("C6")
    For Each Cell In ccr
      Cell.Value = LCase(Cell)
    Next Cell

    Set acr = Range("C9:G9")
    For Each Cell In acr
      Cell.Value = LCase(Cell)
    Next Cell
 Application.EnableEvents = True

End Sub


1 commentaires

Je vous remercie! Cela le corrige



6
votes

Le problème principal est que la modification d'une valeur de cellule Cell.Value déclenchera immédiatement un autre Worksheet_Change . Vous devez Application.EnableEvents = False pour éviter cela.

Je recommande également de travailler avec Intersect pour que le code ne fonctionne que sur les cellules qui sont réellement modifiées.

Private Sub Worksheet_Change(ByVal Target As Range)
    Dim AffectedRange As Range
    Set AffectedRange = Intersect(Target, Target.Parent.Range("C6, C9:G9"))

    Application.EnableEvents = False 'pervent triggering another change event
    On Error GoTo ERR_HANDLING

    If Not AffectedRange Is Nothing Then
        Dim Cel As Range
        For Each Cel In AffectedRange.Cells
            Cel.Value = LCase$(Cel.Value)
        Next Cel
    End If

    On Error GoTo 0

    'no Exit Sub here!
ERR_HANDLING:
    Application.EnableEvents = True 

    If Err.Number <> 0 Then
        Err.Raise Err.Number, Err.Source, Err.Description, Err.HelpFile, Err.HelpContext
    End If
End Sub

En plus du commentaire de @Frank Ball, y compris la gestion des erreurs:

Option Explicit

Private Sub Worksheet_Change(ByVal Target As Range)
    Dim AffectedRange As Range
    Set AffectedRange = Intersect(Target, Target.Parent.Range("C6, C9:G9"))

    If Not AffectedRange Is Nothing Then
        Application.EnableEvents = False 'pervent triggering another change event

        Dim Cel As Range
        For Each Cel In AffectedRange.Cells
            Cel.Value = LCase$(Cel.Value)
        Next Cel

        Application.EnableEvents = True 'don't forget to re-enable events in the end
    End If
End Sub


0 commentaires

2
votes

Les deux événements Worksheet_Change sont assez identiques, ils sont une boucle autour d'une plage, renvoyant LCase () . Ainsi, c'est une bonne idée de créer un Sub séparé pour lui comme ceci:

Private Sub Worksheet_Change(ByVal Target As Range)

    If Not Intersect(Target, Range("C6"), Range("C9:G9")) Is Nothing Then Exit Sub
    Application.EnableEvents = False
    FixRangeLCase Range("C6")
    FixRangeLCase Range("C9:G9")
    Application.EnableEvents = True

End Sub

Ensuite, faites-lui référence à l'événement Worksheet_Change. Dans la mesure où l'événement Worksheet_Change est assez "cher", en cours d'exécution toujours, il est judicieux de ne l'exécuter que lorsqu'une cellule cible spécifique est modifiée et sinon quittez la procédure - If Intersect ( Target, Range ("C6"), Range ("C9: G9")) Is Nothing Then Exit Sub

Le Application.EnableEvents = False est nécessaire pour désactiver les événements. À la fin, il est remis à Vrai .

Sub FixRangeLCase(rangeToFix As Range)        
    Dim myCell As Range
    For Each myCell In rangeToFix
        myCell.Value2 = LCase(myCell.Value2)
    Next myCell    
End Sub


0 commentaires

1
votes

Vous pouvez également utiliser:

Option Explicit

Private Sub Worksheet_Change(ByVal Target As Range)

    Dim rng As Range, cell As Range

    Application.EnableEvents = False

        If Not Intersect(Target, Range("C6")) Is Nothing Or Not Intersect(Target, Range("C9:G9")) Is Nothing Then
            Set rng = Range("C9:G9", "C6")

            For Each cell In rng
                cell.Value = LCase(cell.Value)
            Next
        End If

    Application.EnableEvents = True

End Sub


9 commentaires

Ce n'est pas correct car cela ne change que la cellule cible


Cette Target.Value = LCase (Target.Value) échoue si Target est une plage de plusieurs cellules. LCase ne peut pas gérer un tableau de valeurs Target.Value .


@newguy ce que vous mentionnez n'est pas un problème ici car seules les cellules Target ont réellement changé, les autres sont donc déjà en minuscules (et n'ont pas besoin d'être converties à nouveau). • Le problème ici serait qu'il fonctionnerait sur toutes les cellules modifiées, même en dehors de la plage donnée, non seulement sur les cellules de C6, C9: G9 (mais en fait, il échoue car de la raison que j'ai mentionnée ci-dessus).


@Error Lors de l'utilisation de l'événement Worksheet_Change , gardez toujours à l'esprit que Target peut être une Range collée non seulement une seule cellule.


@ Pᴇʜ je crois avoir compris le point et j'ai modifié la réponse. Est-ce correct?


Réponse @newguy modifiée. toutes autres suggestions ou corrections sont les bienvenues!


Mieux vaut ne pas planter parce que Target est une plage. Mais notez que si, par exemple, seule la cellule C10 est éditée, le rng complet est converti en minuscules alors que seul C10 a changé et il suffirait de convertir cette cellule uniquement. Pour éviter cela, consultez ma réponse où j'ai utilisé AffectedRange = Intersect (Target, Target.Parent.Range ("C6, C9: G9")) pour obtenir uniquement les cellules modifiées de la plage souhaitée. Donc tu finirais avec ce que j'ai répondu.


À quoi sert .parent?


Si l'événement est déclenché alors qu'une autre feuille de calcul a le focus, je ne suis pas sûr sur quelle feuille de calcul Range serait par défaut Target.Parent donne la feuille de calcul où la plage Target se trouve. Cela garantit que Range ("C6, C9: G9") pointe vers la même feuille de calcul que celle vers laquelle pointe Target . Juste pour être sauvé et ne pas laisser Excel décider de quelle feuille je veux dire.