1. 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's Windows).
Là, aucun problème de ce coté 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.

1-1. Remerciements

Je tiens à remercier toutes celles et ceux qui ont participé à la relecture de ce document en y incluant leurs remarques.

1-2. Contact

Pour tous renseignements complémentaires, veuillez me contacter directement (Argyronet) par MP.

2. Présentation du projet

L'objectif de ce tutoriel est de vous montrer qu'il est possible concenoir 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 Euro/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 :

Recopier le résultat trouvé
Sélectionnez

  Forms!NomDuFormulaireParent.NomDuContrôle = Me!txtLCDDisplayEUR

où 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).

3. Présentation du formulaire exemple

Réaliser une calculatrice d'un point de vue graphique n'est pas très difficile en soit ; 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.

3-1. 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 qu'il 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

Image non disponible

Pour le coté graphique, le formulaire voit sa propriété Image affectée à une image dégradée d'un bleu-violet tirant un 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 oeuvre 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 à ce que sa taille devienne 1 x 332 pixels et dans les propriétés Mode d'affichage de l'image du formulaire, choisissez Echelle ; 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.

3-2. 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 :

Propriétés générales Propriétés des événements
Image non disponible Image non disponible


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".

TRES 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...

4. Le formulaire en mode création

Lorsque l'on passe en Mode Création, vous pouvez remarquer que qu'il n'y a pas grandes différences avec le mode formulaire.
Le projet exploite 47 contrôles différents parmi lesquels figurent :

Image non disponible des Boutons
Image non disponible des Zones de texte
Image non disponible des Etiquettes
Image non disponible des Rectangles

Vous en trouverez la liste ci-après.

Le formulaire en mode création (Design)

Image non disponible

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 raccourcis Ctrl+Flèche de direction déplacent un contrôle ou un groupe de contrôles au pixel près.
Les touches de raccourcis Shift+Flèche de direction agrandissent/rétrécissent un contrôle ou un groupe de contrôles au pixel près.

4-1. 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

4-1-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.

Code du changement des couleurs des rectangles
Sélectionnez

    ledCopy.BackColor = RGB(0, 255, 0) ' Vert
    ledPaste.BackColor = RGB(255, 0, 0) ' Rouge

Le 3ème bouton quant à lui est peu un 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
Image non disponible Image non disponible
Mode non Modal : L'accès au formulaire appelant est libre :
(il faudra appuyer de nouveau sur le bouton ouvrant la calculatrice si le formulaire appelant occupe tout l'écran).
Mode Modal : Il est nécessaire de fermer la calculatrice pour avoir la main sur le formulaire appelant :
(Le mode Modal est affecté par défaut au formulaire)

4-1-1-1. 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 :

Avertissement de désactivation du mode Modal
Image non disponible

Avertissement de d'activation du mode Modal
Image non disponible

4-1-1-2. 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.

Image non disponible

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

Image non disponible

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).

Image non disponible

4-1-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 de 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 Euro - 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 avec un fond gris clair et comme Valeur par défaut "0."

Elle 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.

4-1-2-1. 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 les rectangles, zones de texte et images

Image non disponible

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 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
Etiquette € bleue 100 lblCurrency01 x:150 y:300 w:255 h:465
Etiquette € violette 100 lblCurrency02 x:135 y:285 w:255 h:465
Etiquette F bleue 100 lblFRF1 x:165 y:990 w:195 h:285
Etiquette 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

5. 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 oeuvre les gestion des opérateurs, il y a plusieurs manière 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 valeur 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 (Equivaut à vbNullString)

5-1. 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 Modules de projet avec une fonction pour lire dans la Base de Registre...

5-1-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

En-tête de Module du formulaire
Sélectionnez

''' ************************************************************************************
'''     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

Code des boutons 1 à 9 et , et =
Sélectionnez


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 Sub

Les opérations sont gérées par une seule fonction qui est appelée systématiquement

Code des boutons des opérateurs + - x ÷
Sélectionnez


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 Sub

Les boutons d'annulation annulent le contenu des variables et celui d'extinction change la propriété BackColor puis appelle la fonction KeepCurrentResult.

Code des boutons des fonctions spéciales C CE On/Off
Sélectionnez


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 Sub

Les fonctions spéciales exploitent un code aussi simple à comprendre que les opérations de base vues ci-avant.

Code des boutons des fonctions spéciales Vx 1/x % X²
Sélectionnez


Private Sub btnSQRT_Click()
''' Bouton Vx appuyé
'''------------------------
  m_dblFirstEntry = Val(Me!txtLCDDisplayEUR)
  If m_dblFirstEntry < 0 Then
      MsgBox "La racine carré 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  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 Sub

Les gestion de la mémoire s'effectue tout simplement avec une variable de module.

Code des boutons de mise en mémoire
Sélectionnez


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 Sub

Au sein du formulaire sont gérés les événements et plus particulièrement celui qui est généré par le clavier.

Code des procédure événementielles du formulaire
Sélectionnez


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écimal)
'''------------------------
  m_strSeparator = GetSystemDecimalSeparator()
  btnC.SetFocus
End Sub

Private Sub txtLCDDisplayEUR_KeyPress(KeyAscii As Integer)
''' Effet de frappe dans l´afficheur annulé
'''------------------------
  KeyAscii = 0
End Sub

Les fonctions de conversions sont nécessaires notamment à cause du séparateur de décimale du système mais également pour la conversion en Francs de la valeur Euro.

Code des fonctions de conversion
Sélectionnez


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 Function

Les fonctions des actions servent à faire en sorte que les calculs soient effectués selon les touches appuyées.

Code des fonctions et procédures des actions
Sélectionnez

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ésultat 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 Sub

Les fonctions externes (facultatives) peremettent la gestion du Copier/Coller et du mode d'affichage...

Code des autres fonctions (Copier/Coller/Mode) et l'appel au Registre
Sélectionnez

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é 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 Function

5-1-2. Le code du Module basRegistry

Code du Module d´accès au Registre de Windows
Sélectionnez

''' ************************************************************************************
'''     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'un 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 Function

6. 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 égale à "=".

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 fonction 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 visiblité 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...

Image non disponible

- 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.

7. 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 :

Ouvrir un formulaire
Sélectionnez

Private Sub cmdShowCalculator()
  DoCmd.OpenForm "", acNormal, , , , acDialog
End Sub

7-1. Elle est ouverte, mais je 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 :

Déclaration des API´s dans un module
Sélectionnez

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 = 2

Il vous reste alors à gérer les évenenemts pour chacun des rectangles comme suit :

Code du déplacement de la calculatrice
Sélectionnez

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 Sub

L'événement Mouse_Move exploite 4 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...

8. 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 multi-instances).

Je vous laisse donc créer votre calculette personnalisée avec pourquoi pas, d'autres fonctions spécifiques associées à d'autres boutons. Les possiblités sont vastes, ce tutoriel étant en fait une petite base de départ pour commencer.
N'hésitez pas à me contacter si vous rencontrez des difficultés.