DAO - Opérations générales (Sauf les RecordSets)

J'ai établi le chapitre DAO sous Access 95 et Access 97

Version de Jet

les tableaux suivants identifient les composants et les versions de jet utilisées dans les applications.

application version de jet
microsoft access 1.0 1.0
microsoft access 1.1 1.1
microsoft access 2.0 2.0
microsoft access 2.0 2.5 avec le service pack de microsoft access
microsoft access 95 3.0
microsoft access 97 3.5

le tableau suivant illustre comment les fichiers base de données peuvent être utilisés suivant les versions de microsoft jet.

version de jet
1.0 1.1 2.0 2.5 3.0 3.5
version mdb 1.0 o o o o o o
1.1 n o o o o o
2.0 n n o o o o
2.5 n n n o o o
3.0 n n n n o o
3.5 n n n n n o

"o" indique que le fichier base de données peut être utilisé sans conversion.

"n" indique que le fichier base de données ne peut être ni utilisé ni converti.

Le type générique Object

Toutes les déclarations de variables (TableDef, Relations, etc). peuvent sans autre être remplacées par le type générique Object. J'ai testé, ça marche. C'est comme le type variant pour les variables non object.

Création d'objets : Liste exhaustive

La collection

Peut créer un

avec la méthode

DBEngine WorkSpace CreateWorkSpace
WorkSpaces(x) Database CreateDataBase
  Group CreateGroup
  User CreateUser
TableDefs(x) Field CreateField
  Index CreateIndex
  Property CreateProperty
Groups(x) User CreateUser
Users(x) Group CreateGroup
DataBases(x) Property CreateProperty
  QueryDef CreateQueryDef
  Relation CreateRelation
  TableDef CreateTableDef
  Pas de RecordSet  
TableDefs(x) Field CreateField
  Index CreateIndex
  Property CreateProperty
QueryDef Ne peux rien créer du tout  
RecordSet Ne peux rien créer du tout  
Relations(x) Field CreateField
Fields(x) Property CreateProperty
Indexes(x) Field CreateField
  Property CreateProperty
Container Ne peux rien créer du tout  
Parameter Ne peux rien créer du tout  
Document Ne peux rien créer du tout  

Ajout d'un objet à une collection

La méthode Append permet d'ajouter un objet créé avec CreateXXX (voir "Création d'objets")

Voici la liste des collections qui peuvent admettre des nouveaux membres

Properties Oui
Errors non
Workspaces oui
Groups oui
Users oui
DataBases non
TableDefs oui
QueryDefs oui
RecordSets Non
Relations oui
Fields oui
Indexes oui
Containers non
Parameters non
Documents non

Refresh

Dans certains cas particuliers, notamment lors de l'utilisation simultanée par plusieurs utilisateurs, ou lors d'instructions SQL qui détruisent ou créent des tables, les collections DAO ne sont pas forcément exactement à jour. Pour forcer la mise à jour d'une collection, on applique la méthode Refresh, comme ceci :

dbengine.workspaces(0).databases(0).tabledefs.Refresh

pour rafraîchir la collection TableDefs dans cet exemple.

For Each et count

Afin de pouvoir aisément définir le nombre des objets d'une collection, on peut utiliser indifféremment l'un ou l'autre de ces codes, qui énumère le nom de chaque table, l'une après l'autre, dans un MsgBox :

Avec For ... Each (Largement recommandé pour être sûr de son coup)

Dim MaTable As TableDef
For Each MaTable In DBEngine.Workspaces(0).Databases(0).TableDefs
MsgBox MaTable.Name
Next

Avec la propriété Count de la collection

Attention : Count renvoie le nombre exact d'objets dans la collection, mais ils sont indexés a partir de 0. Ainsi, s'il existe 3 tables : Count renvoie 3, mais indexés 0,1,2

For X = 0 To DBEngine.Workspaces(0).Databases(0).TableDefs.Count - 1
MsgBox DBEngine.Workspaces(0).Databases(0).TableDefs(X).Name
Next

Affichage de tous les formulaires ouverts

Dim FormDyn As Form
MsgBox Forms.Count
For Each FormDyn In Forms
MsgBox FormDyn.Name
Next

Type Générique Object

Le type object peut représenter n'importe quel genre d'objet.

Dim xxx As Object

est similaire à

Dim xxx As Property
Set xxx = DBEngine.Workspaces(0).Databases(0).TableDefs("T_Client").Fields(0).Properties(0)
MsgBox xxx.Name

Numérotation des collections

Exemple avec Workspaces

Chaque collection est numérotée à partir de 0. AInsi, le premier objet de la collection Workspace s'appelle

DBEngine.Workspaces(0)

On peut donc demander le nom de ce premier objet :

MsgBox DBEngine.Workspaces(0).Name

Il va nous renvoyer #Default Workspace#. Dès lors, au lieu de demander le premier objet de la liste des Workspaces, on peut immédiatement le nommer :

MsgBox DBEngine.Workspaces("#Default Workspace#").Name

Et il va nous renvoyer exactement la même information.

Property et Properties

Voici l'exemple du changement de la valeur par défaut (DefaultValue) du champ Prenom de la table T_Client de la base de données courante en "XXX" :

DBEngine.Workspaces(0).Databases(0).TableDefs("T_Client").Fields("Prenom").Properties("DefaultValue").Value = "XXX"

DBEngine : Méthodes et propriétés

Compactage d'une base de données

DBEngine.CompactDatabase "C:\Biblio.mdb","C:\NouvBib.mdb","",dbEncrypt

