formateur informatique

Visionneuse d'images sur formulaire Access en VBA

Accueil  >  Bureautique  >  Access  >  Access VBA  >  Visionneuse d'images sur formulaire Access en VBA
Livres à télécharger


Pour partager cette vidéo sur les réseaux sociaux, voici son url absolue :

Pour l'intégrer sur votre site internet ou blog, vous pouvez l'embarquer :

Sujets et formations similaires :


Visionneuse d'images sur un formulaire VBA Access

Dans cette formation, nous proposons de bâtir une application Access permettant de visualiser des images, par le biais d'un formulaire, comme s'il s'agissait d'un album photos. Nous souhaitons nous rapprocher du résultat que nous avions atteint lors de la formation sur la création d'un album photos pour le Web en Javascript.



Visionneuse images archivées en base de données par le code Visual Basic pour Access

Mais la philosophie et le langage sont différents. Donc les adaptations et défis sont nombreux.

Les sources de l'application
Afin de focaliser nos efforts sur le code VBA utile, nous proposons de récupérer des sources existantes. Il s'agit d'images pour l'album photos et d'un formulaire prêt à les accueillir. La décompression conduit à un fichier de base de données Access (visionneuse-images.accdb) ainsi qu'à un sous dossier images dans lequel figurent quelques photos pour réaliser des essais.
  • Double cliquer sur le fichier visionneuse-images.accdb pour l'ouvrir dans Access,
  • Cliquer sur le bouton Activer le contenu du ruban jaune,
  • Dans le volet de gauche des objets Access, double cliquer sur le formulaire Visionneuse,
Le formulaire s'affiche ainsi au centre de l'écran, en mode exécution. Il a été bâti en suivant les règles enseignées par la formation sur la création et la personnalisation de formulaires avec Access. Comme vous le constatez, la structure du formulaire existe déjà. Il affiche l'image principale au centre de l'album et quatre petites miniatures sur la droite. Ces images sont intégrées. Elles s'affichent donc par défaut comme des objets statiques. Aucun code n'est effectivement associé à l'application pour l'instant.

Dans le volet des objets Access, sur la gauche de l'environnement, vous notez la présence de deux tables : Chemin et Images. La table Images sert à stocker et archiver toutes les photos contenues dans un dossier désigné par le bouton Parcourir du formulaire. La table Chemin quant à elle, sert à mémoriser le chemin d'accès absolu à ces images pour éviter les répétitions inutiles dans la table Images.

Structure base de données sur deux tables pour visionneuse images via formulaire Access

Ainsi nous proposons d'offrir à l'utilisateur de générer un nouvel album photos ou bien de naviguer au travers de celui existant, par le biais des informations stockées dans les tables, lors de la dernière utilisation.

Parcourir les fichiers d'un dossier en VBA Access
Dans un premier temps, nous proposons de coder les instructions qui seront déclenchées au clic sur le bouton Parcourir (btn_chercher). L'utilisateur pourra ainsi désigner le dossier des photos à visualiser. Ces dernières devront être traitées et archivées par le code VBA Access qui se chargera ensuite de démarrer la navigation au travers de l'album photos ainsi généré.
  • Cliquer sur la flèche du bouton Affichage, situé sur la gauche dans le ruban Accueil,
  • Dans la liste, choisir Mode création,
  • Sur le formulaire, cliquer sur le bouton à la loupe (btn_chercher) pour le sélectionner,
  • Si la feuille de propriétés n'est pas visible, cliquer sur le bouton Feuille de propriétés dans le ruban Création,
  • Activer l'onglet Evénement de cette dernière,
  • Cliquer sur le petit bouton à droite de son événement Au clic,
  • Dans la boîte de dialogue qui suit, choisir Générateur de code et valider par Ok,
Nous basculons ainsi dans l'éditeur de code VBA Access entre les bornes de la procédure événementielle btn_chercher_Click. Les instructions que nous y ajouterons se déclencheront au clic sur le bouton.

Pour pouvoir manipuler les fichiers du système et les informations de base de données, nous devons commencer par ajouter des références à certaines classes. Ces classes, une fois héritées, fourniront les propriétés et méthodes nécessaires.
  • En haut de l'éditeur, dérouler le menu Outils puis cliquer sur Références,
  • Dans la liste, cocher les lignes Microsoft Office 16.0 Object Library et Microsoft ActiveX Data Objects 6.1 Library, puis valider par Ok,
La première référence permet d'accéder aux méthodes pour manipuler les boîtes de dialogue standards de Windows. La seconde permet d'hériter des méthodes permettant de manipuler les enregistrements et champs de tables d'une base de données. Les numéros (16.0 et 6.1) dépendent des versions des librairies installées sur votre système d'exploitation.

L'extrait de la capture ci-dessous illustre les références ajoutées.

Références VBA Access pour piloter boîtes de dialogue et fichiers et dossiers du disque par le code

Désormais nous pouvons déclarer les variables nécessaires au stockage des informations, ainsi que celles permettant de manipuler les fichiers et dossiers.
  • Dans les bornes de la procédure btn_chercher_Click, ajouter les déclarations suivantes :
Dim boite_dialogue As Office.FileDialog
Dim fichier As Object
Dim dossier, chaque_fichier
Dim nom_fichier As String: Dim extension As String
Dim date_creation As String
Dim num_chemin As Long


Grâce à la première référence, nous déclarons un objet (boite_dialogue) héritant des propriétés et méthodes permettant de piloter les boîtes de dialogue standards. Les trois variables objets suivantes permettront de manipuler les dossiers désignés et les fichiers contenus. Bien sûr, nous souhaitons prélever des informations sur ces fichiers pour les inscrire en base de données, c'est pourquoi nous déclarons les variables nom_fichier, extension et date_creation. Leur nom judicieux explicite clairement leur but. Enfin, num_chemin stockera la valeur numérique, issue de la table Chemin, servant de clé primaire et de liaison entre les tables , pour restituer le chemin complet des photos, afin de pouvoir les afficher sur le formulaire.

Nous devons instancier les classes permettant d'accéder aux fichiers et dossiers en commençant par affecter l'objet permettant de manipuler la boîte de dialogue standard.
  • Pour ce faire, à la suite du code, ajouter les instructions suivantes :
Set boite_dialogue = Application.FileDialog(msoFileDialogFolderPicker)
boite_dialogue.Title = 'Sélectionner un dossier pour récupérer son contenu'
If boite_dialogue.Show = -1 Then
nom_dossier = boite_dialogue.SelectedItems(1)

