I. Avant-propos▲
Ce document a pour but de vous montrer comment concevoir une petite calculatrice sous Access.
En effet, vous pouvez appeler la calculatrice de Windows, mais cette opération vous oblige à vérifier si elle n'est pas déjà lancée, auquel cas, il vous faut l'afficher au premier plan (donc, être à l'aise les API Windows).
Là, aucun problème de ce côté puisqu'il s'agit d'un formulaire et seules les fonctions de base d'ouverture (DoCmd.OpenForm) et de fermeture (DoCmd.Close) sont nécessaires.
I-A. Remerciements▲
Je tiens à remercier toutes celles et tous ceux qui ont participé à la relecture de ce document en y incluant leurs remarques.
I-B. Contact▲
Pour tous renseignements complémentaires, veuillez me contacter directement (Argyronet) par MP.
II. Présentation du projet▲
L'objectif de ce tutoriel est de vous montrer qu'il est possible de concevoir une petite calculatrice d'appoint parfaitement opérationnelle dans votre application Access.
Cette calculatrice offre toutes les fonctions de base y compris la mémoire (+/-) avec un convertisseur euros/francs en temps réel au fur et à mesure des entrées et résultats affichés. Bon, je vous l'accorde, ce n'est pas forcément très utile, mais il s'avère que son usage devient vite pratique si des boutons permettant de l'appeler au moment voulu sont correctement posés dans vos formulaires.
Autre avantage, les calculs trouvés, en dehors du fait que j'ai prévu une routine de copier/coller automatique, peuvent se trouver recopiés dans une zone de texte d'un formulaire parent ou plutôt du formulaire appelant par la ligne de code suivante :
Forms!NomDuFormulaireParent.NomDuContrôle = Me!txtLCDDisplayEURoù bien entendu, txtLCDDisplayEUR est le nom du contrôle affichant le résultat dans la calculatrice ;
(nous verrons les noms des contrôles un peu plus loin).
III. Présentation du formulaire exemple▲
Réaliser une calculatrice d'un point de vue graphique n'est pas très difficile en soi ; il suffit de s'inspirer de sa calculatrice de bureau ou d'une photo idoine représentative…
Le reste se réalise en fonction de vos goûts et en fonction des besoins. Dans ce tutoriel, la calculatrice possède un certain nombre de boutons associés à différentes fonctionnalités et vous n'êtes pas tenu de les greffer tous.
III-A. Le formulaire en mode formulaire▲
Vous pouvez constater que ce formulaire est volontairement dépourvu du sélecteur et du diviseur d'enregistrements.
J'ai également pour les mêmes raisons d'esthétique, supprimé les boutons de déplacement.
Une fois appelé, il s'ouvre au premier plan en mode Modal, c'est-à-dire, que vous ne pouvez pas intervenir sur le formulaire qui l'a ouvert tant que celui-ci reste affiché.
Comme je suis un peu maniaque, j'ai greffé à la calculatrice un petit bouton permettant à volonté de changer cet état Modal en non Modal pour des raisons pratiques.
Le formulaire en mode formulaire
Pour le côté graphique, le formulaire voit sa propriété Image affectée à une image dégradée d'un bleu violet tirant en un écru grisonnant, charte graphique adoptée à l'époque où j'ai créé l'application qui employait cette calculatrice.
Pour créer cette image ou tout autre arrière-plan de votre convenance, prenez un logiciel de retouche d'images (The Gimp, PaintShop Pro, Photoshop…), mettez en œuvre une forme rectangulaire de 100 x 332 pixels puis appliquez-lui le dégradé désiré si vous avez choisi d'opter pour un tel arrière-plan
Rognez ensuite l'image de telle sorte que sa taille devienne 1 x 332 pixels et dans les propriétés Mode d'affichage de l'image du formulaire, choisissez Échelle ; le rendu du dégradé sera répété sur toute la largeur de la section Détail du formulaire.
Merci à cafeine pour cette remarque…
Sauvez l'image au format BMP ou JPG dans le dossier \Images de votre application.
III-B. Les propriétés du formulaire▲
Liste récapitulative des propriétés à affecter au formulaire
Ce formulaire, pour pouvoir être affiché ainsi, possède donc les propriétés suivantes :
Pour afficher les propriétés du formulaire, cliquez deux fois sur la zone externe gris foncé de votre environnement Access ou bien sur le bouton Propriétés de la barre d'outils.
Cliquez alors sur l'onglet intitulé « Toutes » puis « Événements ».
TRÈS IMPORTANT
N'oubliez pas de définir à Oui, la propriété Aperçu des touches (Key Preview) sans quoi, vous ne pourrez pas utiliser la calculatrice avec le clavier…
IV. Le formulaire en mode création▲
Lorsque l'on passe en Mode Création, vous pouvez remarquer qu'il n'y a pas grandes différences avec le mode formulaire.
Le projet exploite 47 contrôles différents parmi lesquels figurent :
|
|
des Boutons |
|
|
des Zones de texte |
|
|
des Étiquettes |
|
|
des Rectangles |
Vous en trouverez la liste ci-après.
Le formulaire en mode création (Design)
Du fait que la mise en place de ces contrôles nécessite une certaine précision, je vous recommande de ne pas afficher la Grille que vous désactivez dans le menu Affichage.
Petite astuce
Les touches de raccourci Ctrl+Flèche de direction déplacent un contrôle ou un groupe de contrôles au pixel près.
Les touches de raccourci Shift+Flèche de direction agrandissent/rétrécissent un contrôle ou un groupe de contrôles au pixel près.
IV-A. Détail des contrôles utilisés▲
Il y a deux parties à considérer pour la réalisation de ce projet :
- 1/ la partie application ;
- 2/ la partie calculatrice.
IV-A-1. La partie application▲
La partie dite « application » relate ce que je considère comme étant les fonctions externes au formulaire lui-même.
En effet, ces fonctions sont représentées par les trois boutons qui sont respectivement :
- Copier (Ctrl+C) ;
- Coller (Ctrl+V) ;
- Premier plan/Arrière-plan.
Vous pouvez constater que trois rectangles de couleur rouge sont leur support.
J'ai posé ces contrôles tout simplement pour indiquer l'état de l'emploi des boutons :
- Rouge : inactif ou non disponible ;
- Vert : activé ou enclenché.
Dans le code, ces rectangles voient leur propriété BackColor changer en conséquence.
ledCopy.BackColor = RGB(0, 255, 0) ' Vert
ledPaste.BackColor = RGB(255, 0, 0) ' RougeLe 3e bouton quant à lui est un peu particulier parce qu'il masque ou affiche son « conjoint » en conséquence de l'état de la fenêtre :
son conjoint est bien évidemment un autre bouton illustré différemment. J'ai préféré cette méthode plutôt que l'affectation dynamique de l'image (qui nécessite que l'image soit dans un dossier en tant que fichier), mais rien ne vous empêche de faire comme bon vous semble pour ce cas précis.
|
Mode non Modal |
Mode Modal |
|---|---|
|
|
|
|
Mode non Modal : l'accès au formulaire appelant est libre : |
Mode Modal : il est nécessaire de fermer la calculatrice pour avoir la main sur le formulaire appelant : |
IV-A-1-a. Les messages circonstanciels à l'usage du mode d'affichage▲
J'ai greffé au projet deux affichages de message avec la fonction MsgBox() qui annoncent respectivement l'état de la calculatrice selon le mode choisi :
IV-A-1-b. Les boutons des fonctions d'application▲
Vous dessinerez donc trois Rectangles identiques au format 3D enfoncé avec par défaut, la couleur de fond rouge.