Le problème est que pour compacter la abse de données en cours d'utilisation, c'est drôlement plus compliqué, car on ne peut pas utiliser alors cette commande. Le plus simple que j'ai trouvé consiste à envoyer une suite de touches avec SendKeys, qui en fait exécute :

Outils/Utilitaires de bases de données/Compacter une base de dnnées, et cliquer sur Oui

Sub CompacterBaseCourante ()
SendKeys ("%o"), False
SendKeys ("u"), False
SendKeys ("m"), False
SendKeys ("o"), False
End Sub

ATTENTION : On ne peut pas placer ce code, suivi d'autres instruction VB, simplement parce que lors ce l'exécution du compactage, Access Ferme et rouvre la base de données. Ce doit donc être un bouton à part.

Groupes et utilisateurs

Renvoi de l'utilisateur courant

MsgBox DBEngine.Workspaces(0).UserName

est similaire à :

MsgBox CurrentUser

Liste de tous les utilisateurs d'un groupe

Cet exemple liste tous les utilisateurs qui se trouvent dans le groupe "Footballeur" :

Dim CetUser As User
For Each CetUser In DBEngine.Workspaces(0).Groups("Footballeur").Users
MsgBox CetUser.Name
Next

Le résultat, en l'occurrence, renverra Ronaldo et Zidane

Liste de tous les groupes contenant un utilisateur particulier

Ce 2ème exemple renvoie l'ensemble de tous les groupes dans lequel Ronaldo est inscrit :

Dim CeGroupe As Group
For Each CeGroupe In DBEngine.Workspaces(0).Users("Ronaldo").Groups
MsgBox CeGroupe.Name
Next

Cet exemple renverra Footballeur et Users

Liste de tous les utilisateurs de tous les groupes

Cet exemple montre l'ensemble des utilisateurs, avec le ou les groupes auxquels ils appartiennent.

Dim CeGroupe As Group
Dim CetUser As User
' On parcourt les groupes :
For Each CeGroupe In DBEngine.Workspaces(0).Groups
' Et pour chaque groupe, on parcourt ses utilisateurs :
For Each CetUser In CeGroupe.Users
MsgBox CeGroupe.Name & " - " & CetUser.Name
Next
Next

Création dynamique d'objets

Lors de la création d'un objet, il est possible de le laisser tel quel (c'est à dire de ne pas utiliser la méthode Append), auquel cas, lorsqu'on le ferme avec la méthode Close, il est effacé de la mémoire. Si, au contraire, on utilise la méthode Append sur cet objet, il est alors ajouté à la collection, et le seul moyen de s'en débarasser est la méthode Delete.

: Base de données

On ne peut pas utiliser la méthode Delete pour retirer un objet Database de la collection Databases. La seule solution pour ce faire consisterait à utiliser l'instruction Kill pour supprimer le fichier physique du système d'exploitation.

Cet exemple crée une base de données dans atelier : Test.mdb. Si la base de données existe déjà, une erreur est générée. Les deux paramètres dbLangGeneral et dbEncrypt sont indispensables

Dim NouvelleBD As DATABASE
Set NouvelleBD = DBEngine.Workspaces(0).
CreateDatabase("c:\atelier\Test", dbLangGeneral, dbEncrypt)
NouvelleBD.
Close

Traitement des tables

Création dynamique d'une table

Dans cet exemple, nous alons créer la table T_Dyn. ATTENTION : Il n'est pas possible de créer une table et de l'ajouter dans la collection TableDefs si elle ne contient pas au moins un champ. C'est pourquoi nous créons un champ ChampBidon, en réel simple (il faut lui donner un type). Nous ajoutons alors d'abord le champ dans la collection Fields, et ensuite, la table dans la collection TableDefs.

Dim NouvelleTable As TableDef
Dim UnChampInutile As Field
Set NouvelleTable = DBEngine.Workspaces(0).Databases(0).CreateTableDef("T_Dyn")
Set UnChampInutile = NouvelleTable.CreateField("ChampBidon", DB_SINGLE)
NouvelleTable.Fields.Append UnChampInutile
MaBase.TableDefs.Append NouvelleTable

Création dynamique d'une table avec SQL

Cet exemple crée la table T_Ami, pourvue de 3 champs : Nom (texte 255), prenom (Texte 30) et Age (Integer)

DoCmd.RunSQL "CREATE TABLE T_Ami (Nom TEXT, Prenom TEXT(30), Age INT);"

Effacement d'une table

DBEngine.Workspaces(0).Databases(0).TableDefs.Delete ("T_xxx")

Traitement des champs et des propriétés

Création dynamique d'un champ dans une table existante

Dans cet exemple, on ajoute le champ Age, de type réel simple, dans la table T_Client

Dim MonChamp As Field
Set MonChamp = CurrentDb.TableDefs("T_Client").CreateField("Age", DB_SINGLE)
CurrentDb.TableDefs("T_Client").Fields.Append MonChamp

Le tableau suivant donne les constantes à utiliser lors de la création de champs dans une table à l'aide de la méthode CreateField

Constante

Longueur

Type

dbBoolean 1 Boolean
dbByte 1 Byte
dbInteger 2 Integer
dbLong 4 Long
dbCurrency 8 Currency
dbSingle 4 Single
dbDouble 8 Double
dbDate 8 Date/heure
dbText 1-255 Texte
dbLongBinary 0 Long Binary (Liaison OLE)
dbMemo 0 Mémo
dbGUID 16 GUID: Création dynamique d'une requête

Création de propriétés

Dans la base de données

Voici la liste des propriétés de la base de données :

Nom de propriété

Exemple

Explication