Set fichier = CreateObject('scripting.filesystemobject')
Set dossier = fichier.getfolder(nom_dossier)
MsgBox (nom_dossier)
End If


Grâce à la référence ajoutée et grâce à la propriété FileDialog de l'objet Application, nous affectons l'objet boite_dialogue qui hérite des méthodes nécessaires. Le paramètre msoFileDialogFolderPicker de l'objet, comme l'enseigne l'aide en ligne, définit une boîte de dialogue permettant de désigner des dossiers.

La méthode Show de cet objet ainsi hérité permet d'afficher cette boîte de dialogue standard. Si elle retourne la valeur numérique -1, c'est que l'utilisateur a cliqué sur le bouton Annuler. C'est la raison pour laquelle nous intégrons la suite du traitement dans une instruction conditionnelle If. Grâce à la propriété SelectedItems de l'objet boîte de dialogue, nous stockons le nom et le chemin du dossier désigné par l'utilisateur dans la variable nom_dossier.

Ensuite et comme nous en avons l'habitude désormais, la fonction CreateObject avec l'argument scripting.filesystemobject, permet d'instancier la classe fournissant toutes les méthodes nécessaires à l'objet fichier ainsi affecté, afin de manipuler les fichiers et dossiers. Dès lors, sa méthode getfolder avec le chemin du dossier en paramètre, permet d'affecter l'objet dossier permettant de pointer sur ce dernier. A titre de vérification enfin, nous déclenchons une boîte de dialogue (MsgBox) temporaire pour vérifier le chemin ainsi récolté.
  • Enregistrer les modifications et basculer sur le formulaire (ALT + F11),
  • L'exécuter (F5), cliquer sur le bouton de la loupe (btn_chercher),
  • Sélectionner un dossier contenant des images et cliquer sur le bouton Ok,
Récupérer chemin et nom dossier désigné par boîte de dialogue en Visual Basic Access

Comme l'illustre la capture ci-dessus, après la boîte de dialogue correctement pilotée, le MsgBox se déclenche, confirmant que le chemin du dossier est correctement stocké dans la variable.
  • Afficher de nouveau le formulaire Access en mode création,
  • Puis, basculer dans l'éditeur de code (ALT + F11),
  • Taper une apostrophe devant la ligne du MsgBox pour passer l'instruction en commentaire,
Nous devons désormais accéder au contenu de ce dossier pour le parcourir à l'aide d'une boucle for each.
  • Pour ce faire, à la suite du code, toujours dans l'instruction conditionnelle If, ajouter les bornes de la boucle comme suit :
For Each chaque_fichier In dossier.Files

Next chaque_fichier


Comme vous le savez, dans une boucle For Each, la variable à gauche du mot clé In doit être du même type que la variable située à droite. La propriété files de l'objet dossier renvoie une collection énumérant chacun des fichiers. La variable chaque_fichier non typée hérite donc de ce type.

Dans cette boucle, nous devons traiter chaque fichier pour l'inscrire dans la table images de la base de données. Mais avant cela, nous devons purger les tables des anciennes informations. Dans les deux cas, nous devons donc accéder aux données. Et pour ce faire, nous devons déclarer les objets héritant des classes ADO (ActiveX Data Objects). Pour que la navigation puisse se poursuivre entre les différentes actions générées depuis le formulaire, nous souhaitons conserver en mémoire la position de l'enregistrement en cours de consultation. Pour cela, nous devons déclarer nos objets dans les variables publiques, en dehors de toute procédure. Ainsi, tant qu'ils ne seront pas détruits, nous pourrons les exploiter dans n'importe quelle procédure événementielle, notamment celles des boutons de navigation.
  • Tout en haut de la page de code, avant la procédure btn_chercher_Click et juste après l'instruction Option Compare Database, ajouter les déclarations publiques suivantes :
Option Compare Database

Dim requete As String: Dim la_base As Database: Dim ligne As Recordset
Dim borne_sup As Integer: Dim borne_inf As Integer: Dim indice As Integer
Dim nom_dossier As String: Dim indice_premiere As Integer


Nous aurons besoin d'exécuter des requêtes sur la base de données. La variable requete permettra donc de stocker la syntaxe SQL des commandes à réaliser. Grâce à la référence à ADO, les objets la_base et ligne permettront de manipuler respectivement la base de données et ses enregistrements.

Les variables borne_sup et borne_inf sont déclarées comme des entiers afin de récupérer l'information du champ numeroAuto de la table des images, pour connaître les bornes de la navigation. Les variables indice et indice_premiere serviront à repérer l'emplacement lors de la navigation et la position de la première miniature, à afficher sur la droite de l'album. Comme toutes ces variables sont publiques, nous en conserverons la mémoire d'une procédure à une autre.

La variable nom_dossier est un peu particulière. En effet, nous l'avons déjà exploitée sans la déclarer (nom_dossier = boite_dialogue.SelectedItems(1)). C'est toute la permissivité de VBA qui la déclarée à la volée en tant que variant, donc non typée. Désormais elle est correctement typée et déclarée en variable publique, de manière à ce que le chemin d'accès au dossier puisse être transféré d'une procédure à une autre.

Nous devons maintenant purger la base de données puis commencer par inscrire le chemin du dossier dans la table parent. Pour cela, nous allons exécuter des requêtes actions dont nous allons écrire la syntaxe. Avant cela, l'objet base de données doit être instancié au démarrage de l'application. Ainsi il sera connu dans toutes les autres procédures, notamment dans btn_chercher_Click.
  • Pour ce faire, après la procédure btn_chercher_Click, créer la procédure demarrage :
Private Sub demarrage()

Set la_base = Application.CurrentDb

End Sub


La méthode CuurentDb de l'objet Application permet de faire pointer l'objet ainsi affecté, sur la base de données en cours.
  • Enregistrer les modifications et basculer sur le formulaire en mode création,
  • A l'aide de la liste déroulante de la feuille de propriétés, choisir l'objet Formulaire,
  • Activer l'onglet Evénement, cliquer sur le bouton de son événement Sur chargement,
  • Dans la boîte de dialogue, choisir Générateur de code et valider par Ok,
Déclencher un code Visual Basic Access au chargement du formulaire de photos

Nous basculons de nouveau dans l'éditeur de code Visual Basic Access mais entre les bornes de la procédure événementielle Form_Load, soit celle du code qui s'activera au chargement du formulaire Access.
  • Entre les bornes de cette procédure, ajouter l'instruction suivante :