Détail des contrôles utilisés pour les actions de Copier/Coller et du Mode fenêtre

Les boutons du mode d'affichage sont superposés ;
- le bouton Mode Modal est au premier plan et possède la propriété Visible définie à True ;
- l'autre bouton Mode non Modal possède bien entendu la propriété Visible définie à False et se trouve sur la couche du milieu.
(Entre le Rectangle et le bouton du premier plan.)

IV-A-2. La partie calculatrice▲
C'est de loin la mise en place la plus délicate, car elle nécessite de la minutie et de la patience…
L'idéal est de dessiner un bouton pour la partie bouton par exemple et de le dupliquer autant de fois que nécessaire, c'est-à-dire 27 fois…
Dans un premier temps, posez-les de façon approximative, mais régulière. L'ajustement se fait soit à l'aide des données de propriétés par les propriétés Haut (Top) et Gauche (Left) ou bien comme précisé ci-avant, à l'aide des raccourcis clavier.
Affectez à chacun d'entre eux la légende appropriée.
Seul le bouton Racine Carrée possède une police Symbole, car je n'avais pas trouvé à l'époque le symbole idoine et j'avais triché avec cette méthode en entrant les caractères Öa.
(Ceci dit, rien ne vous empêche de mettre des images pour chacun des boutons.)
Tous les autres boutons sont définis en police Arial 12 et colorés en noir pour les chiffres et en différentes couleurs pour les fonctions particulières comme l'illustre la première image.
Vous dessinerez en suite les rectangles puis les zones de texte.
=> La zone de texte affichant la valeur en euros - ou plus exactement le résultat trouvé en fait - est définie en Arial 18 Noir avec un fond gris clair et comme Valeur par défaut « 0 ».
=> La zone de texte affichant la valeur en francs est définie en Arial 11 Gris foncé avec un fond gris clair et comme Valeur par défaut « 0 ».
Elles sont toutes les deux justifiées à Gauche.
=> Quatre étiquettes (Label) sont dessinées à gauche de chaque afficheur et recevant respectivement la propriété Intitulé (Caption) avec les caractères € en Arial 16 Italique et F en Arial 10 Italique avec les couleurs Bleu au-dessus du Violet.
=> Deux zones de texte nommées txtPasteData et txtHiddenFinalResult possèdent la propriété Visible à False et sont destinées à recevoir respectivement les informations du presse-papier de façon temporaire et le calcul en cours avant de l'afficher.
IV-A-2-a. Liste des contrôles utilisés avec leur nom et position (en pixels)▲
Je vous recommande (comme je l'ai stipulé dans ce tutoriel) de nommer tous les contrôles, même ceux qui ne sont pas utilisés dans le code.
Cela est bien plus pratique pour vous comme pour tout autre développeur qui reprendrait éventuellement votre projet.
Détail des contrôles utilisés pour les rectangles, zones de texte et images
Vous trouverez ci-dessous, la liste récapitulative des contrôles du formulaire avec leur position en pixels…
Pour ceux d'entre vous qui le souhaitent, vous pouvez très bien mettre en place une procédure de conception du formulaire avec les méthodes Add de l'objet Control ; de ce fait, vous n'aurez plus qu'un minimum de travail à faire…
Tableau récapitulatif des contrôles utilisés avec leur position
|
Objet |
Type de contrôle |
Nom du contrôle |
Position horizontale |
Position verticale |
Largeur |
Hauteur |
|---|---|---|---|---|---|---|
|
Bouton 0 |
104 |
btn0 |
x:375 |
y:3150 |
w:945 |
h:397 |
|
Bouton 1 |
104 |
btn1 |
x:375 |
y:2640 |
w:450 |
h:397 |
|
Bouton 2 |
104 |
btn2 |
x:877 |
y:2640 |
w:450 |
h:397 |
|
Bouton 3 |
104 |
btn3 |
x:1366 |
y:2640 |
w:450 |
h:397 |
|
Bouton 4 |
104 |
btn4 |
x:375 |
y:2130 |
w:450 |
h:397 |
|
Bouton 5 |
104 |
btn5 |
x:877 |
y:2130 |
w:450 |
h:397 |
|
Bouton 6 |
104 |
btn6 |
x:1366 |
y:2130 |
w:450 |
h:397 |
|
Bouton 7 |
104 |
btn7 |
x:375 |
y:1635 |
w:450 |
h:397 |
|
Bouton 8 |
104 |
btn8 |
x:877 |
y:1635 |
w:450 |
h:397 |
|
Bouton 9 |
104 |
btn9 |
x:1365 |
y:1635 |
w:450 |
h:397 |
|
Bouton + |
104 |
btnAdd |
x:1920 |
y:2130 |
w:500 |
h:397 |
|
Bouton C |
104 |
btnC |
x:1920 |
y:1635 |
w:500 |
h:397 |
|
Bouton CE |
104 |
btnCE |
x:2505 |
y:1635 |
w:500 |
h:397 |
|
Bouton , |
104 |
BtnDec |
x:1366 |
y:3150 |
w:450 |
h:397 |
|
Bouton ÷ |
104 |
btnDivide |
x:1920 |
y:3675 |
w:500 |
h:397 |
|
Bouton = |
104 |
btnEqual |
x:1366 |
y:3675 |
w:450 |
h:397 |
|
Bouton 1/x |
104 |
btnInvert |
x:2505 |
y:2640 |
w:500 |
h:397 |
|
Bouton MC |
104 |
btnMemClear |
x:3075 |
y:2647 |
w:470 |
h:397 |
|
Bouton M- |
104 |
btnMemLess |
x:3075 |
y:2130 |
w:470 |
h:397 |
|
Bouton M+ |
104 |
btnMemMore |
x:3075 |
y:1635 |
w:470 |
h:397 |
|
Bouton MR |
104 |
btnMemRecall |
x:3075 |
y:3150 |
w:470 |
h:922 |
|
Bouton X |
104 |
btnMult |
x:1920 |
y:3150 |
w:500 |
h:397 |
|
Bouton On/Off |
122 |
btnOnOff |
x:405 |
y:3720 |
w:870 |
h:307 |
|
Bouton % |
104 |
btnPercent |
x:2505 |
y:3150 |
w:500 |
h:397 |
|
Bouton Vx |
104 |
btnSQRT |
x:2505 |
y:2130 |
w:500 |
h:397 |
|
Bouton - |
104 |
btnSubst |
x:1920 |
y:2640 |
w:500 |
h:397 |
|
Bouton X² |
104 |
btnX2 |
x:2505 |
y:3675 |
w:500 |
h:397 |
|
Bouton Copier |
103 |
cmdCopy |
x:2340 |
y:4305 |
w:315 |
h:334 |
|
Bouton Mode non modal |
103 |
cmdNoTopMost |
x:3195 |
y:4305 |
w:330 |
h:330 |
|
Bouton Coller |
103 |
cmdPaste |
x:2768 |
y:4305 |
w:315 |
h:334 |
|
Bouton Mode modal |
103 |
cmdTopMost |
x:3195 |
y:4305 |
w:315 |
h:334 |
|
Rectangle des boutons du bas |
103 |
imgAppButtons |
x:240 |
y:4140 |
w:3390 |
h:630 |
|
Étiquette € bleue |
100 |
lblCurrency01 |
x:150 |
y:300 |
w:255 |
h:465 |
|
Étiquette € violette |
100 |
lblCurrency02 |
x:135 |
y:285 |
w:255 |
h:465 |
|
Étiquette F bleue |
100 |
lblFRF1 |
x:165 |
y:990 |
w:195 |
h:285 |
|
Étiquette F violette |
100 |
lblFRF2 |
x:180 |
y:1005 |
w:195 |
h:285 |
|
Rectangle Copier |
101 |
ledCopy |
x:2295 |
y:4260 |
w:405 |
h:420 |
|
Rectangle Coller |
101 |
ledPaste |
x:2723 |
y:4260 |
w:421 |
h:420 |
|
Rectangle Mode |
101 |
ledTopMost |
x:3151 |
y:4260 |
w:405 |
h:420 |
|
Rectangle grand cadre |
101 |
shpCalc1 |
x:90 |
y:90 |
w:3673 |
h:4822 |
|
Rectangle moyen cadre |
101 |
shpCalc2 |
x:225 |
y:1530 |
w:3405 |
h:3255 |
|
Rectangle cadre de l'afficheur Euro |
101 |
shpEuroDisplayer |
x:405 |
y:165 |
w:3255 |
h:615 |
|
Rectangle cadre de l'afficheur FRF |
101 |
shpFRFDisplayer |
x:405 |
y:960 |
w:3255 |
h:435 |
|
Rectangle du bouton On/Off |
101 |
shpLED |
x:375 |
y:3690 |
w:930 |
h:360 |
|
Rectangle trait de séparation |
101 |
shpSeparator |
x:120 |
y:855 |
w:3630 |
h:45 |
|
Zone de texte Afficheur Euro |
109 |
txtLCDDisplayEUR |
x:465 |
y:225 |
w:3135 |
h:495 |
|
Zone de texte Afficheur FRF |
109 |
txtLCDDisplayFRF |
x:465 |
y:1020 |
w:3135 |
h:315 |
V. Le code de la calculatrice▲
Bien que cela semble être on ne peut plus simple de comprendre les fonctions de base d'une calculatrice, il faut considérer qu'un certain nombre de lignes de code est nécessaire à son bon fonctionnement.
Bien entendu, les fonctions de base d'additions, de soustractions, de multiplications et de divisions restent les plus simples.
L'usage de deux variables tampons qui subissent un changement de résultat en fonction de l'opérateur choisi.
Pour mettre en œuvre les gestions des opérateurs, il y a plusieurs manières de procéder. J'ai, pour ce projet, choisi l'usage d'une variable de type String qui prend la valeur idoine en fonction du fait que j'appuie sur un chiffre, une virgule ou un opérateur…
Cette variable nommée « m_strLastFlagEntry » reçoit les valeurs suivantes :
|
Valeur reçue |
Description |
|---|---|
|
C |
L'utilisateur a appuyé sur C |
|
CE |
L'utilisateur a appuyé sur C |
|
ActionIsOperator |
L'utilisateur a appuyé sur un opérateur |
|
ActionIsNumber |
L'utilisateur a appuyé sur un chiffre |
|
ActionIsNothing |
Aucune action (équivaut à vbNullString) |
V-A. Le projet est constitué de deux modules de code▲
Le code associé à ce projet est réparti dans deux modules ; le premier étant bien entendu le module de classe du formulaire lui-même et l'autre étant un module de projet avec une fonction pour lire dans la base de registre…
V-A-1. Le code du formulaire▲
L'ensemble du code ci-après est à recopier dans le module du formulaire…
Dans l'en-tête sont déclarées des variables de module ainsi qu'une fonction API
''' ************************************************************************************
''' PROJECT NAME : Calculator for MS Access Applications
''' MODULE NAME : frmCalculator
''' AUTHOR : Jean-Philippe AMBROSINO
''' CONTACT : http://cerbermail.com/?wDzGFhHk1g
'''
''' PUBLISHED ON : http://access.developpez.com/
'''
''' ************************************************************************************
Option Compare Database
Option Explicit
Private m_dblFirstEntry As Double
Private m_dblSecondEntry As Double
Private m_blnWithDecimal As Integer
Private m_blnOperationNumber As Integer
Private m_strLastFlagEntry As String
Private m_strFlagOperator As String
Private m_vntTotalDisplayed As Variant
Private m_intMaxDigit As Integer
Private m_dblBufferMemory As Double
Private m_vntFinalResult As Double
Private m_dblEuroValue As Double
Private m_strSeparator As String
Private Declare Sub Sleep Lib "kernel32" (ByVal dwMilliseconds As Long)Les boutons gèrent toujours les mêmes événements
Private Sub btn0_Click()
''' Bouton 0 appuyé
'''------------------------
FlagActionButtons "0"
KeepFocus "btn0"
ConvertToFRF
End Sub
Private Sub btn1_Click()
''' Bouton 1 appuyé
'''------------------------
FlagActionButtons "1"
KeepFocus "btn1"
ConvertToFRF
End Sub
Private Sub btn2_Click()
''' Bouton 2 appuyé
'''------------------------
FlagActionButtons "2"
KeepFocus "btn2"
ConvertToFRF
End Sub
Private Sub btn3_Click()
''' Bouton 3 appuyé
'''------------------------
FlagActionButtons "3"
KeepFocus "btn3"
ConvertToFRF
End Sub
Private Sub btn4_Click()
''' Bouton 4 appuyé
'''------------------------
FlagActionButtons "4"
KeepFocus "btn4"
ConvertToFRF
End Sub
Private Sub btn5_Click()
''' Bouton 5 appuyé
'''------------------------
FlagActionButtons "5"
KeepFocus "btn5"
ConvertToFRF
End Sub
Private Sub btn6_Click()
''' Bouton 6 appuyé
'''------------------------
FlagActionButtons "6"
KeepFocus "btn6"
ConvertToFRF
End Sub
Private Sub btn7_Click()
''' Bouton 7 appuyé
'''------------------------
FlagActionButtons "7"
KeepFocus "btn7"
ConvertToFRF
End Sub
Private Sub btn8_Click()
''' Bouton 8 appuyé
'''------------------------
FlagActionButtons "8"
KeepFocus "btn8"
ConvertToFRF
End Sub
Private Sub btn9_Click()
''' Bouton 9 appuyé
'''------------------------
FlagActionButtons "9"
KeepFocus "btn9"
ConvertToFRF
End Sub
Private Sub btnDec_Click()
''' Bouton , appuyé
'''------------------------
If m_strLastFlagEntry <> "ActionIsNumber" Then
Me!txtLCDDisplayEUR = "0" & m_strSeparator
ElseIf m_blnWithDecimal = False Then
Me!txtLCDDisplayEUR = Me!txtLCDDisplayEUR & m_strSeparator
m_intMaxDigit = m_intMaxDigit + 1
End If
m_blnWithDecimal = True
m_strLastFlagEntry = "ActionIsNumber"
End Sub
Private Sub btnEqual_Click()
''' Bouton = appuyé
'''------------------------
FunctionOperator "="
m_intMaxDigit = 0
KeepFocus "btnEqual"
ConvertToFRF
End SubLes opérations sont gérées par une seule fonction qui est appelée systématiquement
Private Sub btnAdd_Click()
''' Bouton + appuyé
'''------------------------
FunctionOperator "+"
m_intMaxDigit = 0
KeepFocus "btnAdd"
ConvertToFRF
End Sub
Private Sub btnSubst_Click()
''' Bouton - appuyé
'''------------------------
FunctionOperator "-"
m_intMaxDigit = 0
KeepFocus "btnSubst"
ConvertToFRF
End Sub
Private Sub btnDivide_Click()
''' Bouton / appuyé
'''------------------------
FunctionOperator "/"
m_intMaxDigit = 0
KeepFocus "btnDivide"
End Sub
Private Sub btnMult_Click()
''' Bouton X appuyé
'''------------------------
FunctionOperator "*"
m_intMaxDigit = 0
KeepFocus "btnMult"
ConvertToFRF
End SubLes boutons d'annulation annulent le contenu des variables et celui d'extinction change la propriété BackColor puis appelle la fonction KeepCurrentResult.
Private Sub btnC_Click()
''' Bouton C appuyé
'''------------------------
Me!txtLCDDisplayEUR = "0" & m_strSeparator
Me!txtLCDDisplayFRF = "0" & m_strSeparator
m_blnWithDecimal = False
m_strLastFlagEntry = "C"
KeepFocus "btnC"
m_intMaxDigit = 0
m_dblEuroValue = 0
End Sub
Private Sub btnCE_Click()
''' Bouton CE appuyé
'''------------------------
m_strLastFlagEntry = "CE"
KeepFocus "btnCE"
End Sub
Private Sub btnOnOff_Click()
''' Bouton On/Off appuyé
'''------------------------
btnOnOff.Caption = "O F F"
btnOnOff.ForeColor = RGB(255, 0, 0)
shpLED.BackColor = RGB(255, 0, 0)
DoEvents
Call Sleep(1000)
KeepCurrentResult
End SubLes fonctions spéciales exploitent un code aussi simple à comprendre que les opérations de base vues ci-avant.
Private Sub btnSQRT_Click()
''' Bouton Vx appuyé
'''------------------------
m_dblFirstEntry = Val(Me!txtLCDDisplayEUR)
If m_dblFirstEntry < 0 Then
MsgBox "La racine carrée d´un nombre négatif n´est pas valable !" & vbCrLf & _
vbCrLf & "Le calcul est impossible", 48, "Erreur"
Else
m_dblFirstEntry = Sqr(m_dblFirstEntry)
m_intMaxDigit = 0
End If
Me!txtLCDDisplayEUR = str$(m_dblFirstEntry)
m_blnOperationNumber = 1
m_strLastFlagEntry = "ActionIsOperator"
m_strFlagOperator = vbNullString
ConvertToFRF
End Sub
Private Sub btnInvert_Click()
''' Bouton 1/x appuyé
'''------------------------
m_dblFirstEntry = Val(Me!txtLCDDisplayEUR)
If m_dblFirstEntry = 0 Then
MsgBox "Cette opération est une division par zéro !" & vbCrLf & vbCrLf & _
"Le calcul est impossible", 48, "Erreur"
Else
m_dblFirstEntry = 1 / m_dblFirstEntry
m_intMaxDigit = 0
End If
Me!txtLCDDisplayEUR = str$(m_dblFirstEntry)
m_blnOperationNumber = 1
m_strLastFlagEntry = "ActionIsOperator"
m_strFlagOperator = vbNullString
KeepFocus "btnInvert"
ConvertToFRF
End Sub
Private Sub btnPercent_Click()
''' Bouton % appuyé
'''------------------------
Me!txtLCDDisplayEUR = Format$(m_dblFirstEntry * Val(Me!txtLCDDisplayEUR) / 100)
m_intMaxDigit = 0
KeepFocus "btnPercent"
ConvertToFRF
End Sub
Private Sub btnX2_Click()
''' Bouton X² appuyé
'''------------------------
On Error GoTo L_ErrOverFow
m_dblFirstEntry = Val(Me!txtLCDDisplayEUR)
m_dblFirstEntry = m_dblFirstEntry * m_dblFirstEntry
m_intMaxDigit = 0
Me!txtLCDDisplayEUR = str$(m_dblFirstEntry)
m_blnOperationNumber = 1
m_strLastFlagEntry = "ActionIsOperator"
m_strFlagOperator = vbNullString
ConvertToFRF
L_ExOverFow:
Exit Sub
L_ErrOverFow:
MsgBox "Vous avez atteint la capacité maximum de calcul du processeur...", 48, "Calcul dépassé"
Resume L_ExOverFow
End SubLa gestion de la mémoire s'effectue tout simplement avec une variable de module.
Private Sub btnMemClear_Click()
''' Bouton MC appuyé
'''------------------------
If m_dblBufferMemory Then
If MsgBox("Effacer le contenu de la mémoire maintenant ?", 52, "Effacer") = 6 Then
m_dblBufferMemory = 0
End If
End If
End Sub
Private Sub btnMemLess_Click()
''' Bouton M- appuyé
'''------------------------
m_dblBufferMemory = m_dblBufferMemory - Me!txtLCDDisplayEUR
End Sub
Private Sub btnMemMore_Click()
m_dblBufferMemory = m_dblBufferMemory + Me!txtLCDDisplayEUR
End Sub
Private Sub btnMemRecall_Click()
''' Bouton MR appuyé
'''------------------------
Me!txtLCDDisplayEUR = vbNullString
Me!txtLCDDisplayEUR = str(m_dblBufferMemory)
End SubAu sein du formulaire sont gérés les événements et plus particulièrement celui qui est généré par le clavier.
Private Sub Form_KeyDown(KeyCode As Integer, Shift As Integer)
''' Gestion des touches du clavier
'''------------------------
Select Case KeyCode
Case 96, 48: btn0_Click
Case 97, 49: btn1_Click
Case 98, 50: btn2_Click
Case 99, 51: btn3_Click
Case 100, 52: btn4_Click
Case 101, 53: btn5_Click
Case 102, 54: btn6_Click
Case 103, 55: btn7_Click
Case 104, 56: btn8_Click
Case 105, 57: btn9_Click
Case 110: btnDec_Click
Case 111: btnDivide_Click
Case 106: btnMult_Click
Case 107: btnAdd_Click
Case 109: btnSubst_Click
Case 46: btnCE_Click
Case 13: btnEqual_Click
Case 27: InitializeCalculator: btnC_Click
End Select
End Sub
Private Sub Form_Load()
''' Chargement... (intialise le séparateur de décimales)
'''------------------------
m_strSeparator = GetSystemDecimalSeparator()
btnC.SetFocus
End Sub
Private Sub txtLCDDisplayEUR_KeyPress(KeyAscii As Integer)
''' Effet de frappe dans l´afficheur annulé
'''------------------------
KeyAscii = 0
End SubLes fonctions de conversions sont nécessaires notamment à cause du séparateur de décimales du système, mais également pour la conversion en francs de la valeur en euros.
Private Sub ConvertToFRF()
''' Procédure de conversion en francs
'''----------------------------------------
Const EURO As Double = 6.55957
Dim dblResultFound As Double
dblResultFound = ConvertToDouble(Me!txtLCDDisplayEUR)
If dblResultFound = 0 Then
Exit Sub
End If
m_dblEuroValue = dblResultFound
dblResultFound = dblResultFound * EURO
dblResultFound = RoundNumber(dblResultFound, 2)
Me!txtLCDDisplayFRF = str$(dblResultFound)
End Sub
Private Function ConvertToDouble(ByVal varValue As Variant) As Double
''' Fonction de conversion en double
'''----------------------------------------
Const CHR_DECIMALSEP_COMMA As String = ","
Const CHR_DECIMALSEP_DOT As String = "."
Dim strNumberSeparator As String
Dim strTempNumber As String
Dim dblResult As Double
If InStr(1, varValue, CHR_DECIMALSEP_COMMA) Then
strTempNumber = Replace(varValue, CHR_DECIMALSEP_COMMA, m_strSeparator, 1, -1, vbTextCompare)
ElseIf InStr(1, varValue, CHR_DECIMALSEP_DOT) Then
strTempNumber = Replace(varValue, CHR_DECIMALSEP_DOT, m_strSeparator, 1, -1, vbTextCompare)
Else
strTempNumber = varValue
End If
dblResult = CDbl(strTempNumber)
ConvertToDouble = dblResult
End Function
Private Function RoundNumber(ByVal NValue As Double, ByVal nDigits As Integer) As Double
''' Fonction d´arrondi à n décimales
'''------------------------------------------------
RoundNumber = Int(NValue * (10 ^ nDigits) + 0.5) / (10 ^ nDigits)
End FunctionLes fonctions des actions servent à faire en sorte que les calculs soient effectués selon les touches appuyées.
Private Sub FunctionOperator(ByVal Operator As String)
''' Fonction déterminant l´opérateur choisi
'''----------------------------------------
If m_strLastFlagEntry = "ActionIsNumber" Then
m_blnOperationNumber = m_blnOperationNumber + 1
End If
If m_blnOperationNumber = 1 Then
m_dblFirstEntry = ConvertToDouble(Me!txtLCDDisplayEUR)
ElseIf m_blnOperationNumber = 2 Then
m_dblSecondEntry = ConvertToDouble(Me!txtLCDDisplayEUR)
Select Case m_strFlagOperator
Case "+"
m_dblFirstEntry = m_dblFirstEntry + m_dblSecondEntry
Case "-"
m_dblFirstEntry = m_dblFirstEntry - m_dblSecondEntry
Case "*"
m_dblFirstEntry = m_dblFirstEntry * m_dblSecondEntry
Case "/"
If m_dblSecondEntry = 0 Then
MsgBox "Cette opération est une division par zéro !" & vbCrLf & vbCrLf & _
"Le calcul est impossible", 48, "Erreur"
Else
m_dblFirstEntry = m_dblFirstEntry / m_dblSecondEntry
End If
Case "="
m_dblFirstEntry = m_dblSecondEntry
End Select
Me!txtLCDDisplayEUR = m_dblFirstEntry
m_blnOperationNumber = 1
End If
m_strLastFlagEntry = "ActionIsOperator"
m_strFlagOperator = Operator
End Sub
Sub KeepFocus(ByVal CtrlName As String)
''' Fonction permettant de garder le focus
'''----------------------------------------
Dim oForm As Form
Dim oCtl As Control
Set oForm = Form
Set oCtl = oForm.Controls(CtrlName)
oCtl.SetFocus
Set oCtl = Nothing
Set oForm = Nothing
End Sub
Private Sub InitializeCalculator()
''' Procédure d'initialisation
'''----------------------------------------
m_blnWithDecimal = False
m_blnOperationNumber = 0
m_strLastFlagEntry = "ActionIsNothing"
m_strFlagOperator = vbNullString
End Sub
Private Sub FlagActionButtons(ByVal EnterNum As String)
''' Procédure de gestion des actions
'''----------------------------------------
If m_strLastFlagEntry <> "ActionIsNumber" Then
Me!txtLCDDisplayEUR = vbNullString
Me!txtHiddenFinalResult = 0
m_blnWithDecimal = False
End If
If m_intMaxDigit > 13 Then
Beep
Exit Sub
End If
Me!txtLCDDisplayEUR = Me!txtLCDDisplayEUR + LTrim$(EnterNum)
Me!txtHiddenFinalResult = ConvertToDouble(Me!txtLCDDisplayEUR)
m_intMaxDigit = m_intMaxDigit + 1
m_strLastFlagEntry = "ActionIsNumber"
End Sub
Private Sub KeepCurrentResult()
''' Procédure de mise en mémoire du résultat
'''-----------------------------------------
Const MSG_CLIPBOARD_01 = "Votre résultat "
Const MSG_CLIPBOARD_02 = " est copié en mémoire..."
Const MSG_CLIPBOARD_03 = "Vous pourrez le coller en utilisant les touches Ctrl+V !"
Const MSG_CLIPBOARD_00 = "Copie"
Const MMSG_KEEP_RESULT_01 = "Voulez-vous conserver les résultats de votre calcul pour un usage ultérieur ?"
Const MMSG_KEEP_RESULT_00 = "Conserver le résultat de "
m_vntTotalDisplayed = Val(Me!txtLCDDisplayEUR)
If m_vntTotalDisplayed = 0 Then GoTo L_ExCopyFail
If MsgBox(MMSG_KEEP_RESULT_01, 36, MMSG_KEEP_RESULT_00 & txtLCDDisplayEUR) = 6 Then
DoCmd.GoToControl "txtLCDDisplayEUR"
m_vntFinalResult = ConvertToDouble(Me!txtLCDDisplayEUR)
txtHiddenFinalResult.Visible = True
Me!txtHiddenFinalResult = m_vntFinalResult
txtHiddenFinalResult.SetFocus
txtHiddenFinalResult.SelStart = 0
txtHiddenFinalResult.SelLength = Len(Me!txtHiddenFinalResult)
On Error GoTo L_ErrCopyFail
DoCmd.RunCommand acCmdCopy
MsgBox MSG_CLIPBOARD_01 & m_vntFinalResult & MSG_CLIPBOARD_02 & vbCrLf & _
vbCrLf & MSG_CLIPBOARD_03, , MSG_CLIPBOARD_00
End If
L_ExCopyFail:
DoCmd.Close 2, "frmCalculator"
Exit Sub
L_ErrCopyFail:
MsgBox "Désolé, la copie a échoué...", 48, "Copie"
Resume L_ExCopyFail
End SubLes fonctions externes (facultatives) permettent la gestion du Copier/Coller et du mode d'affichage.
Private Sub cmdCopy_Click()
''' Bouton Copier appuyé
'''------------------------
m_vntTotalDisplayed = Val(Me!txtLCDDisplayEUR)
If m_vntTotalDisplayed = 0 Then Exit Sub
CopyResultToClipboard
ledCopy.BackColor = RGB(0, 255, 0)
ledPaste.BackColor = RGB(255, 0, 0)
End Sub
Private Sub cmdPaste_Click()
''' Bouton Coller appuyé
'''------------------------
Dim dblPastedValue
txtPasteData.Visible = True
txtPasteData.SetFocus
On Error Resume Next
Application.RunCommand acCmdPaste
dblPastedValue = Me!txtPasteData
If dblPastedValue <> 0 And IsNumeric(dblPastedValue) Then
Me!txtLCDDisplayEUR = dblPastedValue
ConvertToFRF
ledCopy.BackColor = RGB(255, 0, 0)
ledPaste.BackColor = RGB(0, 255, 0)
Else
MsgBox "Aucun résultat valide n´est applicable à l´afficheur de la calculatrice !", _
48, "Donnée non numérique ou nulle"
End If
txtPasteData.Visible = False
End Sub
Private Sub cmdNoTopMost_Click()
''' Bouton Mode Non Modal appuyé
'''------------------------
If MsgBox("La calculatrice est affichée en mode verrouillé et vous devrez l'éteindre pour intervenir " & _
"sur la fenêtre en arrière-plan...", 49, "Mode d´affichage") = 1 Then
cmdTopMost.Visible = True
Me.Modal = True
cmdNoTopMost.Visible = False
ledTopMost.BackColor = RGB(255, 0, 0)
End If
End Sub
Private Sub cmdTopMost_Click()
''' Bouton Mode Modal appuyé
'''------------------------
If MsgBox("La calculatrice n´est plus en mode exclusif de fenêtre et vous pouvez intervenir " & _
"sur la fenêtre en arrière-plan...", 65, "Mode d´affichage") = 1 Then
cmdNoTopMost.Visible = True
Me.Modal = False
cmdTopMost.Visible = False
ledTopMost.BackColor = RGB(0, 255, 0)
End If
End Sub
Private Sub CopyResultToClipboard()
''' Procédure de gestion du presse-papier
'''----------------------------------------
m_vntTotalDisplayed = Val(Me!txtLCDDisplayEUR)
If m_vntTotalDisplayed = 0 Then GoTo L_ExCopyFail
DoCmd.GoToControl "txtLCDDisplayEUR"
m_vntFinalResult = ConvertToDouble(Me!txtLCDDisplayEUR)
txtHiddenFinalResult.Visible = True
Me!txtHiddenFinalResult = m_vntFinalResult
txtHiddenFinalResult.SetFocus
txtHiddenFinalResult.SelStart = 0
txtHiddenFinalResult.SelLength = Len(Me!txtHiddenFinalResult)
On Error GoTo L_ErrCopyFail
DoCmd.RunCommand acCmdCopy
btnOnOff.SetFocus
txtHiddenFinalResult.Visible = False
L_ExCopyFail:
Exit Sub
L_ErrCopyFail:
MsgBox "Désolé, la copie a échoué...", 48, "Copie"
Resume L_ExCopyFail
End Sub
Private Function GetSystemDecimalSeparator() As String
''' Fonction retournant le séparateur du système
'''---------------------------------------------
Const HKEY_CPANEL_INTLKEYS As String = "Control Panel\International"
Dim strDecimal As String
strDecimal = fRegistryGetKeyValue(rootHKeyCurrentUser, HKEY_CPANEL_INTLKEYS, "sDecimal")
If VarType(strDecimal) = vbError Then
Exit Function
End If
GetSystemDecimalSeparator = strDecimal
End FunctionV-A-2. Le code du Module basRegistry▲
''' ************************************************************************************
''' PROJET : Calculator for MS Access Applications
''' MODULE : basRegistry
''' AUTEUR : Jean-Philippe AMBROSINO
''' CONTACT : http://cerbermail.com/?wDzGFhHk1g
'''
''' PUBLIÉ SUR : http://access.developpez.com/
'''
''' ************************************************************************************
Option Compare Database
Option Explicit
Private Declare Function RegCloseKey Lib "advapi32.dll" (ByVal lngHKey As Long) _
As Long
Private Declare Function RegOpenKeyEx Lib "advapi32.dll" Alias "RegOpenKeyExA" _
(ByVal lngHKey As Long, ByVal lpSubKey As String, ByVal ulOptions As Long, _
ByVal samDesired As Long, phkResult As Long) As Long
Private Declare Function RegQueryValueExString Lib "advapi32.dll" Alias _
"RegQueryValueExA" (ByVal lngHKey As Long, ByVal lpValueName As String, ByVal _
lpReserved As Long, lpType As Long, ByVal lpData As String, lpcbData As Long) _
As Long
Private Declare Function RegQueryValueExLong Lib "advapi32.dll" Alias _
"RegQueryValueExA" (ByVal lngHKey As Long, ByVal lpValueName As String, ByVal _
lpReserved As Long, lpType As Long, lpData As Long, lpcbData As Long) As Long
Private Declare Function RegQueryValueExBinary Lib "advapi32.dll" Alias _
"RegQueryValueExA" (ByVal lngHKey As Long, ByVal lpValueName As String, ByVal _
lpReserved As Long, lpType As Long, ByVal lpData As Long, lpcbData As Long) As _
Long
Private Declare Function RegQueryValueExNULL Lib "advapi32.dll" Alias _
"RegQueryValueExA" (ByVal lngHKey As Long, ByVal lpValueName As String, ByVal _
lpReserved As Long, lpType As Long, ByVal lpData As Long, lpcbData As Long) As _
Long
Public Enum EnumRegistryRootKeys
rootHKeyClassesRoot = &H80000000
rootHKeyCurrentUser = &H80000001
rootHKeyLocalMachine = &H80000002
rootHKeyUsers = &H80000003
End Enum
Public Enum EnumRegistryValueType
RRKREGSZ = 1
RRKREGBINARY = 3
RRKREGDWORD = 4
End Enum
Public Const HKEYLOCALMACHINE As String = "HKEY_LOCAL_MACHINE"
Public Const HKEYCURRENTUSER As String = "HKEY_CURRENT_USER"
Private Const mcregOptionNonVolatile = 0
Private Const MCREGERRORNONE = 0
Private Const MCREGKEYALLACCESS = &H3F
Private Const MCREGKEYQUERYVALUE = &H1
Public Function fRegistryGetKeyValue(ByVal eRootKey As EnumRegistryRootKeys, ByVal strKeyName As String, _
ByVal strValueName As String) As Variant
''' Fonction permettant la lecture d'une clé du registre
'''----------------------------------------------------
Dim lngRetVal As Long
Dim lngHKey As Long
Dim varValue As Variant
Dim strValueData As String
Dim abytValueData() As Byte
Dim lngValueData As Long
Dim lngValueType As Long
Dim lngDataSize As Long
On Error GoTo L_ErrRegistryOperation
varValue = Empty
lngRetVal = RegOpenKeyEx(eRootKey, strKeyName, 0&, MCREGKEYQUERYVALUE, lngHKey)
If MCREGERRORNONE = lngRetVal Then
lngRetVal = RegQueryValueExNULL(lngHKey, strValueName, 0&, lngValueType, 0&, lngDataSize)
If lngRetVal = MCREGERRORNONE Then
Select Case lngValueType
Case RRKREGSZ:
If lngDataSize > 0 Then
strValueData = String(lngDataSize, 0)
lngRetVal = RegQueryValueExString(lngHKey, strValueName, 0&, lngValueType, _
strValueData, lngDataSize)
If InStr(strValueData, vbNullChar) > 0 Then
strValueData = Mid$(strValueData, 1, InStr(strValueData, vbNullChar) - 1)
End If
End If
If MCREGERRORNONE = lngRetVal Then
varValue = Left$(strValueData, lngDataSize)
Else
varValue = Empty
End If
Case RRKREGDWORD:
lngRetVal = RegQueryValueExLong(lngHKey, strValueName, 0&, _
lngValueType, lngValueData, lngDataSize)
If MCREGERRORNONE = lngRetVal Then
varValue = lngValueData
End If
Case RRKREGBINARY
If lngDataSize > 0 Then
ReDim abytValueData(lngDataSize) As Byte
lngRetVal = RegQueryValueExBinary(lngHKey, strValueName, 0&, _
lngValueType, VarPtr(abytValueData(0)), lngDataSize)
End If
If MCREGERRORNONE = lngRetVal Then
varValue = abytValueData
Else
varValue = Empty
End If
Case Else
lngRetVal = -1
End Select
End If
RegCloseKey (lngHKey)
End If
fRegistryGetKeyValue = varValue
L_ExRegistryOperation:
Erase abytValueData
Exit Function
L_ErrRegistryOperation:
MsgBox "Error: " & Err.Number & ". " & Err.Description, , _
"fRegistryGetKeyValue"
Resume L_ExRegistryOperation
End FunctionVI. Comment cela fonctionne-t-il ?▲
Le fonctionnement global est relativement simple : le formulaire intercepte les séquences de touche par l'événement Form_KeyDown et appelle la procédure associée selon le KeyCode correspondant.
Entrée de chiffres
Quelle que soit la valeur de KeyCode s'il s'agit d'un chiffre, la variable m_strLastFlagEntry prend la valeur appropriée et la fonction FlagActionButtons() est appelée. C'est elle qui régit l'affichage de ce qui est frappé ou cliqué et contrôle que ces séquences ne dépassent pas 13, nombre maximum de chiffres affichables.
Choix d'un opérateur
Quelle que soit la valeur de KeyCode s'il s'agit d'un opérateur, la variable m_strLastFlagEntry prend la valeur appropriée et la fonction FunctionOperator() est appelée. C'est elle qui régit le calcul de ce qui est demandé et contrôle l'erreur potentielle de la division par zéro puis met à jour l'affichage à partir du moment où le paramètre Operator de la fonction est égal à « = ».
Appelle des fonctions spéciales
Quelle que soit la valeur de KeyCode s'il s'agit d'une fonction mathématique spéciale, la variable m_strLastFlagEntry prend la valeur appropriée et chacun des boutons exécute le calcul selon que ce soit :
- une racine carrée : fonction SQR(valeur)
- l'inverse 1/x : opération 1 / valeur
- un pourcentage : opération valeur / 100
- une élévation X² : opération valeur * valeur
Appelle des fonctions de mémoire
Ici, une variable de module nommée « m_dblBufferMemory » sert de buffer pour stocker en plus ou en moins la valeur du résultat courant.
Appelle de fonction de Copier/Coller et de mode d'affichage
=> Copier : cette action appelle la fonction CopyResultToClipboard afin de stocker le résultat dans le presse-papier.
=> Coller : cette action appelle la fonction Application.RunCommand acCmdPaste afin de coller le résultat dans la zone de texte txtLCDDisplayEUR et appelle la fonction ConvertToFRF pour convertir instantanément la valeur en francs.
=> Mode : cette action change la propriété Modal à True ou à False selon le choix et change la visibilité du bouton ainsi que la couleur du rectangle selon le cas.
Les autres opérations
- L'extinction de la calculatrice ferme le formulaire et affiche un message pour conserver le résultat dans le presse-papier.

- L'utilisation de la touche C vide le contenu de la variable m_dblEuroValue et met les afficheurs à 0.
- Pour chacun des boutons, la fonction KeepFocus est appelée de manière à laisser le focus sur le bouton lors de l'utilisation du clavier pour simuler l'usage de la souris.
VII. Comment l'utiliser ?▲
Bien une fois que vous avez terminé la conception et testé en mode formulaire que tout fonctionnait correctement, vous pouvez ouvrir la calculatrice depuis un autre formulaire avec la méthode :
Private Sub cmdShowCalculator()
DoCmd.OpenForm "", acNormal, , , , acDialog
End SubVII-A. Elle est ouverte, mais je ne peux pas la déplacer !!!▲
Si effectivement vous avez, comme je l'ai stipulé, défini la propriété Fen indépendante à Oui, vous n'avez alors plus de barre de titre ; de ce fait, il est impossible de déplacer le formulaire.
La solution consiste alors à ajouter du code sur les événements Mouse_Move des rectangles de la calculatrice
Pour ce faire, vous devez ajouter un Module que vous nommez basAPI par exemple dans lequel vous copiez le code suivant :
Public Declare Function ReleaseCapture Lib "user32" () As Long
Public Declare Function SendMessage Lib "user32" Alias "SendMessageA" _
(ByVal hwnd As Long, ByVal wMsg As Long, ByVal wParam As Long, lParam As Any) As Long
Public Const WM_NCLBUTTONDOWN = &HA1
Public Const HTCAPTION = 2Il vous reste alors à gérer les événements pour chacun des rectangles comme suit :
Private Sub imgAppButtons_MouseMove(Button As Integer, Shift As Integer, X As Single, Y As Single)
MoveMyCalculator Button
End Sub
Private Sub shpCalc1_MouseMove(Button As Integer, Shift As Integer, X As Single, Y As Single)
MoveMyCalculator Button
End Sub
Private Sub shpCalc2_MouseMove(Button As Integer, Shift As Integer, X As Single, Y As Single)
MoveMyCalculator Button
End Sub
Private Sub MoveMyCalculator(Button As Integer)
If Button = 1 Then
ReleaseCapture
SendMessage Me.hwnd, WM_NCLBUTTONDOWN, HTCAPTION, 0&
End If
End SubL'événement Mouse_Move exploite quatre paramètres :
- Button => identifie quel bouton (gauche ou droite) est appuyé ;
- Shift => identifie quelle touche du clavier parmi les touches Ctrl, Alt, Maj ;
- X => identifie la position horizontale du pointeur ;
- Y => identifie la position verticale du pointeur.
Dans la procédure MoveMyCalculator, on identifie le bouton et si c'est bien le bouton gauche qui est appuyé (1) alors les appels des fonctions ReleaseCapture et de SendMessage sont lancés et permettent d'effectuer le déplacement de la fenêtre.
VIII. Conclusion▲
Ce petit projet reste intéressant pour les développeurs qui souhaitent mettre à la disposition des utilisateurs de leurs applications une calculatrice personnalisée.
Les avantages ont été évoqués en début de document et personnellement, je préfère offrir une calculette de ce type pour mes applications plutôt que mettre un exécutable qui ouvrirait de façon unique et au premier plan la calculatrice de Windows (la calculatrice de Windows est malheureusement multiinstance).
Je vous laisse donc créer votre calculette personnalisée avec pourquoi pas, d'autres fonctions spécifiques associées à d'autres boutons. Les possibilités sont vastes, ce tutoriel étant en fait une petite base de départ.
N'hésitez pas à me contacter si vous rencontrez des difficultés.