Name D:\Atelier\ByPass.mdb Chemin et nom de la base
Connect    
Transactions Vrai  
Updatable Vrai  
CollatingOrder 1033  
QueryTimeout 60  
Version 3.0
RecordsAffected 0  
ReplicaID    
DesignMasterID    
Connection: NON-AFFICHABLE  
AccessVersion 07.53  
Build 4122  
StartUpShowDBWindow Vrai Accepte d'afficher la fenêtre de la base de données
StartUpShowStatusBar Vrai Affiche la barre d'état
AllowShortcutMenus Vrai Autorise l'utilisation de menus contextuels
AllowFullMenus Vrai Menus entiers (True), ou réduits (false)
AllowBuiltInToolbars Vrai  
AllowToolbarChanges Vrai Accepte la modification des barres d'outils
AllowBreakIntoCode Vrai Accepte de montrer le code en cas d'erreur
AllowSpecialKeys Vrai Accepte les touches spéciales d'accès (notamment F11 pour la fenêtre de base de données)
StartUpForm F_MenuPrincipal Formulaire de démarrage
AppTitle Gestion d'un cirque Titre de l'application

Les propriétés de base de données suivantes doivent être soit crées par DAO, soit changées manuellement par l'utilisateur dans Outils/Démarrage

AppIcon C:\Atelier\Test.ICO Détermine l'icône de représentation de la base.

SOIT ETRE CREE SOIT PAR DAO SOIT PAR OUTILS/DEMARRAGE

StartupShortcutMenuBar NomMenuContextuelMacro Nom de la macro de menu contextuel par défaut

SOIT ETRE CREE SOIT PAR DAO SOIT PAR OUTILS/DEMARRAGE

StartUpMenuBar NomMenuMacro Nom de la macro de menu par défaut

SOIT ETRE CREE SOIT PAR DAO SOIT PAR OUTILS/DEMARRAGE

Cette propriété doit être crée la première fois par DAO avant de l'accéder librement

AllowByPassKey Vrai Empêche l'utilisation de SHIFT pour court-circuiter les options de démarrage

DOIT ETRE CREE PAR DAO

Cet exemple simple crée une propriété Hauteur qui contient la valeur 55, et qui est a partir du moment ou on l'a créée, disponible en permanence dans cette base de données

Dim MaProp As Property
' Voici la ligne de création :
' - Hauteur est le nom de la propriété
' - dbSingle est le type de la propriété (on ne peut pas se passer de ce paramètre)
' - 4 est la valeur initiale de la propriété (on doit également, il me semble, la définir à l'initialisation)
Set MaProp = CurrentDb.CreateProperty("Hauteur", dbSingle, 4)
' On ajoute cette propriété dans la collection
CurrentDb.Properties.Append MaProp
' Et a partir de maintenant, on peut redéfinir en toute liberté le Name de la nouvelle propriété
CurrentDb.Properties("Hauteur").Value = 55
MsgBox CurrentDb.Properties("Hauteur").Value
' Maintenant, quand on liste les propriétés de la base en question, les nouvelles propriétés en font partie. La gestion d'erreur est indispensable, car certaines propriétés ne sont pas éditables
On Error Resume Next
For Each MaProp In CurrentDb.Properties
MsgBox MaProp.Name & " = " & MaProp.Value
Next

Options de démarrage

Il est possible, par programmation de modifier les options de démarrage (Outils/Démarrage). Les différentes options sont les suivantes :

CurrentDb.Properties("StartUpForm") = "xxx"
CurrentDb.Properties("StartupShowDBWindow") = True
CurrentDb.Properties("StartupShowStatusBar") = True
CurrentDb.Properties("AllowBuiltinToolbars") = True
CurrentDb.Properties("AllowFullMenus") = True
CurrentDb.Properties("AllowBreakIntoCode") = True
CurrentDb.Properties("AllowSpecialKeys") = True

Il y a plusieurs particularités : La première est que toutes ces propriétés ne sont pas existantes tant que l'utilisateur n'en a pas changé une manuellement au moins une fois. Par exemple, si on exécute ce code immédiatement, on aura une erreur à chaque ligne. Mais, si par exemple, l'utilisateur fait d'abord Outils/Démarrage, et change par exemple StartupForm (Formulaire de démarrage) et met n'importe quoi, par exemple "MenuPrincipal", alors, quand on revient exécuter le code par la suite, on n'a plus du tout d'erreur, sauf sur AllowSpecialKeys qui continuera de renvoyer une erreur. Il s'agit une propriété qui n'existe justement pas dans Outils/Démarrage. C'est cette propriété qui empêche l'utilisation de SHIFT pour court-circuiter la macro AutoExec et toutes les propriétés de démarrage. Elle n'est pas disponible aussi facilement car elle est sans doute trop dangereuse, car, effectivement, si on définit cette propriété à FALSE, et qu'on a dans la volée restreint complètement les autorisations de menus, barres d'outils, etc., alors on n'a VRAIMENT plus accès à la création de la base de données. C'est aussi verrouillé qu'un .MDE.

AInsi donc, pour accéder à cette propriété AllowSpecialKeys, il est nécessaire de créer une nouvelle propriété, de cette manière :

Set Prp = CurrentDb.CreateProperty("AllowByPassKey", dbBoolean, False)
CurrentDb.Properties.Append Prp
CurrentDb.Properties("AllowBypassKey") = False

ATTENTION toutefois, ce code de création de la propriété AllowByPassKey n'a PAS pour effet de l'ajouter dans Outils/Démarrage d'une part, et on ne peut l'exécuter qu'UNE fois dans la vie de la base de données d'autre part (Evidemment, puisqu'une fois qu'elle est créée, on ne peut pas la recréer)

Autre chose :

Dans le champ d'une table