demarrage

Nous nous contentons de passer la main à la procédure demarrage qui s'occupera des traitements d'initialisation, communs avec ceux que doit enclencher la procédure btn_chercher_Click à l'issue.
  • Dans la procédure btn_chercher_Click, juste avant la boucle For Each, ajouter les instructions suivantes :
requete = 'DELETE FROM Chemin'
la_base.Execute requete

requete = 'DELETE FROM Images'
la_base.Execute requete

requete = 'INSERT INTO Chemin (chemin_adr) VALUES ('' & nom_dossier & '' & '')'
la_base.Execute requete

Set ligne = la_base.OpenRecordset('SELECT chemin_num FROM Chemin', dbOpenDynaset)
ligne.MoveFirst
num_chemin = ligne.Fields('chemin_num').Value
ligne.Close

MsgBox num_chemin


La méthode Execute de l'objet de base de données la_base permet de lancer l'exécution de la requête action qui lui est passée en paramètre (requete). Cette variable est préalablement affectée de la syntaxe nécessaire pour purger la table Chemin dans un premier temps, grâce au mot clé DELETE. Nous réitérons ce processus sur la table Images en adaptant le nom dans la syntaxe SQL. Ensuite, grâce aux mots clés INSERT INTO, nous montons une requête Ajout sur la table Chemin pour inscrire dans le champ chemin_adr la valeur du chemin d'accès complet auquel nous ajoutons un anti-slash de séparation (INSERT INTO Chemin (chemin_adr) VALUES ('' & nom_dossier & '' & '')).

Enfin, grâce à une requête sélection que nous exécutons par la méthode OpenRecordset de l'objet de base de données, avec le paramètre dbOpenDynaset, nous récupérons l'identifiant de l'information inscrite dans le champ (SELECT chemin_num), car elle sert de liaison avec la table Images dans laquelle nous devons inscrire les données des fichiers que nous allons récupérer. Comme nous l'avait appris la formation pour établir la connexion aux données, la méthode MoveFirst de l'objet Recordset (ligne) ainsi instancié, permet de placer le pointeur sur le premier enregistrement qui est le seul en l'occurrence, dans cette table. De fait, la propriété Fields avec le nom du champ en paramètre, permet de récupérer cette valeur générée automatiquement par Access, afin de l'inscrire dans la variable num_chemin. Nous n'oublions pas de fermer la connexion ouverte par cet objet Recordset pour libérer les ressources.
  • Enregistrer les modifications, basculer sur le formulaire et l'exécuter,
  • Cliquer sur le bouton de recherche, désigner un dossier et valider,
Le numéro contenu dans le champ de la table Chemin s'affiche grâce à la fonction MsgBox.
  • Afficher le formulaire en mode création,
  • Dans le volet des objets Access, double cliquer sur la table Chemin pour l'ouvrir,
Comme l'illustre la capture ci-dessous, nous avons parfaitement archivé le chemin d'accès complet au dossier des images, désigné par le biais de la boîte de dialogue pilotée par le code VBA. Vous constatez de même la correspondance entre le numéro affiché par le MsgBox et celui inscrit dans le champ chemin_num de la table. Nous pourrons donc l'exploiter pour l'inscrire dans le champ de la clé étrangère de la table enfant, soit la table Images.

Archiver chemin accès aux images par code VBA dans table de base de données

Désormais, nous devons parcourir les fichiers du dossier grâce à la boucle For Each, afin de récupérer les informations et de les inscrire dans la table Images.
  • Fermer la table Chemin et afficher le formulaire en mode création,
  • Puis, basculer dans l'éditeur de code Visual Basic (Alt + F11),
  • Passer la ligne du MsgBox en commentaire,
  • Dans les bornes de la boucle For Each de la procédure btn_chercher_Click, ajouter les instructions suivantes :
nom_fichier = chaque_fichier.Name
extension = fichier.GetExtensionName(nom_dossier & '' & nom_fichier)

If (UCase(extension) = 'JPG' Or Ucase(extension) = 'PNG' Or Ucase(extension) = 'BMP' Or Ucase(extension) = 'JPEG') Then
date_creation = fichier.GetFile(nom_dossier & '' & nom_fichier).DateCreated
requete = 'INSERT INTO Images (images_adr, images_fichier, images_texte, images_date) VALUES (' & num_chemin & ','' & nom_fichier & '','' & nom_fichier & '','' & date_creation & '')'
la_base.Execute requete

End If


La propriété héritée Name de l'objet pour manipuler les fichiers permet de récupérer le nom de fichier en cours de lecture dans la boucle. La méthode GetExtensionName obtient l'extension du fichier passé en paramètre. Ainsi nous pouvons réaliser un test multi-critères pour nous assurer que le fichier lu est bien une image (jpg, png, bmp ou jpeg). Nous exploitons à ce titre la fonction VBA Ucase pour transformer en majuscules le texte passé en paramètres, et éviter les différences de casse dans la comparaison. Enfin, nous exécutons une requête Ajout (INSERT INTO) sur la table Images (From Images) de manière à insérer chacune de ces informations dans les champs correspondants.
  • Enregistrer les modifications, basculer sur le formulaire puis l'exécuter,
  • Désigner un dossier à l'aide du bouton à la loupe,
  • A l'issue, ouvrir la table Images,
Inscrire dans table de base de données informations sur images parcourues par code VBA Access dans un dossier

Comme l'illustre la capture ci-dessus, nous avons parfaitement réussi à enregistrer les informations de chaque fichier image dans les enregistrements de la table Images. Nous n'avons pas encore traité le texte du nom de fichier. Nous le prévoirons plus tard afin d'offrir un descriptif plus clair. Notez l'information sur la date, obtenue par la propriété DateCreated de la méthode GetFile exploitée sur l'objet fichier.



Chargement des images, connexion aux données
Le chargement des premières images doit survenir dans deux cas. Le premier se déclenche après la sélection d'un dossier par le biais du bouton Parcourir. Le deuxième cas correspond à la première ouverture du formulaire, lorsque des images ont été archivées lors d'une précédente utilisation. Nous proposons donc d'exploiter la procédure demarrage, créée précédemment. Celle-ci doit être appelée par la procédure btn_chercher_click ainsi que par la procédure form_load. Pour cette dernière, nous avons déjà réalisé l'appel.
  • Fermer la table Images et afficher le formulaire en mode Création,
  • Puis, basculer de nouveau dans l'éditeur de code Visual Basic Access,
  • A la fin de la procédure btn_chercher_Click, avant le End Sub, ajouter l'appel de la procédure :
