Les leçons précédentes nous ont permis de présenter l'objectif de ce tutorial : construire une application de gestion commerciale en Paradox pour windows. Nous sommes arrivés à un schéma des données à gérer, nous avons construit les tables dans Paradox puis nous avons constuit notre première fiche Paradox pour constituer le menu de notre application. Nous allons maintenant coder les événements intéressants de cette fiche et tenter de rendre réutilisable le code qui peut l'être en l'intégrant dans une bibliothèque.
Nous avons déjà rencontré pour nos boutons "poussoir" les méthodes PushButton(), MouseEnter(), MouseExit() mais plus globalement pour notre fiche Menu.fsl nous allons programmer d'autre événements :
- l'Open de la fiche , pour paramétrer le cadre de notre application
(taille, position, titre, menu principal)
- Le Close de la fiche pour demander une confirmation à l'utilisateur
avant de quitter l'application
- La réaction aux touches de fonctions F1, F2 ... afin de bloquer toute
fonctionnalité non souhaitée
Pour l'open il est prudent de prévoir un code centralisé permettant d'effectuer les mêmes opérations pour toutes les fiches à ouvrir, comme la constitution d'un menu commun etc... Quitte à centraliser du code nous allons également intégrer la routine d'incrémentation qui nous servira à fournir à la demande des numéros uniques pour indexer nos tables.
- Dans la fenêtre projet, cliquez avec le bouton droit sur Bibliothèque
et choisissez Nouveau
- Dans la fenêtre vierge qui s'affiche, cliquez avec le bouton droit et
demandez Explorateur d'objets
- Vérifiez que vous êtes sur l'onglet Evénement et double-cliquez
sur Nouvelle méthode
- Tapez alors le code suivant :
method OpenForm() Var M Menu sTItre String EndVar sTitre="Mon Appli" HideToolBar() Maximize() M.addText(sTitre) M.show() FormatSetDateDefault("Windows court") endmethod |
En gros cette méthode déclare 2 variables M et sTitre qui vont nous servir à créer notre menu d'application, elle définit le titre de l'application, ici "Mon Appli" puis elle demande de masquer la barre d'outils de Paradox avec HideToolBar() afin d'empêcher l'utilisateur d'accéder directement aux fonctionnalités de Paradox, elle maximise la taille de la fenêtre puis elle construit un menu qu'elle affiche et enfin elle demande de gérer les dates sur le style "Windows court" c'est à dire jj/mm/aaaa dans le cas le plus courant.
En 9 lignes de code nous réalisons là toute une série de manipulations qui nécessiteraient bien plus d'instructions dans un langage moins performant. Cette simplicité et cette lisibilité du code constitue l'avantage principal de Paradox sur les autres outils. Même Access, réputé facile, est très loin de cette simplicité quand il s'agit de programmer une application.
- Vérifiez la syntaxe avec <F9>, un message en bas d'écran
doit vous annoncer "Auncune erreur de syntaxe".
- Fermer alors la fenêtre de la méthode OpenForm
- Double-cliquez sur Nouvelle méthode
- Choisissez comme nom de méthode et validez
- Tapez (ou récupérez) alors le code suivant :
method GetNextID(Const vID SmallInt) LongInt Var P,PControle TCursor sRep,sTable String vCle LongInt EndVar if not P.open(":App:NoPiece") then MsgStop("Erreur !","Impossible d'ouvrir la table NoPiece.") return 0 endif if not P.locate("ID",vID) then MsgStop("Erreur !","Impossible de trouver le paramètre N°Pièce demandé.") P.close() return 0 endif if P.TableControle="" then MsgStop("Erreur !","Impossible de trouver la table de contrôle.") P.close() return 0 endif sTable=":App:"+P.TableControle if not PControle.open(stable) then MsgStop("Erreur !","Impossible d'ouvrir la table "+sTable) return 0 endif P.edit() sRep="" while true if not P.LockRecord() then sRep=msgQuestion("Un autre utilisateur demande un "+P.NomPiece+" :","On réessaye ?") if sRep="No" then quitloop else loop endif else quitloop endif endwhile if sRep="No" then P.endedit() P.Close() return 0 endif vCle=P.NoPiece + 1 ;// On controle que la clé n'existe pas déjà dans la table de contrôle associée while true if PControle.locate(1,vCle) then vCle=vCle+1 else quitloop endif endwhile P.NoPiece=vCle P.endedit() P.close() PControle.close() return vCle endmethod |
Bon pas de panique, nous allons détailler précisément ce qu'elle fait.
On commence par déclarer quelques variables dont 2 d'un type particulier : le TCursor.
C'est la baguette magique du développeur Paradox ! En effet un TCursor (un curseur en mémoire sur une table) permet une fois ouvert sur une table donnée de parcourir celle-ci mais également de restraindre la vue aux enregistrements vérifiant des conditions et surtout d'appliquer aux données contenues dans les enregistrements tout une série de manipulations :
- insertion d'enregistrement
- suppression d'enregistrement
- modification des données d'un champ
- calcul sur les données...
Lors du développement de cette application nous aurons très souvent recours aux TCursors. Ils permettent également de manipuler les tables sans les afficher à l'écran et ,notamment grâce à la boucle Scan / EndScan, d'appliquer une série d'instructions OPAL aux enregistrements sélectionnés.
C'est à vous de nommer vos variables comme bon vous semble. Il existe des nomenclatures plus ou moins officielles sur le sujet mais le bon sens impose surtout d'être consistant et simple. Pour notre part nous avons pris quelques habitudes mais elles ne sont pas forcément exemplaires :
P : Pointeur de type TCursor
T : Pointeur de type Table
(Paradox offre un autre objet pour manipuler physiquement les tables : création,
suppression, copie etc...)
s : Variable de type String (chaîne de caractères)
v : Variable de type Number (nombre avec décimales) ou SmallInt (petit
nombre entier) ou LongInt (grand nombre entier)
d : Variable de type Date ou DateTime (Heure + Date)
Après donc avoir déclaré les variables nécessaires on essaie d'ouvrir un TCursor "P" sur la table NoPiece.db qui contient les derniers numéros utilisés. Notez l'utlisation très importante de l'alias avant le nom de table ce qui permettra par la suite de déplacer les tables ou même de se constituer très facilement une application de test par un simple changement dans la définition de l'alias. Souvenez-vous également pour une application réelle de la nécessité de spécifier pour les tables un alias différent des fiches et des états ( par exemple :Data: ) comme expliqué dans la partie 2 du tutorial.
En cas d'insuccès lors de l'open on envoie un message d'erreur à l'utilisateur (admirez la simplicité de la syntaxe !) puis on arrête la fonction en renvoyant 0.
En cas de succès, on cherche à localiser dans la table (donc NoPiece.db) l'enregistrement souhaité. Cet enregistrement est passé en paramètre à la fonction sous la forme d'une constante numérique appelée ici vID. La fonction locate utilisée ici est l'une des plus importantes à connaître pour le TCursor car elle permet de tenter de se positionner sur l'enregistrement répondant aux critères transmis. Cette première syntaxe demande de localiser le premier enregistrement de la table dont le champs nommé "NoID" contient la valeur vID. En cas de succès le TCursor se déplace sur l'enregistrement trouvé et la fonction locate renvoie True, sinon le TCursor ne bouge pas et la fonction locate renvoie False.
Puis on vérifie qu'une table de contrôle a bien été spécifiée dans la table NoPiece pour la clé désirée. L'intérêt de la chose et de s'assurer contre tout problème possible sur ce dernier numéro stocké. Que faire si l'utilisateur se trompe en revenant en arrière pour sa séquence de N°Facture alors qu'il existe des N° de factures déjà attribués ? Que faire si un problème informatique (oui cela arrive parfois, micro coupure etc...) décale la valeur du dernier numéro utilisé ? Un moyen simple est de protéger l'application en vérifiant que le numéro que l'on s'apprête à attribuer n'est pas déjà utilisé. Si par harsard il l'était alors on demande à l'application de trouver le prochain numéro libre en incrémentant et on reprend ce premier numéro disponible comme valeur de départ. En somme le programme s'auto-corrige en cas de souci !
La syntaxe if P.TableControle="" permet de tester le champ TableControle de la table NoPiece pour l'enregistrement sélectionné auparavant.
Une fois le nom de la table de contrôle obtenu, on ouvre un deuxième TCursor sur celle-ci. On passe ensuite le TCursor pointant sur NoPiece.db en mode édition afin de pouvoir changer la valeur du dernier numéro. On initialise une variable string sRep à blanc pour pouvoir la tester plus loin, puis on boucle en attendant qu'une instruction renvoie une valeur False stoppant la boucle while true.
L'objectif est de verrouiller l'enregistrement car cela garantirait que personne d'autre sur le réseau ne peut, en même temps que nous, incrémenter une clé. Paradox gère pour nous automatiquement ces verrous d'enregistrement. Dès qu'un utilisateur modifie un enregistrement, les autres ne peuvent que consulter celui-ci. Cela étant nous voulons que si le verrouillage échoue notre utilisateur puisse recommencer la tentative autant de fois qu'il le désire en espérant que le verrou aura été libéré entre-temps. Le risque est grand avec une telle boucle de lancer une tâche sans fin donc la question posée à l'utilisateur assure qu'en cas de problème il pourra sortir de la boucle.
Si tout est bon à ce stade (verrouillage réussi) on récupère dans la variable vCle le prochain numéro à utiliser pour le champ correspondant. Mais avant de l'attribuer on vérifie dans la table de contrôle que cette valeur ne pose pas de problème. La syntaxe if PControle.locate(1,vCle) then utilise une autre possiblité de la méthode locate de l'objet TCursor, la possibilité de spécifier en premier paramètre non pas le nom du champ entre guillemets mais le numéro de colonne du champ. C'est ce qu'il nous faut car nous ne connaissons pas à ce stade le nom du champ clé de la table de contrôle qui dépend du champ à incrémenter. Nous savons par contre que ce sera toujours la première colonne.
Ce contrôle effectué , nous affectons cette nouvelle valeur à notre compteur dans la table NoPiece.db, nous validons l'enregistrement en arrêtant le mode édition puis nous fermons nos TCursor pour être sûr que la mémoire soit bien libérée. Nous retournons enfin vCle comme résultat de la fonction.
Pas trop mal au crâne ? Bien, vous avez vu le plus compliqué ! Le reste est un jeu d'enfant...
Mais continuons nos routines de bibliothèque.
method ConstMenu(Const NomEtat String) Var PmFichier,PmEtat PopUpMenu M Menu Etat Report EndVar Etat.open(NomEtat) Etat.maximize() if not M.isAssigned() then PmFichier.AddText("&Imprimer...",MenuEnabled,MenufilePrint) PmFichier.AddText("&Configurer l'imprimante...",MenuEnabled,MenufilePrinterSetUp) PmFichier.AddText("&Fermer",MenuEnabled,MenuControlClose) PmEtat.AddText("Première page",MenuEnabled,MenuReportPageFirst) PmEtat.AddText("Page suivante",MenuEnabled,MenuReportPageNext) PmEtat.AddText("Page précédente",MenuEnabled,MenuReportPagePrevious) PmEtat.AddText("Dernière page",MenuEnabled,MenuReportPageLast) PmEtat.AddText("Aller à la page...",MenuEnabled,MenuReportPageGoto) M.AddPopUp("&Fichier",PmFichier) M.AddPopUp("&Etat",PmEtat) endif Etat.setmenu(M) endmethod |
Bon là rien de bien sorcier si ce n'est que ce code vous rendra de grands services ! On construit un menu avec deux rubriques, l'une Fichier offira les options classiques : Imprimer, Configurer l'imprimante, Fermer tandis que l'autre, Etat offrira la navigation dans les pages : Première page, page suivante, page précédente, dernière page, aller à la page...
Attention ce code ne fonctionne qu'à partir de la version 5.0 de Paradox. Il est important car il vous assure que lors de la visualisation d'un état l'utilisateur ne verra pas apparaître le menu complet de Paradox qui lui permettrait par exemple d'accéder directement aux tables sans passer par votre application ou de lancer des commandes sans contrôle.
Une fois ces méthodes utiles créées et leur syntaxe vérifiée il convient de sauvegarder cette bibliothèque:
- Menu Fichier / Sauvegarder
- Choisissez Lib.lsl comme nom de fichier
Remarquez qu'encore une fois Paradox crée un fichier à part pour
chaque bibliothèque ce qui s'avère à l'usage très
pratique pour dupliquer du code entre différents projets.
- Faciliter la maintenance de l'application : On change à un endroit
le code qui est appelé par les différentes fiches
- Facilité pour réutiliser le code : On duplique sa ou ses bibliothèques
pour les nouveaux projets.
Dès que l'on rencontre un code qui semble revenir souvent il faut essayer de le rendre général avec, si nécessaire, l'introduction de paramètres et le centraliser en bibliothèque.
© Copyright 2000-2007 , Clairinfo ® , http://www.clairinfo.fr