Dans cet exemple, nous allons créer une propriété qui s'appelle Date de création, de type Byte, et dont la valeur est 4. (c'est pas terrible comme cohérence, 4 pour une date, mais bon...). Ensuite, dans la méthode CreateProperty, on définit cette propriété comme s'appelant DateDeCreation. Ce DateDeCreation serait en fait son .Name, si on ne définissait pas à la ligne suivante que le .Name est Date de création. Ensuite, on Append cette nouvelle propriété à la collection des Properties du champ prénom de cette table T_Client.

Attention : Ce n'est pas parce que l'on crée une nouvelle propriété que dans le mode création de la table, elle apparaît!

Il est en outre obligatoire de définir les propriétés Type et Value. Attention : Particularité : La propriété Description de chaque champ n'est pas créée tant que l'utilisateur n'indique pas quelque chose dans ce champ. Si on veut toutefois l'utiliser dans le DAO alors qu'il est encore vide, il est nécessaire de l'ajouter dans la collection Property du Field en question.

Dim MaBase As Database
Dim MaPropriete As Property
Set MaBase = DBEngine.Workspaces(0).Databases(0)
Set MaPropriete = MaBase.TableDefs("T_Client").Fields("Prenom").CreateProperty("DateDeCreation")
MaPropriete.Name = "Date de création"
MaPropriete.Type = DB_BYTE
MaPropriete.Value = 4
MaBase.TableDefs("T_Client").Fields("Prenom").Properties.Append MaPropriete

Création de relations

Il est possible de définir des relations par DAO.

Exemple :

Dim LaRelation As Relation
Dim ChampRelation As Field

' 1. On définit les 2 tables à mettre en relation

Set LaRelation = CurrentDb.CreateRelation("Mariage", "T_Fruit", "T_FactureDetail")

' 2. On définit le champ (clé primaire) de la 1ère table

Set ChampRelation = LaRelation.CreateField("IDFruit")

' 3. On définit la clé étrangère dans l'autre table

ChampRelation.ForeignName = "IDFruit"

' 4. On définit facultativement les propriétés de la relation (voir tableau un peu plus bas)

LaRelation.Attributes = dbRelationUpdateCascade Or dbRelationDeleteCascade

' 5. AJout de la relation dans la Collection

LaRelation.Fields.Append ChampRelation

CurrentDb.Relations.Append LaRelation

Dans cet exemple, on donne le nom "Mariage" à la relation. Si l'on tente d'exécuter ce code une 2ème fois, il y aura une erreur, car la relation "Mariage" existe déjà. Par contre, si on crée un code absolument identique, mais ou on remplace "Mariage" par "Fiançailles" par exemple, alors, une 2ème relation est ainsi créee, que l'on pourra voir dans Outils/Relation : Une 2ème relation prendra le pas sur la première ("Mariage" se verra supprimer son intégrité référentielle)

Types de relations possibles

Voici le tableau des types de relations possible. Dans le cas ou on ne spécifie rien, la relation est simplement pourvue d'une intégrité référentielle sans option.

Exemple d'attributions de propriété :

LaRelation.Attributes = dbRelationUpdateCascade Or dbRelationDeleteCascade

Par contre

LaRelation.Attributes = dbRelationDontEnforce Or dbRelationUpdateCascade

génèrera une erreur au niveau de l'Append, car on ne peut pas demander la non-intégrité, et la mise à jour en cascade

Constante

Description

dbRelationUnique Relation un-à-un.
dbRelationDontEnforce La relation n'est pas appliquée (absence d'intégrité référentielle).
dbRelationInherited La relation existe dans une base autre que la base de données en cours, qui contient les deux tables attachées.
dbRelationUpdateCascade Les mises à jour sont répercutées en cascade.
dbRelationDeleteCascade Les suppressions sont répercutées en cascade.

Copyright (c) 1996 Microsoft Corporation

Traitement des requêtes

Création dynamique d'une requête

CreateQueryDef

Pour créer une requête par le DAO, il est possible de créer la requête normalement, et d'ensuite copier le code SQL. Si la requête existe déjà, une erreur est générée. Attention : Pas besoin d'utiliser la méthode Append pour ajouter la requête à la collection, elle s'y ajoute automatiquement dès la création.

Mais attention : On peut créer une requête par DAO, ensuite, si on l'efface dans l'interface Access, et qu'on recrée la même requête, une requête en plus chaque fois est créée dans la collection QueryDefs, quoique après certains tests, ce n'est pas évident que ce soit aussi systématique...

ATTENTION : Dans le code SQL originel, les chaînes de caractères (dans cet exemple : 'd*') sont encadrées de guillemets. Il faut les remplacer par des apostrophes.

Dim MaBase As DATABASE, UneRequete As QueryDef
Dim JeuEnregistrement As Recordset
Set MaBase = DBEngine.Workspaces(0).Databases(0)
Set UneRequete = MaBase.
CreateQueryDef("R_Client", "SELECT DISTINCTROW T_Client.Prenom, T_Client.NomClient FROM T_Client WHERE (((T_Client.NomClient) Like 'd*')) ORDER BY T_Client.NomClient;")

Effacement d'une requête

DBEngine.WorkSpaces(0).DataBases(0).QueryDefs.Delete ("R_Client")

Utilisation d'une requête temporaire

On a souvent besoin d'une requête pour une tâche spécifique, mais qu'on ne veut pas créer définitivement. Dans cet exemple, nous avons besoin de compter le nombre d'enregistrements de T_Client habitant Genève

Access 97

Dim rst As Recordset
Dim chSQL As String
Set rst = CurrentDb.OpenRecordset("SELECT * FROM T_Client WHERE T_Client.Ville='Genève';")
rst.MoveLast ' Important pour avoir le RecordCount correct.
MsgBox rst.RecordCount
rst.Close