demarrage

Le code complet de la procédure btn_chercher_Click est le suivant :

Private Sub btn_chercher_Click()
Dim boite_dialogue As Office.FileDialog
Dim fichier As Object
Dim dossier, chaque_fichier
Dim objet_fichier
Dim nom_fichier As String: Dim extension As String
Dim date_creation As String
Dim num_chemin As Long

Set boite_dialogue = Application.FileDialog(msoFileDialogFolderPicker)
boite_dialogue.Title = 'Sélectionner un dossier pour récupérer son contenu'

If boite_dialogue.Show = -1 Then
nom_dossier = boite_dialogue.SelectedItems(1)

Set fichier = CreateObject('scripting.filesystemobject')
Set dossier = fichier.getfolder(nom_dossier)
'MsgBox (nom_dossier)

requete = 'DELETE FROM Chemin'
la_base.Execute requete

requete = 'DELETE FROM Images'
la_base.Execute requete

requete = 'INSERT INTO Chemin (chemin_adr) VALUES ('' & nom_dossier & '' & '')'
la_base.Execute requete

Set ligne = la_base.OpenRecordset('SELECT chemin_num FROM Chemin',dbOpenDynaset)
ligne.MoveFirst
num_chemin = ligne.Fields('chemin_num').Value
ligne.Close
'MsgBox num_chemin

For Each chaque_fichier In dossier.Files
nom_fichier = chaque_fichier.Name
extension = fichier.GetExtensionName(nom_dossier & '' & nom_fichier)
'MsgBox (nom_dossier & '' & nom_fichier)

If (UCase(extension) = 'JPG' Or UCase(extension) = 'PNG' Or UCase(extension) = 'BMP' Or UCase(extension) = 'JPEG') Then
date_creation = fichier.GetFile(nom_dossier & '' & nom_fichier).DateCreated
'MsgBox (nom_fichier & ' - ' & extension & '-' & date_creation)

requete = 'INSERT INTO Images(images_adr, images_fichier, images_texte, images_date) VALUES (' & num_chemin & ','' & nom_fichier & '','' & nom_fichier & '','' & date_creation & '')'
la_base.Execute requete
End If
Next chaque_fichier

End If

demarrage

End Sub
  • Au début de la procédure demarrage, avant l'affectation de la variable la_base, ajouter les déclarations et affectations suivantes :
On Error Resume Next
Dim nom_fichier As String: Dim extension As String
Dim date_creation As String: Dim titre_fichier As String

indice = 0
indice_premiere = 0


L'instruction On Error Resume Next permet d'ignorer toute exception non gérée dans le cas par exemple, où il n'y aurait pas assez de photos à traiter. Nous déclarons ensuite les variables locales nécessaires pour manipuler les informations extraites de la table, afin de les afficher sur le formulaire. Puis, nous initialisons les deux variables publiques qui permettent de garder le fil de la navigation.

Avant d'afficher la première image au centre de l'album et les quatre premières dans les contrôles miniatures respectifs, nous devons connaître les bornes sur lesquelles nous travaillons. Ces bornes correspondent au plus petit numéro pour la première image et au plus grand pour la dernière. Le langage SQL propose respectivement les fonctions Max et Min à appliquer sur un champ, afin d'extraire ces valeurs remarquables.
  • Après l'affectation de la variable la_base, ajouter les instructions suivantes :
Set ligne = la_base.OpenRecordset('SELECT MAX(images_num) AS borne_max FROM Images', dbOpenDynaset)
ligne.MoveFirst
borne_sup = ligne.Fields('borne_max').Value
ligne.Close

Set ligne = la_base.OpenRecordset('SELECT MIN(images_num) AS borne_min FROM Images', dbOpenDynaset)
ligne.MoveFirst
borne_inf = ligne.Fields('borne_min').Value
ligne.Close

MsgBox borne_sup & '-' & borne_inf


Comme nous le faisons désormais habituellement, nous exploitons la méthode OpenRecordset de l'objet de base de données afin d'exécuter une requête sélection. Nous exploitons respectivement les fonctions SQL Max et Min pour lesquelles nous stockons les résultats dans des champs créés à la volée grâce au mot clé As (AS borne_max et AS borne_min). Après nous être positionnés sur l'enregistrement résultant (ligne.MoveFirst), il ne reste plus qu'à questionner le champ ainsi créé pour récupérer la valeur calculée. C'est comme toujours la propriété Fields de l'objet Recordset qui permet d'accéder à la valeur du champ désigné en paramètre. Enfin, le MsgBox permet de réaliser un test temporaire.
  • Enregistrer les modifications, basculer sur le formulaire et l'exécuter,
  • Cliquer sur le bouton de la loupe et désigner par exemple le sous dossier Images,
Requêtes SQL exécutées en VBA Access pour récupérer les valeurs max et min dans la table

Les deux bornes sont correctement récupérées et stockées par le code VBA grâce aux requêtes SQL ainsi exécutées. Si vous ouvrez la table Images, vous en aurez la confirmation.
  • Afficher le formulaire en mode création et revenir dans l'éditeur de code VBA,
Forts de ces enseignements, il est désormais temps d'accéder aux enregistrements des tables afin d'en extraire les informations. Nous devons commencer par récupérer le chemin absolu dans la table Chemin, pour les images archivées dans la table Images. En effet, si nous possédons déjà cette information après avoir cliqué sur le bouton pour désigner un dossier, il n'en est rien à la première ouverture du formulaire.
  • Passer la ligne de la fonction MsgBox en commentaire,
  • Puis, à la suite du code, ajouter les instructions suivantes :
Set ligne = la_base.OpenRecordset('SELECT chemin_adr FROM Chemin', dbOpenDynaset)
ligne.MoveFirst
nom_dossier = ligne.Fields('chemin_adr').Value
ligne.Close

Set ligne = la_base.OpenRecordset('SELECT * FROM Images', dbOpenDynaset)
ligne.MoveFirst
nom_fichier = ligne.Fields('images_fichier').Value
date_creation = ligne.Fields('images_date').Value
titre_fichier = ligne.Fields('images_texte').Value
indice = ligne.Fields('images_num').Value - borne_inf

