Bonjour,

Suite à plusieurs discussions sur la manipulation du statut de l'activation des évènements, calculs, affichage, protection des feuilles ect ... qui est source de conflit,
je me suis penché sur le problème.

Historiquement, des problèmes peuvent apparaitre lors de l'appel de sous-fonction qui manipulent ces status, dont la fonction appelante n'a pas conscience (et elle n'a pas à s'en préoccuper).
Par exemple:
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
Public Sub Foo()
    Application.ScreenUpdating = False
    '// code qui manipule l'affichage
 
    Bar
    '// code qui manipule l'affichage
 
    Application.ScreenUpdating = True
End Sub
 
Private Sub Bar()
    Application.ScreenUpdating = False
 
    '// code qui manipule l'affichage
 
    Application.ScreenUpdating = True
End Sub
La fonction Bar rétablit l'affichage, en conséquence, les instructions suivantes mettant à jour l'affichage provoquent d'horribles clignotements à l'écran, ce qui dégrade l'expérience utilisateur.

Une première solution peut consister à ne désactiver l'affichage que dans la fonction de plus haut niveau:
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
Public Sub Foo()
    Application.ScreenUpdating = False
    '// code qui manipule l'affichage
 
    Bar
    '// code qui manipule l'affichage
 
    Application.ScreenUpdating = True
End Sub
 
Private Sub Bar()
    '// code qui manipule l'affichage
End Sub
Mais, cela implique que la fonction appelante aie une idée précise du comportement des fonctions appelées,
et que les fonctions appelées assument que la fonction appelante a pris ses dispositions.
Ce qui en contradiction avec la Loi de Demeter.

Une seconde solution peut consister à mémoriser l'état de l'affichage en début de fonction, et le restituer en fin de fonction:
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
Public Sub Foo()
    Application.ScreenUpdating = False
    '// code qui manipule l'affichage
 
    Bar
    '// code qui manipule l'affichage
 
    Application.ScreenUpdating = True
End Sub
 
Private Sub Bar()
    Dim ScreenUpdating As Boolean
    ScreenUpdating = Application.ScreenUpdating
 
    Application.ScreenUpdating = False
    '// code qui manipule l'affichage
 
    Application.ScreenUpdating = ScreenUpdating
End Sub
C'est correcte, mais c'est une approche défensive de la programmation, d'une part lourdingue, d'autre part qui détourne le développeur de sa tâche première: Produire une fonction avec le comportement escompté.

N'y a t'il pas un moyen d'automatiser la sauvegarde / restauration de ces status ?
Je pense que oui, via une classe au final fort simple, qu'il suffira d'instancier (et oublier sa présence):
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
'// Class ApplicationStateHolder
Option Explicit
 
    '// référence vers l'application
Private mApp As Excel.Application
 
    '// Status que l'on désire mémoriser
Private mEnableEvents As Boolean
Private mScreenUpdating As Boolean
Private mCalculation As Boolean
 
    '// Pseudo-constructeur
    '// Memorisation des status
Friend Sub Create(ByRef App As Excel.Application)
    Set mApp = App
    mEnableEvents = mApp.EnableEvents
    mScreenUpdating = mApp.ScreenUpdating
    mCalculation = mApp.Calculation
End Sub
 
    '// Destructeur
    '// Restitution des status
Private Sub Class_Terminate()
    mApp.enableevent = mEnableEvents
    mApp.sceenupdating = mScreenUpdating
End Sub
Cette classe nécéssite un module Factory pour être instanciée:
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
'// Module: Factory
Option Explicit
 
Public Function CreateApplicationStateHolder(ByRef App As Excel.Application) As ApplicationStateHolder
    Dim State As ApplicationStateHolder
    Set State = New ApplicationStateHolder
 
    State.Create App
    Set CreateApplicationStateHolder = State
End Function
Demo:
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
Public Sub Foo()
    Dim State As ApplicationStateHolder
    Set State = Factory.CreateApplicationStateHolder(Application)
 
    Application.ScreenUpdating = False
    '// code qui manipule l'affichage
 
    Bar
    '// code qui manipule l'affichage
End Sub
 
Private Sub Bar()
    Dim State As ApplicationStateHolder
    Set State = Factory.CreateApplicationStateHolder(Application)
 
    Application.ScreenUpdating = False
    '// code qui manipule l'affichage
End Sub
Lorsqu'une fonction se termine, l'instance State est détruite, ce qui entraine la restauration des status sauvegardés en début de fonction.
Les fonctions Foo et Bar peuvent maintenant manipuler l'affichage pour leurs besoins propre, sans avoir à se soucier de ce qu'on fait leurs prédécesseurs, ni ce que feront leurs successeurs,
elles sont plus facile à lire et à comprendre.
Des classes reposant sur le même principe, concernant les feuilles (protection) ou autre objet sont envisageable.