Attention à la chaîne de caractères qui représente l'instruction SQL (ici : "SELECT * FROM T_Client"). Dans ce cas, pas de problème, mais bien souvent, on utilise une requête paramétrée avec comme paramètre un champ de formulaire. Dans ce cas, on demande bien dans la requête, la génération du paramètre avec le générateur pour ne pas faire d'erreur de syntaxe. Quand on récupère la chaîne de caractères en SQL, on se retrouve avec une chaîne du style :

Dim NombreDetailCommande as RecodSet
Set NombreDetailCommande = DBEngine.Workspaces(0).Databases(0).OpenRecordset("SELECT T_CommandeDetail.IDCommande FROM T_CommandeDetail WHERE (((T_CommandeDetail.IDCommande)=[Formulaires]![F_Commande]![IDCommande]));")

(à lire en une seule ligne). Dans ce cas, il faut modifier deux choses : 1. le mot-clé Formulaires doit être remplacé par Forms, et 2. le paramètre ne doit pas être inclut bêtement dans la chaîne de caractères, mais c'est le contenu de [Formulaires]![F_Commande]![IDCommande] qui doit être collé. Voici donc la version qui fonctionne :

Set NombreDetailCommande = DBEngine.Workspaces(0).Databases(0).OpenRecordset("SELECT T_CommandeDetail.IDCommande FROM T_CommandeDetail WHERE (((T_CommandeDetail.IDCommande)=" & [Forms]![F_Commande]![IDCommande] & "));")

Formulaires et autres

Listing des contrôles d'un formulaire

Cet exemple liste tous les champs d'un formulaire.

ATTENTION : Le formulaire doit être ouvert !

Dim ctl As Control
For Each ctl In Forms("F_Client").Controls
MsgBox ctl.Properties("Name")
Next ctl

Ce 2ème exemple, plus complexe, liste tous les contrôles du premier état OUVERT, et liste également chaque propriété de chaque contrôle de cet état.

ATTENTION : Certaines propriétés ne sont lisibles qu'en lecture seule

' Inutile de définir Currentdb.Reports(0)
For C1 = 0 To Reports(0).Controls.Count - 1
For C2 = 0 To Reports(0).Controls(C1).Properties.Count
MsgBox Reports(0).Controls(C1).Properties(C2).Name & " = " & Reports(0).Controls(C1).Properties(C2).Value
Next C2
Next C1

Création dynamique de formulaires et de contrôles

Lors de la création dynamique d'un formulaire, il n'est pas sauvegardé, c'est l'utilisateur, lorsqu'il le quitte qui doit le sauvegarder. A part une inesthétique utilisation de SendKeys("^{s}"), voici une commande plus propre :

DoCmd.Save acForm, "F_NomFormulaire", acSaveYes

Le problème avec cette commande est qu'un formulaire créé avec CreateForm n'a pas encore de nom, et il est absolument impossible d'après mes tests de sauvegarder un formulaire encore inexistant avec un nouveau nom sans aucune confirmation de l'utilisateur. Même avec une suite de SendKeys, la tâche semble impossible.

Par contre (cf quelques exemples plus bas), dans le cas ou on ouvre un formulaire existant, alors, il est possible de le modifier ET de le quitter avec

DoCmd.Save acForm, "F_NomFormulaire", acSaveYes

sans demande de confirmation de la part de l'utilisateur

CreateForm
Exemble minimaliste

Cet exemple crée un formulaire tout vide, sans nom :

Dim FormulaireNouveau As Form
Set FormulaireNouveau = CreateForm

Exemple basique :

Le même exemple, mais avec une vue de la syntaxe pour définir une propriété de ce nouveau formulaire, ainsi que la commande DoCmd (on constate alors que lors de la création d'un formulaire, le focus est automatiquement dessus, puisque le Restore s'applique au nouveau formulaire plutôt qu'au formulaire appelant)

Dim MonFormulaire As Form
Set MonFormulaire =
CreateForm
DoCmd.Restore
MonFormulaire.RecordSource = "T_Client"

Exemple avec création de contrôles

CreateForm, Form, Control, CreateControl

Et ensuite création d'un bouton au sein de ce nouveau formulaire

Dim NouvForm As Form
Dim NouvContr As Control
Set NouvForm = CreateForm
NouvForm.RecordSource = "NomTable"
Set NouvContr =
CreateControl(NouvForm.Name, acCommandButton, 0, , , 1000, 500)
NouvContr.Caption = "Légende"

Ici, NouvForm.Name prend par défaut "Formulaire1", s'il n'existe pas encore dans la base, sinon, il prend "Formulaire2", et ainsi de suite

Exemple avec création et réutilisation de contrôles

Dim NouvForm As Form
Dim NouvContr As Control
Set NouvForm = CreateForm
NouvForm.RecordSource = "Clients"
Set NouvContr = CreateControl(NouvForm.Name, acTextBox, acDetail, , , 0, 0, 5000, 200)
NouvContr.ControlSource = "Société"
Set NouvContr = CreateControl(NouvForm.Name, acTextBox, acDetail, , , 0, 350, 5000, 200)
NouvContr.ControlSource = "Contact"

Ouverture d'un formulaire existant, ajout de contrôle et sauvegarde

Dans le cas ou on ouvre un formulaire existant, on peut alors le fermer et le sauver sans confirmation de l'utilisateur

Dim FormulaireF1 As Form
Dim NouvContr As Control
DoCmd.OpenForm "F1", acDesign
Set FormulaireF1 = Forms("F1") ' ou Me
FormulaireF1.RecordSource = "Clients"
Set NouvContr = CreateControl(FormulaireF1.Name, acTextBox, acDetail, , , 0, 0, 5000, 200)
NouvContr.ControlSource = "Société"
DoCmd.Close acForm, "F1", acSaveYes

Création dynamique d'EventProcedure

Il serait normalement possible d'attribuer du code à un événement grâce à l'instruction CreateEventProc, mais je n'y arrive pas. L'exemple donné dans l'aide en ligne ne fonctionne pas (et pourtant j'ai tenté de le modifier). Il me renvoie une erreur dont si on clique sur le bouton d'aide, la source de l'erreur peut être multiple, mais les explications sont incompréhensibles.