album.Picture = nom_dossier & '' & nom_fichier
mini1.Picture = nom_dossier & '' & nom_fichier
titre.Caption = titre_fichier
date_cliche.Caption = date_creation


Nous réalisons deux requêtes sélection, habituelles désormais. La première permet de récupérer l'information contenue dans le champ chemin_adr de la table Chemin. Cette adresse est ainsi stockée dans la variable publique nom_dossier, pour concaténation future. La seconde récupère tous les champs (*) de la table Images et pointe sur le premier enregistrement (ligne.MoveFirst), soit sur la première photo archivée dans la table. Pour cet enregistrement, nous stockons la valeur de chaque champ dans des variables respectives.

Puis, grâce à la propriété Picture des contrôles Image, nous affectons le chemin complet (par concaténation) de la première image au centre de l'album et dans la première miniature. Enfin, les propriétés Caption des contrôles Label permettent d'inscrire les informations textuelles récupérées par la requête sélection. Nous développerons plus tard une fonction VBA pour retravailler la présentation du titre qui à ce stade, n'est autre qu'une reproduction identique du nom de fichier.

Il s'agit désormais de récupérer les trois images suivantes, afin de les afficher dans les contrôles de miniatures correspondants. Nous devons donc avancer le pointeur de lecture enregistrement par enregistrement, grâce à la méthode MoveNext de l'objet Recordset. Ensuite, c'est une fois de plus la propriété Fields du même objet qui permettra de récupérer l'information du champ, sur l'enregistrement correctement positionné.
  • A la suite du code de la procédure demarrage, ajouter les instructions suivantes :
ligne.MoveNext
nom_fichier = ligne.Fields('images_fichier').Value
mini2.Picture = nom_dossier & '' & nom_fichier

ligne.MoveNext
nom_fichier = ligne.Fields('images_fichier').Value
mini3.Picture = nom_dossier & '' & nom_fichier

ligne.MoveNext
nom_fichier = ligne.Fields('images_fichier').Value
mini4.Picture = nom_dossier & '' & nom_fichier

ligne.Move (-3)


A chaque fois que nous avançons au travers des enregistrements (ligne.MoveNext), nous prélevons le nom du fichier. Et après reconstruction du chemin complet par concaténation, nous affichons l'image correspondante dans le contrôle dédié grâce à sa propriété Picture. Comme nous nous sommes déplacés de 3 enregistrements vers l'avant, nous n'oublions pas de revenir sur la position de la photo en cours de lecture sur l'album grâce à la méthode Move de l'objet Recordset. Avec la valeur -3 en argument, nous revenons cette fois en arrière de 3 enregistrements.
  • Enregistrer les modifications, basculer sur le formulaire et l'exécuter,
  • Cliquer sur le bouton Parcourir et désigner un dossier d'images,
Chargement des premières images contenues dans un dossier sur formulaire Access par code VBA

Comme vous le constatez, le code VBA Access est parfaitement fonctionnel. La première photo est inscrite au centre de l'album et dans le premier contrôle miniature. Les trois suivantes sont insérées dans les trois autres miniatures respectives. Le pointeur de lecture quant à lui est bien repositionné sur la photo en cours, soit sur la première image, grâce à la méthode Move de l'objet ligne.

Le code complet de la procédure demarrage est le suivant :

Private Sub demarrage()
On Error Resume Next
Dim nom_fichier As String: Dim extension As String
Dim date_creation As String: Dim titre_fichier As String

indice = 0
indice_premiere = 0

Set la_base = Application.CurrentDb
Set ligne = la_base.OpenRecordset('SELECT MAX(images_num) AS borne_max FROM Images', dbOpenDynaset)
ligne.MoveFirst
borne_sup = ligne.Fields('borne_max').Value
ligne.Close

Set ligne = la_base.OpenRecordset('SELECT MIN(images_num) AS borne_min FROM Images', dbOpenDynaset)
ligne.MoveFirst
borne_inf = ligne.Fields('borne_min').Value
ligne.Close
'MsgBox borne_sup & '-' & borne_inf

Set ligne = la_base.OpenRecordset('SELECT chemin_adr FROM Chemin',dbOpenDynaset)
ligne.MoveFirst
nom_dossier = ligne.Fields('chemin_adr').Value
ligne.Close

Set ligne = la_base.OpenRecordset('SELECT * FROM Images',dbOpenDynaset)
ligne.MoveFirst
nom_fichier = ligne.Fields('images_fichier').Value
date_creation = ligne.Fields('images_date').Value
titre_fichier = ligne.Fields('images_texte').Value
indice = ligne.Fields('images_num').Value - borne_inf
album.Picture = nom_dossier & '' & nom_fichier
mini1.Picture = nom_dossier & '' & nom_fichier
titre.Caption = titre_fichier
date_cliche.Caption = date_creation

ligne.MoveNext
nom_fichier = ligne.Fields('images_fichier').Value
mini2.Picture = nom_dossier & '' & nom_fichier

ligne.MoveNext
nom_fichier = ligne.Fields('images_fichier').Value
mini3.Picture = nom_dossier & '' & nom_fichier

ligne.MoveNext
nom_fichier = ligne.Fields('images_fichier').Value
mini4.Picture = nom_dossier & '' & nom_fichier

ligne.Move (-3)
'MsgBox ligne.Fields('images_num').Value
End Sub


Navigation au travers des photos par clics sur les boutons
Il est temps maintenant de rendre disponible la navigation. Un clic sur la flèche orientée à droite doit afficher la photo suivante. Un clic sur la flèche orientée à gauche doit afficher la photo précédente. Mais ce n'est pas tout, il y a deux défis à relever. Premièrement, lorsque la limite est atteinte, première ou dernière photo, la navigation doit reprendre depuis la borne opposée. Deuxièmement, lorsque l'image affichée ne fait plus partie des miniatures proposées, le jeu des images incrustées doit être mis à jour.
  • Afficher le formulaire en mode création,
  • Sélectionner le contrôle btn_suiv (Flèche orientée à droite),
  • Dans l'onglet Evénément de sa feuille de propriétés, cliquer sur le bouton de son événement Au clic,
  • Dans la boîte de dialogue, choisir Générateur de code et valider par Ok,
  • Réitérer le même processus pour le contrôle btn_prec (flèche orientée à gauche),
