MS ACCESS et persistance (2)

Après avoir parlé, dans un billet précédent, de la persistance dans un fichier ini, nous allons poursuivre sur la technique de la base de registre.

Ecrire dans la base de registre apporte, contrairement au fichier ini, un semblant de sécurité. En effet il est moins aisé de retrouver une valeur parmi les milliers existantes dans la base de registre que de consulter un fichier ini. La destination n’est donc pas la même.

Quels sont les moyens à notre disposition ?

Grâce à la bibliothèque Windows Script Host que l’on retrouve dans les références sous le nom de Microsoft Scripting Runtime on peut invoquer différentes commandes de gestion de la base de registre.

Déclaration

La déclaration de cette bibliothèque peut se faire en early ou late binding (cf http://blogaccess.free.fr/?p=127). C’est la deuxième méthode que nous allons utiliser ici.

Le modèle est toujours le même, déclaration, instanciation, utilisation, libération.

Dim wsh As Object

Set wsh = CreateObject("WScript.Shell")

... instructions

Set wsh = Nothing

Lecture

Pour la lecture on utilise l’instruction RegRead en précisant le chemin de la clef à lire.

Chemin = wsh.RegRead("HKEY_CURRENT_USER\Software\Microsoft\Office\" & _
      Val(SysCmd(acSysCmdAccessVer)) & ".0\Access\Security\" & _
      "Trusted Locations\mon application\Path"

En retour la fonction renvoi le contenu de la clef. Dans cet exemple on interroge la base de registre pour connaître l’emplacement approuvé pour « mon application ».

Ecriture

De la même manière on peut écrire une valeur ou créer une clef à l’aide de l’instruction RegWrite.

wsh.RegWrite "HKEY_CURRENT_USER\Software\Microsoft\Office\" & _
      Val(SysCmd(acSysCmdAccessVer)) & ".0\Access\Security\" & _
      "Trusted Locations\" & vNameProduit & "\Path", _
      "c:\application access\mon application"

Le premier paramètre est le chemin de la clef de registre et le second la valeur à écrire.

Si la clef (le chemin) n’existe pas, elle est créée.

Conclusion

Comme nous l’avons vu rien de sorcier dans l’utilisation de ces instructions. Par contre il est fortement conseillé d’avoir les bases de connaissances minimales de ce qu’est la base de registre. En effet, si la manipulation d’un fichier ini (cf http://blogaccess.free.fr/?p=122) pour gérer de la persistance n’est pas critique, manipuler la base de registre l’est sans aucun doute. Prenez des précautions avant la manipuler.

MS ACCESS : Classe et gain de temps

Le concept de classe en VBA a certes des lacunes mais il ne doit pas être négligés pour autant.

En effet par leur utilisation ont peut gagner un temps précieux. Voici un petit exemple fonctionnel d’une classe qui gère les évènements DblClick et NotInList d’une zone de liste déroulante. Le DblClick permet d’ouvrir un formulaire de saisie/édition avec l’item de la liste, tandis que NotInList permet de saisir l’item.

Créer le module de classe

Dans VBE faites Insertion/Module de classe. En bas à gauche dans la fenêtre des propriétés vous devez renseigner le nom de l’instance. cComboBox

classe1

Instancing permet de définir si la classe n’est visible que dans le fichier courant ou si elle est visible depuis l’extérieur. Laissez 1 – Private.

Déclaration

Sur la ligne qui suit les options, nous allons déclarer les variables nécessaires. Tout d’abord la plus importante, celle qui contiendra l’objet Combobox.

Private WithEvents LmCombo          As Access.ComboBox

WithEvents indique que cet objet pourra lever des évènements.

Access.ComboBox est le type Liste déroulante.

Ensuite nous aurons besoin de variables de type String pour stocker différents éléments nécessaires à l’ouverture du formulaire.

'nom du formulaire qui est appelé lors du notinlist
Private strFormNameLie     As String       
   'nom du champ id de la table recevant la nouvelle valeur
Private strIdFieldName     As String       
   'nom du champ recevant la valeur saisie dans la combo 
Private strControlName     As String       
   'nom de la table recevant la nouvelle valeur
Private strTableFormLie    As String

En suivant nous écrivons deux méthodes habituelles dans les classes.

Private Sub Class_Initialize()
' Initialise

End Sub

La Class_Initialize est exécutée au démarrage de la classe. Elle ne contient rien dans cet exemple.

Private Sub Class_Terminate()
' libère les variables
    On Error Resume Next
    Set LmCombo = Nothing
End Sub

Tandis que Class_Terminate est exécuté lorsqu’on libère la classe, qu’on la décharge. Celle-ci est importante car c’est là qu’on décharge également toutes nos variables.

Pour charger la combo depuis le formulaire il faut utiliser un Setteur. Le voici !

Public Property Set objComboBox(objCombo As Access.ComboBox)
    Set LmCombo = objCombo
    LmCombo.OnDblClick = "[Event Procedure]"
    LmCombo.OnNotInList = "[Event Procedure]"
End Property

On voit qu’on lui passe un objet combobox et qu’il est chargé dans la variable définie précédemment. Les 2 lignes suivantes permettent d’activer l’écoute des évènements dont nous avons besoin : DblClick et NotInList.

Les autres variables ou propriétés de la classe doivent également être valorisées toujours à l’aide de Setteurs.

Public Property Let frmNameLie(strNomFormulaireLie As String)
    strFormNameLie = strNomFormulaireLie
End Property
Public Property Let idFieldName(strNomIdFieldFormulaireLie As String)
    strIdFieldName = strNomIdFieldFormulaireLie
End Property
Public Property Let controlNameLie(strNomControleFormulaireLie As String)
    strControlName = strNomControleFormulaireLie
End Property
Public Property Let tableNameLie(strNomTableFormulaireLie As String)
    strTableFormLie = strNomTableFormulaireLie
End Property

Rien de bien particulier à part qu’on utilise un Let au lieu du Set réservé aux objets.

Nos variables sont devenus des propriétés de la classe.

Méthode privée

Une méthode privée permettra d’attendre la fermeture du formulaire de saisie avant de poursuivre l’exécution du code. Nous d’obtiendrons un déroulement synchrone du processus.

Private Sub pAttendreFermeture(vlFrmRprt As Object)
      '-----------------------------------------------------------
      ' Procedure   : pAttendreFermeture
      ' Author      : Fabrice CONSTANS
      ' Date        : 20/10/2011
      ' Description : Attend la fermeture de l'objet pour rendre la main
      ' Paramètres  : vlFr est l'objet à controler
      '-----------------------------------------------------------
        On Error GoTo pgAttenteFermeture_Error
        Do
            DoEvents
        Loop While vlFrmRprt.Visible

pgAttenteFermeture_Error:
        On Error GoTo 0
        Exit Sub
End Sub

Les évènements

Les évènements vont être gérés à l’aide de procédure qui deviendront de fait des méthodes de la classe. La différence c’est qu’à aucun moment nous ne devront les appeler pour qu’elles fonctionnent. Ceci grâce au WithEvents de la déclaration et à l’activation des écouteurs.

Le Notinlist

On remarque que la signature de la procédure est strictement identique à celle qui pourrait être générée dans un formulaire. Je vous conseille d’ailleurs d’utiliser des signature générée et de les copier dans la classe.

Public Sub LmCombo_NotInList(NewData As String, Response As Integer)
'--------------------------------------------------------------------
' Procedure : LmContact_NotInList
' Author    : Fabrice CONSTANS (MVP)
' Date      : 21/01/2016
' Purpose   : Nouveau contact, on appelle le formulaire de saisie.
'------------------------------------------------------------------
'
Dim strNouveauNumero As String

On Error GoTo Errsub

    If vbYes = MsgBox("Cette valeur n'existe pas. " & _
"Souhaitez-vous la créer ?", vbInformation + vbYesNo, _
                                      "classe ccombo") Then
      'ouverture du formulaire en mode Ajout  
      DoCmd.OpenForm strFormNameLie, acNormal, , , acFormAdd
      'on gèle le contrôle reception de la valeur saisie
      Forms(strFormNameLie).Controls(strControlName).Enabled = False
      'on ajoute la valeur saisie dans la liste
      Forms(strFormNameLie).Controls(strControlName).Value = NewData
      'traitement synchrone
      pAttendreFermeture Forms(strFormNameLie)

'Vérification que le valeur existe (peut-être que l'utilisateur a
' changé d'avis et a annulé la création
   If dLookUp("nz(" & strIdFieldName & ",-1)" _
, strTableFormLie, strControlName & "=""" & NewData & """") > 0 Then
         'la valeur a été saisi
         Response = acDataErrAdded
      Else
        'la valeur n'a pas été saisi (annulation)  
        Response = acDataErrContinue
        LmCombo.Parent.Undo
     End If
   Else
     'il ne souhaite pas la création
     Response = acDataErrContinue
     LmCombo.Parent.Undo
   End If

Exitsub:
      On Error GoTo 0
      Exit Sub
Errsub:
       msgbox "cComboBox.LmCombo_NotInList", Err, Erl, Err.Description
End Sub

Vous pouvez constater que nous utilisons bien les constantes habituelles pour ce type d’évènement : NewData, acDataErrContinue, acDataErrAdded

Le DblClick

Cet évènement est plus simple que le précédent puisqu’il ne fait qu’ouvrir le formulaire en mode consultation/modification.
A la sortie on pense à rafraichir le contenu de la combo au cas ou l’utilisateur a modifié une valeur.
Il va de soit que pour cette dernière le traitement doit également être synchrone.

Public Sub LmCombo_DblClick(Cancel As Integer)
'--------------------------------------------------------------------
' Procedure : btnEditContact_Click
' Author    : Fabrice CONSTANS (MVP)
' Date      : 16/02/2016
' Purpose   : edit et raffraichit la liste au retour
'--------------------------------------------------------------------
'
Dim Id As Long

On Error GoTo Errsub

    If IsNull(LmCombo.Column(0)) Then
        MsgBox "Pour créer un item vous devez entrer sa valeur.", _
               vbInformation + vbOKOnly, "classe ccombo"
        Exit Sub
    End If

    Id = LmCombo.Column(0)

    DoCmd.OpenForm strFormNameLie, acNormal, , _
                strIdFieldName & "=" & Id, acFormEdit

    pAttendreFermeture Forms(strFormNameLie)
    LmCombo.Undo
    LmCombo.Requery
    LmCombo.value = Id

 Exitsub:
   On Error GoTo 0
   Exit Sub 

Errsub:
    msgbox ("cComboBox.LmCombo_DblClick", Err, Erl, Err.Description)
End Sub

Voilà la Classe cCombo est prête à être utilisée. Voyons cela dans les faits.

Utiliser la classe dans un formulaire

En premier lieu il faut indiquer au formulaire que nous allons utiliser cette classe. Pour cela nous devons utiliser autant de variables qu’il y a de zone de liste à implémenter. Ces variables doivent être globale, donc déclarer immédiatement après les 2 lignes d’Options du module de classe du formulaire.

Dim cComboClient         As cComboBox

Dans l’événement Sur Ouverture du formulaire on poursuit la déclaration.

Set cComboClient = New cComboBox

cComboClient.controlNameLie = "RaisonSociale"
cComboClient.frmNameLie = "fClient"
cComboClient.idFieldName = "id_Client"
Set cComboClient.objComboBox = Me.Controls("lmClient")
cComboClient.tableNameLie = "tClient"

New permet de créer l’instance de la classe et ainsi pouvoir valoriser les propriétés.

Dans cet exemple la liste déroulante se nomme lmClient. Vous pouvez adapter ce code déclaratif à votre propre cas. Vous ne devez jamais modifier la classe pour y intégrer des noms propres à l’application. Une classe doit rester générique sous peine de ne plus être portable.

Il va de soit qu’à chaque nouvelle liste déroulante à instancier, un code similaire devra être ajouté.

Une fois sauvegardé, vous pouvez utiliser le formulaire.

Les tests de comportement à faire :

  • Entrer une nouvelle valeur.
  • Double cliquer sur la zone de liste.

Classe en VBA, quels avantages ?

Le poids de l’application : On remplace des centaine de lignes par une partie déclarative.

L’uniformité du comportement : Comme on utilise le même code pour tous les contrôles nous sommes sûr que le comportement sera la même.

Maintenance et évolution : Si un bug est constaté ou que l’on souhaite faire évoluer le comportement, une seule intervention est nécessaire. Le recopie de code ne sont plus nécessaires avec leur lots d’erreurs potentielles.

Rapidité de conception : Une simple déclaration permet d’exploiter la classe.

Le revers de la médaille : En cas d’erreur non traitée, il peut se produire un déchargement de classes. Il faudra alors relancer les déclarations.

La mise au point peut s’avérer plus complexe qu’avec un code standard, c’est pour cela qu’il est conseillé lorsqu’on débute, de concevoir les traitements dans un formulaire pour ensuite le transformer en classe.

Conclusion

J’espère que ce tuto vous aura plu et qu’il vous donnera des idées d’implémentations pour vos développements.