Libération de la mémoire

A la fin de l'utilisation d'objets, il est bien de libérer la mémoire prise par la définition de ces objets.

L'utilisation de Nothing, après Close permet cette libération
Dim BaseSource As Database
Dim TableClient As Recordset
Set BaseSource = DBEngine.Workspaces(0).OpenDatabase("d:\atelier\Source.mdb")
Set TableClient = BaseSource.OpenRecordset("T_Client", dbOpenDynaset)
TableClient.Close
BaseSource.Close
Set BaseSource =
Nothing
Set TableClient = Nothing

Ouverture d'une base de données externe

Dim bdsAutre As Database
Set bdsAutre = DBEngine.Workspaces(0).OpenDatabase("d:\atelier\Source.mdb")

Attache d'une table

Dans cet exemple, nous allons attacher dans la base de données courante, une table provenant de la base de données SPA.MDB, qui s'appelle T_Animal. Au passage, nous allons nommer cette table une fois attachée dans la base courante T_LiaisonAnimal. Attention : On ne peut pas attacher des tables si la base de données fournisseuse est ouverte. Dans la méthode connect, la chaîne de caractères commence par un point-virgule. On ajoute des choses avant ce point virgule lorsqu'on lie des tables qui ne sont pas Access.

Dim BD As Database
Dim TableLiee As TableDef
Set BD = DBEngine.Workspaces(0).Databases(0)
Set TableLiee = BD.CreateTableDef("T_LiaisonAnimal")
TableLiee.SourceTableName = "T_Animal"
TableLiee.Connect = ";DATABASE=C:\Atelier\spa.mdb"
BD.TableDefs.Append TableLiee

Accès à une propriété

Property, CreateProperty, Properties

Les propriétés sont soit déjà existantes, soit on peut les créer de toutes pièces. Dans tous les cas, en DAO, il est nécessaire d'utiliser la méthode CreateProperty, même pour recréer une propriété existante (dans cet exemple, on utilise la propriété de table existante Description). Dans le cas ou on crée une propriété complètement nouvelle, même après sa création avec DAO, elle n'est pas visible normalement comme toutes les propriétés.

Analyse de CreateProperty :

("Description", dbText, "test") est le nom de la propriété. Ici, on se réfère à la propriété existante Description
("Description", dbText, "test") est le type du contenu de la propriété. Comme dans ce cas, Description va contenir la chaîne de caractères test, il faut indiquer le type dbText
("Description", dbText, "test") Contenu réel de la propriété Description

Attention : On ne peut utiliser la méthode Append sur une propriété que lorsque l'objet sur lequel elle se base est persistant

Dim MaBase As DATABASE, DefinitionTable As TableDef
Dim Propriete As
Property
Set MaBase = CurrentDb
Set DefinitionTable = MaBase.TableDefs!T_Client
Set Propriete = DefinitionTable.
CreateProperty("Description", dbText, "test")
MsgBox DefinitionTable.
Properties("Description")

Lecture de la propriété DateCreated

Dim CetteBase As Database
Dim TDefClient As TableDef
Set CetteBase = CurrentDb
Set TDefClient = CetteBase.TableDefs("T_Client")
MsgBox TDefClient.DateCreated

ATTENTION : On ne peut pas remplacer

CetteBase.TableDefs("T_Client")

par

CurrentDB.TableDefs("T_Client")

C'est pourtant la même chose, mais ça ne marche pas.

Listing exhaustif des propriétés d'une base de données

Cet exemple écrit dans une table "T_Propriete" l'ensemble des propriétés d'une base de données. Avant d'exécuter ce code, il est nécessaire d'avoir une table T_Propriete munie des champs : IDPropriere, NomPropriete, Valeur

Le On Error Resume Next est nécessaire, car pour des raisons que j'ignore, certaines propriétés ne sont pas éditables.

Dim TablePropriete As Recordset
Set TablePropriete = CurrentDb.OpenRecordset("T_Propriete", dbOpenDynaset)
Dim x
On Error Resume Next
For x = 0 To CurrentDb.Properties.Count - 1
TablePropriete.AddNew
TablePropriete("IDPropriete") = x
TablePropriete("NomPropriete") = CurrentDb.Properties(x).Name
TablePropriete("Valeur") = CurrentDb.Properties(x).Value
TablePropriete.Update
Next
TablePropriete.Close

Gestion des relations

Grâce à la collection Relations, il est possible d'avoir un listing exhaustif de toutes les relations de chaque table. La propriété Table renvoie le nom de la table du côté 1, et ForeignTable renvoie le nom de la table du côté Infini.

L'exemple suivant affiche toutes les relations

Dim Rel As Relation
For Each Rel In CurrentDb.Relations
MsgBox Rel.Table & " va vers " & Rel.ForeignTable

' Exemple T_Client va vers T_ClientGenre

Next

Propriétés des TableDefs
Attributes

La propriété Attributes détermine si une table (ou un autre objet) est Caché ou Système. Pour ce faire, il est nécessaire de passer par la commande AND avec des constantes intrinsèques : dbSystemObject et dbHiddenObject