Nous obtenons ainsi les deux procédures événementielles (btn_suiv_Click et btn_prec_Click) qui déclencheront un code au clic sur ces boutons respectifs. Elles doivent réaliser quasiment le même traitement afin d'offrir la navigation. Seule le sens change. Donc nous proposons de construire une procédure commune qui sera appelée par l'une et l'autre avec un argument distinctif.
  • Créer la procédure navigation avec un paramètre en attente, comme suit :
Private Sub navigation(sens)

End Sub
  • Puis ajouter l'appel dans les deux procédures précédentes comme suit :
Private Sub btn_prec_Click()
navigation ('moins')
End Sub

Private Sub btn_suiv_Click()
navigation ('plus')
End Sub

Private Sub navigation(sens)

End Sub


Dans le premier cas, nous appelons la procédure navigation avec le paramètre moins pour indiquer que nous naviguons vers l'arrière. Dans le second cas, nous lui passons le paramètre plus pour indiquer que nous naviguons vers l'avant. Bien sûr, dans la procédure navigation, il va s'agir de tenir compte de ce paramètre.

Dans la procédure navigation, il s'agit d'incrémenter ou de décrémenter la variable indice selon le cas et en conséquence, de positionner le pointeur de lecture de la connexion ouverte sur le bon enregistrement, afin de prélever l'image. Mais ce n'est pas tout. Lorsque le jeu des quatre vignettes est dépassé, dans un sens ou dans un autre, il doit être réactualisé pour accompagner le flux de la navigation. C'est la raison pour laquelle nous devons commencer par déclarer deux variables qui permettront de vérifier si la position atteinte est un multiple de 4 (4 vignettes).
  • Dans les bornes de la procédure navigation, ajouter les instructions suivantes :
Dim reste As Double: Dim partie As Integer

If (sens = 'plus') Then
indice = indice + 1

If (indice + borne_inf > borne_sup) Then
indice = 0
indice_premiere = 0
ligne.MoveFirst
Else
ligne.MoveNext
End If

Else
indice = indice - 1

If (indice < 0) Then
ligne.MoveLast
indice = borne_sup - borne_inf
indice_premiere = indice - 3
Else
ligne.MovePrevious
End If

End If

nom_fichier = ligne.Fields('images_fichier').Value
date_creation = ligne.Fields('images_date').Value
titre_fichier = ligne.Fields('images_texte').Value
album.Picture = nom_dossier & '' & nom_fichier
titre.Caption = titre_fichier
date_cliche.Caption = date_creation


Dans le cas de la progression vers l'avant (sens = 'plus'), nous incrémentons la variable indice qui était partie de la position 0. Comme cette variable est publique, nous nous assurons que nous n'avons pas dépassé la limite des images proposées (indice + borne_inf > borne_sup). Si tel est le cas, nous réinitialisons les variables et repositionnons le pointeur de lecture de la connexion ouverte sur le premier enregistrement (ligne.MoveFirst), afin de reprendre la navigation depuis la première image. Dans le cas contraire, nous poursuivons la navigation vers l'avant (ligne.MoveNext).

Dans le cas où nous naviguons vers l'arrière, nous décrémentons la variable de position. Nous vérifions que nous ne sommes pas revenus avant la toute première image (indice < 0). Si tel est le cas, nous affectons les variables de position sur les derniers indices et plaçons le pointeur de lecture sur le tout dernier enregistrement (ligne.MoveLast), soit sur la dernière photo. Dans le cas contraire, nous poursuivons la navigation vers l'arrière (ligne.MovePrevious).

Ensuite, comme dans la procédure démarrage, nous accédons aux informations du nouvel enregistrement grâce à la propriété Fields d'un objet Recordset. Puis, nous affectons les différents contrôles du formulaire sur ces nouvelles valeurs.
  • Enregistrer les modifications, basculer sur le formulaire et l'exécuter,
  • Naviguer vers l'avant jusqu'à dépasser la dernière image,
  • Puis, naviguer vers l'arrière jusqu'à revenir avant la première image,
Tout d'abord nous constatons que les images défilent parfaitement dans un sens comme dans l'autre. Ensuite, lorsque nous allons trop loin, la navigation reprend parfaitement à partir de la première image afin de boucler. De la même façon, lorsque nous revenons trop en arrière, la navigation reprend depuis la dernière photo et c'est parfait. En revanche, le jeu des quatre miniatures ne suit pas le mouvement lorsque la dernière d'entre elles est dépassée.
  • Afficher de nouveau le formulaire en mode création,
  • Puis, revenir dans l'éditeur de code,
Si nous passons de la quatrième image (indice de position 3) à la cinquième (indice de position 4), nous devons proposer les miniatures qui suivent. Inversement, si nous revenons de la cinquième (indice 4) à la quatrième (indice 3), nous devons restaurer les précédentes miniatures. D'une manière générale, si l'indice de position vers l'avant est un multiple de quatre, ou que l'indice de position incrémenté, vers l'arrière, est un multiple de quatre, nous devons mettre à jour les vignettes. Pour cela, nous devons tester le reste de la division de l'indice de position par 4. S'il vaut 0, les vignettes doivent être réactualisées.
  • Avant le else de l'instruction conditionnelle (sens = 'plus'), ajouter les lignes suivantes :
partie = Int(indice / 4): reste = indice / 4

If (reste - partie = 0) Then
indice_premiere = indice
nom_fichier = ligne.Fields('images_fichier').Value
mini1.Picture = nom_dossier & '' & nom_fichier

ligne.MoveNext
nom_fichier = ligne.Fields('images_fichier').Value
mini2.Picture = nom_dossier & '' & nom_fichier

ligne.MoveNext
nom_fichier = ligne.Fields('images_fichier').Value
mini3.Picture = nom_dossier & '' & nom_fichier

ligne.MoveNext
nom_fichier = ligne.Fields('images_fichier').Value
mini4.Picture = nom_dossier & '' & nom_fichier

ligne.Move (-3)
End If


Nous stockons la partie entière de la division de l'indice par 4 grâce à la fonction VBA Int que nous avons déjà exploitée lors de maintes formations. Nous stockons le résultat réel de la division, dans la variable reste. Si la différence entre les deux vaut 0 (reste - partie = 0), nous en déduisons que le jeu des miniatures doit être mis à jour. Comme nous progressons vers l'avant, nous allons chercher les miniatures suivantes (ligne.MoveNext). A l'issue, nous n'oublions pas de replacer le pointeur sur l'enregistrement de l'image en cours de lecture afin d'assurer la continuité de la navigation (ligne.Move(-3)). Constatez de même que nous avons pris soin d'initialiser la variable indice_premiere sur l'indice en cours. Cette valeur nous sera utile pour gérer le clic sur les miniatures afin de connaître la progression demandée.

Nous devons faire de même dans le cas de la progression vers l'arrière mais en testant la division de (indice + 1) par 4. Lorsque nous revenons par exemple de la cinquième (4) à la quatrième (3), c'est que nous sommes revenus (3+1) sur le jeu des miniatures précédentes.
  • Dans la branche Else du If, avant le End If, ajouter les lignes suivantes :
partie = Int((indice + 1) / 4): reste =(indice + 1) / 4

If (reste - partie = 0) Then
indice_premiere = indice - 3
nom_fichier = ligne.Fields('images_fichier').Value
mini4.Picture = nom_dossier & '' & nom_fichier

ligne.MovePrevious
nom_fichier = ligne.Fields('images_fichier').Value
mini3.Picture = nom_dossier & '' & nom_fichier

ligne.MovePrevious
nom_fichier = ligne.Fields('images_fichier').Value
mini2.Picture = nom_dossier & '' & nom_fichier

ligne.MovePrevious
nom_fichier = ligne.Fields('images_fichier').Value
mini1.Picture = nom_dossier & '' & nom_fichier

ligne.Move (3)
End If


Le principe est identique mais la progression change. Si le reste de la division est nul (reste - partie = 0), nous devons aller chercher les quatre miniatures précédentes. C'est la raison pour laquelle nous naviguons vers l'arrière grâce à la méthode MovePrevious de l'objet Recordset.
  • Enregistrer les modifications, basculer sur le formulaire et l'exécuter,
  • Naviguer dans les deux sens de manière à constater la mise à jour des vignettes,
Navigation images album photos par déplacements enregistrements grâce à code VBA Access

Comme vous le constatez, notre album photos est de plus en plus fonctionnel. Désormais, les miniatures accompagnent la progression de la navigation afin d'offrir une vue sur l'ensemble des images à suivre.

Néanmoins certaines exceptions ne sont pas encore gérées dans la mesure où nous réalisons des tests sur un cas particulier. Notre dossier images contient un nombre de photos qui est un multiple de 4. Si tel n'était pas le cas, la réactualisation des miniatures causerait une erreur puisque le code VBA Access tente d'accéder aux enregistrements comme s'ils existaient.
  • Afficher de nouveau le formulaire en mode création et revenir dans le code,
Ici nous nous contenterons de gérer les erreurs de manière globale ou plutôt de les ignorer. Il serait opportun, dans un prochain volet de réaliser une gestion plus fine afin d'adapter le visuel sur les miniatures non considérées.
  • Au début de la procédure navigation, ajouter l'instruction suivante :
On Error Resume Next

Le code complet de la procédure navigation est le suivant :

Private Sub navigation(sens)
On Error Resume Next
Dim reste As Double: Dim partie As Integer

If (sens = 'plus') Then
indice = indice + 1
If (indice + borne_inf > borne_sup) Then
indice = 0
indice_premiere = 0
ligne.MoveFirst
Else
ligne.MoveNext
End If

partie = Int(indice / 4): reste = indice / 4
'MsgBox (partie & '-' & reste)
If (reste - partie = 0) Then
indice_premiere = indice
nom_fichier = ligne.Fields('images_fichier').Value
mini1.Picture = nom_dossier & '' & nom_fichier

ligne.MoveNext
nom_fichier = ligne.Fields('images_fichier').Value
mini2.Picture = nom_dossier & '' & nom_fichier

ligne.MoveNext
nom_fichier = ligne.Fields('images_fichier').Value
mini3.Picture = nom_dossier & '' & nom_fichier

ligne.MoveNext
nom_fichier = ligne.Fields('images_fichier').Value
mini4.Picture = nom_dossier & '' & nom_fichier

ligne.Move (-3)
End If

Else
indice = indice - 1
If (indice < 0) Then
ligne.MoveLast
indice = borne_sup - borne_inf
indice_premiere = indice - 3
Else
ligne.MovePrevious
End If

partie = Int((indice + 1) / 4): reste = (indice + 1) / 4
'MsgBox (partie & '-' & reste)

If (reste - partie = 0) Then
indice_premiere = indice - 3
nom_fichier = ligne.Fields('images_fichier').Value
mini4.Picture = nom_dossier & '' & nom_fichier

ligne.MovePrevious
nom_fichier = ligne.Fields('images_fichier').Value
mini3.Picture = nom_dossier & '' & nom_fichier

ligne.MovePrevious
nom_fichier = ligne.Fields('images_fichier').Value
mini2.Picture = nom_dossier & '' & nom_fichier

ligne.MovePrevious
nom_fichier = ligne.Fields('images_fichier').Value
mini1.Picture = nom_dossier & '' & nom_fichier

ligne.Move (3)
End If
End If

nom_fichier = ligne.Fields('images_fichier').Value
date_creation = ligne.Fields('images_date').Value
titre_fichier = ligne.Fields('images_texte').Value
album.Picture = nom_dossier & '' & nom_fichier
titre.Caption = titre_fichier
date_cliche.Caption = date_creation
End Sub




Navigation par clic sur les miniatures d'images
Pour parfaire l'album photos, nous souhaitons proposer à l'utilisateur de visualiser directement l'une des miniatures cliquées. Nous devons être en mesure, entre autres, de recaler le pointeur de lecture de la connexion sur le bon enregistrement afin d'assurer la continuité de la navigation. C'est ici qu'entre en jeu la variable indice_premiere que nous avons judicieusement affectée dans la précédente procédure. Mais avant cela, nous devons palier un souci de taille. Nous travaillons depuis le début sur une connexion que nous ne fermons jamais. C'est pourquoi, nous proposons de vider tous les objets de base de données à la fermeture du formulaire, puisque ces variables sont publiques.
  • Afficher le formulaire en mode création,
  • Dans la liste déroulante de la feuille de propriétés, choisir l'objet Formulaire,
  • Cliquer sur le bouton de son événement Sur fermeture,
  • Dans la boîte de dialogue, choisir Générateur de code et valider par Ok,
  • Entre les bornes de la procédure ainsi générée, ajouter les instructions suivantes :
ligne.Close
la_base.Close
Set ligne = Nothing
Set la_base = Nothing


Ainsi à la fermeture du formulaire et donc de l'album photos, nous fermons les connexions ouvertes et vidons les objets de base de données.
  • Grâce à l'onglet Evénement de la feuille de propriétés, créer les quatre procédures événementielles qui déclencheront un code au clic sur les miniatures respectives, soit :