msgbox CurrentDB.Tabledefs("T_Client").Attributes and dbHiddenObject

montrera 0 (une autre valeur si la table était cachée)

Name

Permet de récupérer le nom d'une

Récupérer le type d'un contrôle

Voici la manière d'obtenir le type du premier contrôle du premier formulaire ouvert :

MsgBox TypeName(Forms(0).Controls(0))

Comme on le voit, c'est une fonction, et pas une propriété comme on aurait pu s'y attendre du style :

MsgBox Forms(0).Controls(0).TypeName

Par contre, on peut avoir le numéro du type du contrôle :

MsgBox Forms(0).Controls(0).ControlType

Mais la méthode ControlType envoie un numéro, qui correspond à une constante :

acLabel Étiquette
acRectangle Rectangle
acLine Trait
acImage Image
acCommandButton Bouton de commande
acOptionButton Bouton d'options
acCheckBox Case à cocher
acOptionGroup Groupe d'options
acBoundObjectFrame Cadre d'objet dépendant
acTextBox Zone de texte
acListBox Zone de liste
acComboBox Zone de liste modifiable
acSubform Sous-formulaire/Sous-état
acObjectFrame Cadre d'objet indépendant ou graphique
acPageBreak Saut de page
acPage Page
acCustomControl Contrôle ActiveX (personnalisé)
acToggleButton Bouton bascule
acTabCtl Onglet
Une manière élégante de parcourir les contrôles d'un formulaire

Dim Jouet As Form
Set Jouet = Forms!F_Jouet
Dim Ctl As Control
' Gestion d'erreurs nécessaire dans le sens que certains contrôles ne contiennent aucune Value
On Error Resume Next
For Each Ctl In Jouet
With Ctl
MsgBox "Nom : " & .Name & " - Type : " & TypeName(Ctl) & " - Contient : " & .Value
End With
Next

Les mêmes propriétés sur des objets différents

Attention aux confusions : Prenons comme exemple la propriété ValidationRule. Elle est accessible depuis un RecordSet, mais aussi dans un TableDef. dans le RecordSet, elle est en lecture seule, tandis que dans le TableDef, elle est modifiable.

Dim Enr As Recordset
Set Enr = CurrentDb.OpenRecordset("Test")
Enr.Index = "NomPersonne"
MsgBox Enr("Age").ValidationRule
' Enr("Age").ValidationRule = ">20" Provoque une erreur
CurrentDb.TableDefs("Test").Fields("Age").ValidationRule = ">20" ' Cette assignation est correcte (pour autant que la table soit fermée non seulement physiquement depuis Access mais également avec le RecordSet avec la méthode Close.

Transactions

Il existe 3 méthodes (de WorkSpace) pour gérer les transactions :

BeginTrans Débute l'espace des transactions
CommitTrans A la fin de la transaction, confirme l'ensemble des actions de la transaction
RollBack A la fin de la transaction, élimine tous les changements effectués.

Cet exemple ouvre la table Test, se positionne sur le premier enregistrement par nom alphabétique, et change le nom en XXXXX. L'écriture effective ne se fait que lorsque la méthode CommitTrans est effectuée. Dans le cas ou Roll-Back est exécuté à la place de CommitTrans, toutes les modifications sont annulées.

Dim Enr As Recordset
Set Enr = CurrentDb.OpenRecordset("Test")
Enr.Index = "NomPersonne"
Workspaces(0).BeginTrans
Enr.Edit
Enr("NomPersonne") = "XXXXX"
Enr.Update
Workspaces(0).CommitTrans
' Workspaces(0).RollBack aurait annulé toutes les modifications de la transaction.
Enr.Close

Il est possible d'imbriquer les BeginTrans comme des For Next jusqu'à 5 niveaux. Le premier RollBack ou CommitTrans rencontré s'appliquera alors au dernier BeginTrans, je suppose

ATTENTION : CommitTrans OU RollBack DOIT être rencontré dans le code. Dans le cas ou on ouvre une transaction avec BeginTrans, et qu'on effectue une modification, cette table est verrouillée complètement en écriture jusqu'à ce que :

Comptages

Chaque collection comprend un certain nombre d'objets. Ces objets sont référencés de 1 jusqu'à Collection.Count.

: Compter le nombre d'enregistrements en cours

RecordCount

Dim Rec As Recordset
Set Rec = CurrentDb.OpenRecordset("T_Client")
Rec.MoveLast
MsgBox Rec.RecordCount

ATTENTION : il est très important d'utiliser MoveLast pour connaître le nombre exact d'enregistrements du RecordSet, sinon, il renvoie 1.

Il y a un autre piège : Si le Recordset est vide, et qu'on utilise MoveLast, on reçoit une erreur. Pour contrer le tout, il faut :

  1. Regarder si RecordCount renvoie 0. Si c'est le cas, le Recordset est vide
  2. Sinon, il faut utiliser la méthode MoveLast, et,
  3. Réutiliser la méthode RecordCount

: Comptage des formulaires ouverts et chacun de leurs contrôles

Forms(x), Count, Name

Cet exemple nous donne le nombre de formulaires ouverts

Debug.Print Forms.Count

Cet exemple nous affiche le nombre de contrôles existant dans le formulaire F_Client. Si ce formulaire n'est pas ouvert, une erreur est générée :

Debug.Print Forms![F_Client].Count