Private Sub mini1_Click()

End Sub

Private Sub mini2_Click()

End Sub

Private Sub mini3_Click()

End Sub

Private Sub mini4_Click()

End Sub


Dans chaque procédure, à chaque clic sur une miniature, nous devons calculer l'écart entre la position en cours et la position de la première des miniatures, en la réajustant sur la position de la miniature cliquée.
  • Entre les bornes de la procédure mini1_Click, ajouter les instructions suivantes :
Dim difference As Integer

album.Picture = mini1.Picture
difference = indice - indice_premiere

If (difference > 0) Then
For i = 1 To difference - 1
ligne.MovePrevious
indice = indice - 1
Next i
navigation ('moins')
Else
difference = -difference
For i = 1 To difference - 1
ligne.MoveNext
indice = indice + 1
Next i
navigation ('plus')
End If


Très simplement, nous commençons par affecter l'image contenue dans le contrôle mini1 au contrôle album, soit au milieu du formulaire et ce, grâce à leur propriété Picture. Ensuite pour connaître le sens de la navigation à réajuster, nous calculons la différence entre le dernier indice connu et celui de la position de la première miniature (difference = indice - indice_premiere). Comme c'est elle-même qui a été cliquée, nous avons l'écart précis à parcourir pour recaler le pointeur sur les enregistrements.

Si cette valeur est positive (difference > 0), c'est que nous revenons vers l'arrière. Dans ce cas, nous utilisons une boucle pour revenir (ligne.MovePrevious) juste avant la position à atteindre (For i = 1 To difference - 1). En effet, dans la foulée, nous appelons la procédure navigation avec le paramètre moins. Et cette dernière va décrémenter du pas manquant pour rejoindre la position calculée.

Dans le cas contraire, sur la valeur positive du calcul, nous progressons juste avant la position à atteindre par la méthode MoveNext dans la même boucle. Puis, nous appelons la procédure navigation avec le paramètre plus qui se charge de la dernière incrémentation et de la gestion qui suit.
  • Selon le même principe, coder les trois procédures événementielles restantes, comme suit :
Private Sub mini2_Click()
Dim difference As Integer

album.Picture = mini2.Picture
difference = indice - indice_premiere - 1

If (difference > 0) Then
For i = 1 To difference - 1
ligne.MovePrevious
indice = indice - 1
Next i
navigation ('moins')
Else
difference = -difference
For i = 1 To difference - 1
ligne.MoveNext
indice = indice + 1
Next i
navigation ('plus')
End If
End Sub

Private Sub mini3_Click()
Dim difference As Integer

album.Picture = mini3.Picture
difference = indice - indice_premiere - 2

If (difference > 0) Then
For i = 1 To difference - 1
ligne.MovePrevious
indice = indice - 1
Next i
navigation ('moins')
Else
difference = -difference
For i = 1 To difference - 1
ligne.MoveNext
indice = indice + 1
Next i
navigation ('plus')
End If
End Sub

Private Sub mini4_Click()
Dim difference As Integer

album.Picture = mini4.Picture
difference = indice - indice_premiere - 3

If (difference > 0) Then
For i = 1 To difference - 1
ligne.MovePrevious
indice = indice - 1
Next i
navigation ('moins')
Else
difference = -difference
For i = 1 To difference - 1
ligne.MoveNext
indice = indice + 1
Next i
navigation ('plus')
End If
End Sub


Le code est le même. Seul le calcul de la différence s'adapte en fonction de la miniature cliquée. Dans le cas de la quatrième par exemple (indice - indice_premiere - 3), nous prenons en compte son indice de position (3), afin de réajuster la distance à parcourir pour rejoindre la vignette à afficher.
  • Enregistrer les modifications, basculer sur le formulaire et l'exécuter,
  • Naviguer avec les boutons puis cliquer sur une miniature,
  • Cliquer de nouveau sur les flèches de navigation,
Comme vous le constatez, l'album fonctionne parfaitement. Lorsqu'une miniature est cliquée, sa photo est affichée au centre de l'album et la navigation reprend depuis ce nouvel emplacement, dans un sens comme dans l'autre. Il nous resterait à ajouter une gestion des exceptions, lorsque le nombre des photos ne correspond pas à unmultiple de quatre (If (borne_inf + indice - indice_premiere + 1 > borne_sup) Then Exit Sub). L'application reste donc à parfaire.

Traitement des chaînes de caractères en Visual Basic
Nous proposons un traitement très simple à l'aide d'une fonction qui retournera la chaîne purgée. Nous souhaitons remplacer les tirets par des espaces et supprimer l'extension. Ainsi, le texte affiché au-dessus de la photo sera plus clair en tant que descriptif.
  • Créer la fonction traite_titre comme suit :
Function traite_titre(texte)
Dim position As Integer

position = InStrRev(texte, '.')
texte = Mid(texte, 1, position - 1)
texte = Replace(texte, '-', ' ')

traite_titre = texte
End Function


Grâce à la fonction VBA InStrRev, nous mémorisons la position de la dernière occurrence du point, correspondant donc au début de l'extension du fichier. Le fonction Mid que nous avions abordée dans l'importation de données en VBA Excel, permet alors de prélever le texte jusqu'à cette extension en l'excluant. Puis nous exploitons la fonction VBA Replace afin de remplacer tous les tirets par un espace. Nous affectons ensuite le nom de la fonction à la chaine ainsi retraitée. Par cette méthode la valeur est retournée à la procédure qui l'appelle, comme nous l'avait appris la formation pour créer des fonctions de calcul en VBA Excel.

Il ne reste plus qu'à l'appeler dans les procédures navigation et demarrage au moment de l'inscription de l'information dans le contrôle Label :

titre.Caption = traite_titre(titre_fichier)

Notre application pour visionner les images est terminée. Encore une fois, elle mériterait d'intégrer la gestion des exceptions au clic sur les miniatures, lorsque les capacités sont dépassées. De même, il serait intéressant d'envisager une mise en valeur de la miniature active par les bordures, comme nous l'avions fait pour l'album photos Web en Javascript. Mais comme le plus compliqué a été traité ici, nous le laissons volontiers de côté, afin que chacun puisse adapter les dernières retouches à sa guise.

 
Sur Facebook
Sur G+
Sur Youtube
Les livres
Contact
Mentions légales