Chaque formulaire Form étant numéroté de 0 jusqu'à NombreFormulaire-1, il est possible de les énumérer : C'est la boucle principale. Chaque contrôle étant numéroté de 0 à NombreContrôles – 1 dans le formulaire définit avec Set MonFormulaire = Forms(CtrFormulaire), on peut alors, à l'aide de la petite boucle, énumérer chaque contrôle de chaque formulaire. Les numéros des formulaires sont attribués définitivement au fur et à mesure de leur création.

Dim MonFormulaire As Form, CtrFormulaire As Integer, CtrControle As Integer, Tabulation As String
Tabulation = Chr(9)
For CtrFormulaire = 0 To
Forms.Count - 1
Set MonFormulaire = Forms(CtrFormulaire)
Debug.Print MonFormulaire.Name
For CtrControle = 0 To MonFormulaire.Count - 1
Debug.Print Tabulation; MonFormulaire(CtrControle).
Name
Next CtrControle
Next CtrFormulaire

: Comptage du nombre de tables (ouverts ou non) de la base en cours

Count, TableDefs

Dim MaBase As DATABASE, MaPropriete As Property
Set MaBase = DBEngine.Workspaces(0).Databases(0)
Debug.Print MaBase.
TableDefs.Count

: Compte le nombre d'enregistrements

RecordCount

Dans le cas ou le RecordSet est vide, RecodrCount renvoie .-1

Dim MaBase As DATABASE, JeuEnregistrement As Recordset
Set MaBase = DBEngine.Workspaces(0).Databases(0)
Set JeuEnregistrement = MaBase.OpenRecordset("T_Client")
Debug.Print JeuEnregistrement.
RecordCount

comptage du nombre d'objets des collections

Dim EspaceTravail As Workspace
Dim UneBase As DATABASE
Dim UneTable As TableDef
Dim UnChamp As Field
Set EspaceTravail = DBEngine.Workspaces(0)
Set UneBase = EspaceTravail.Databases(0)
Set UneTable = UneBase.TableDefs!T_Client
Set UnChamp = UneTable.Fields!NomClient
Debug.Print "Nombre WorkSpaces "; DBEngine.Workspaces.Count
Debug.Print "NombreDatabases "; EspaceTravail.Databases.Count
Debug.Print "Nombre Tables "; UneBase.TableDefs.Count
Debug.Print "Nombre champs "; "UneTable.Fields.Count"
Debug.Print "Nombre index : "; "UneTable.Indexes.Count"

Affichage des différents éléments d'une base de données

Affichage de toutes les tables, et de tous leurs champs

Cette fois-ci, on n'utilise plus le For Each, mais le fait que chaque collection d'objet peut se référencer par son numéro, et la propriété Count de chaque collection

Dim MaBase As DATABASE
Dim DefinitionTable As TableDef
Dim MonChamp As Field
Dim x, y
Set MaBase = CurrentDB
For x = 0 To MaBase.TableDefs.Count - 1
Debug.Print MaBase.TableDefs(x).Name
Set DefinitionTable = MaBase.TableDefs(x)
For y = 0 To DefinitionTable.Fields.Count - 1
Debug.Print " " & DefinitionTable.Fields(y).Name
Next y
Next

With – End With

Dans cet exemple, on utilise With pour simplifier l'écriture

Dim MaBD As DATABASE
Dim MaTable As Recordset
Set MaBD = DBEngine.Workspaces(0).Databases(0)
Set MaTable = MaBD.OpenRecordset("T_Client")
Do Until MaTable.EOF
If MaTable![NomClient] = "Pochtron" Then
With MaTable
.Edit
![NomClient] = "Pochon"
.UPDATE
End With
End If
MaTable.MoveNext
Loop
MaTable.Close
MaBD.Close

Actualisation immédiate des collections

Refresh

Lors de programmation en DAO, il est possible que les colections ne soit pas immédiatement actualisées. Par exemple, imaginons que nous soyons dans un formulaire qui contient le code suivant :

Dim EspaceTravail As Workspace
Dim UneTable As TableDef
Set UneBase = CurrentDB
Set UneTable = UneBase.TableDefs!T_Client
UneBase.TableDefs.
Refresh
Debug.Print "Nombre Tables "; UneBase.TableDefs.Count

Lançons-e une première fois. Sans fermer le formulaire, allons ajouter une table dans notre base de données. Ensuite, revenons dans le formulaire, et réexécutons le code. Si on ne place pas la méthode Refresh commedans l'exemple, le nombre des tables affiché n'est pas à jour.

Divers

Quand on utilise la méthode Close, c'est comme si on remettait la variable objet en question à 0. Il faut, si on veut continuer à l'utiliser, lui redéfinir une valeur à l'aide de Set.

Ecriture condensée

Utilisation des objets par défaut des collections

Chaque collection possède un objet par défaut. Ainsi, l'Objet par défaut de DBEngine est Workspaces. On peut donc, à la place de :

DBEngine.WorkSpaces(0)

écrire :

DBEngine(0)

L'objet par défaut de WorkSpaces est DataBase. On peut donc, à la place de

DBEngine.WorkSpaces(0).DataBases(0)
DBEngine(0)(0)

Utilisation de CurrentDB

Set MaBase = DBEngine.Workspaces(0).Databases(0)

est égal à

Set MaBase = CurrentDB

Utilisation des variables objets

Les 2 exemples suivants créent dynamiquement la table T_Dyn, et y installent un champ Age

Set MonChamp = DBEngine.Workspaces(0).Databases(0).CreateTableDef("T_Dyn").CreateField("Age", DB_SINGLE)

Est égal à

Set MaBase = DBEngine.Workspaces(0).Databases(0)
Set MaTableDef = MaBase.CreateTableDef("T_Dyn")
Set MonChamp = MaTableDef.CreateField("ReelSimple", DB_SINGLE)

' test Grobeutana