Programmation Bd

  • June 2020
  • PDF

This document was uploaded by user and they confirmed that they have the permission to share it. If you are author or own the copyright of this book, please report to us by using this DMCA report form. Report DMCA


Overview

Download & View Programmation Bd as PDF for free.

More details

  • Words: 6,371
  • Pages: 21
Programmation d'applications orientées données

13. Programmation

d'applications utilisant des bases de données

Le chapitre précédent a permis de manipuler les différents composants utilisés pour construire une application "orientée données". Il a permis aussi de se familiariser avec les mécanismes et les utilitaires mis en œuvre. Mais il ne fait aucun doute qu'il n'est pas possible de réaliser des applications professionnelles en se contentant de déposer et d'initialiser des composants, aussi séduisants soient-ils ( TDBGrid et TDBNavigator en particulier ). Seules les démonstrations que l'on voit dans certains salons professionnels se satisfont de cela. Pour le reste, pour construire des applications complexes et robustes, il va falloir se retrousser les manches et ... programmer.

13.1 : Gestion d'un ensemble de données Les différents ensembles de données ( tables ou, on le verra plus loin, résultat d'une requête SQL ), sont caractérisés par leur état, également appelé mode. Une application peut placer les différents ensembles de données qu'elle manipule dans des modes différents en appelant une méthode appropriée.

13.11 : Les différents modes Les modes possibles sont les suivants : Mode Inactive Browse Edit Insert

Setkey CalcFields

Description L'ensemble de donnée est fermé État par défaut à l'ouverture. Il permet de visualiser les données et de se déplacer au sein de l'ensemble de données mais n'autorise pas les modifications ni les ajouts. Permet la modification de l'enregistrement actif . Permet l'insertion d'une nouvelle ligne à l'emplacement courant qui devient l'enregistrement courant. Permet l'édition dans cet enregistrement. ( L'ex-enregistrement courant est donc déplacé d'un rang après le nouvel enregistrement ) Permet à certaines méthodes ( FindKey, GoToKey, etc. ) de rechercher des valeurs dans l'ensemble de données. Mode utilisé uniquement avec des champs calculés. Page XIII.1

Programmation d'applications orientées données

13.12 : Déplacement dans un ensemble de données Pour parcourir un ensemble de données, BDE met en place un curseur interne (matérialisé dans une grille par un sélecteur ). C'est ce curseur qui définit l'enregistrement actif (enregistrement courant ) que l'on visualise ou que l'on édite en vue de modifications. Les différents ensembles de données peuvent être parcourus à l'aide des méthodes du composant TTable suivantes : Méthode First Last Next Prior BOF EOF MoveBy (n)

Action Déplace le curseur sur le premier enregistrement Déplace le curseur sur le dernier enregistrement Déplace le curseur à l'enregistrement suivant Déplace le curseur à l'enregistrement précédent Renvoie true lorsque le curseur est au début de l'ensemble de données Renvoie true lorsque le curseur est à la fin de l'ensemble de données Déplace le curseur de n enregistrements ( n peut être positif ou négatif ) par rapport à l'enregistrement courant.

13.13 : Modification des états Différentes méthodes du composant TTable permettent de faire basculer un ensemble de données d'un mode à l'autre. Méthode Open Close Cancel Edit Insert Append Post Delete

Action Ouvre l'ensemble de données (*). Le met en mode Browse. Ferme l'ensemble de données. Le met en mode Inactive Annule l'opération en cours et place l'ensemble de données en mode Browse Place l'ensemble de données en mode Edit. (**) Place l'ensemble de données en mode Insert. Positionne le curseur à la fin de l'ensemble de données et le place en mode Insert Expédie l'enregistrement nouveau à la base de données ( valide donc les nouvelles données ) et place l'ensemble de données en mode Browse Supprime l'enregistrement en cours et place l'ensemble de données en mode Browse

(*) Dans le cas ou l'ensemble de données n'a pas sa propriété Active = true lors de la phase de conception. (**) Se met en mode Insert si l'ensemble de données est vide.

C'est donc la méthode Post ( ), qui en fait actualise les tables physiques. Elle joue donc un rôle primordial et il faut bien comprendre son mécanisme : - En mode Edit ( ), Post ( ) modifie l'enregistrement en cours; - En mode Insert ( ), Post ( ) insère un nouvel élément.



Si le fait de modifier les données ne peut être réalisé ( violation de clé, données non compatibles, etc. ) l'appel à la méthode Post provoque une exception et les données expédiées ne sont pas prises en compte ( toutes les données, pas seulement la donnée ayant provoqué l'exception ).

Les méthodes First( ), Next( ), Prior( ), Last( ) génèrent un appel implicite à Post( ) si l'ensemble est en mode Edit ( ) ou Insert ( ).

Page XIII.2

Programmation d'applications orientées données Les méthodes Insert( ) et Append( ) effectuent elles aussi des appels implicites à Post() (validation des données contenues dans l'enregistrement courant avant que celui-ci soit remplacé par un nouvel enregistrement ). Dans les autres cas il faut appeler Post( ) explicitement.



Post n'est pas appelée implicitement par Close ( ). Il faut utiliser l'événement BeforeClose ( ) pour expédier explicitement les données en attente.



La méthode Cancel ( ) peut défaire les modifications apportées à un enregistrement ( tant qu'un appel à Post ( ) n'a pas été réalisé explicitement ou implicitement ).



Si l'on utilise une DBGrid en mode édition ( ou autoedit ), le fait de changer d'enregistrement actif provoque un appel implicite à Post ( ) .



Un appel à Post ( ) annule le mode Insert ou Append. Il faut donc repasser dans un de ces modes si on doit poursuivre des opérations de modifications. On a donc les différentes lignes de code suivantes : Table1 -> Open ( ) ; // rend la table active Table1 -> Last ( ) ; // positionne le curseur à la fin de la table ..... TTable -> Open ( ) ; TTable -> First ( ) ; while ( ! TTable -> Eof ) { < actions > ; TTable -> Next ( ) ; // ne pas oublier !!!! ..... } /* Code très répandu qui réalise une action donnée sur tous les enregistrements jusqu'à l'arrivée en fin de table */



Lorsque l'on réalise une action itérative sur tous les enregistrements d'une table, l'application est ralentie par toutes les actions de rafraîchissement de l'écran qu'elle doit réaliser. Il faut donc penser à inhiber le composant TDataSource lié à la table pendant toute la durée du traitement et de ne le réactiver qu'à l'issue :

DSMarque -> Enabled = false ; ... { traitement précédent } .... DSMarque -> Enabled = true ;

TTable -> Edit ( ) ; // mise en mode Edition < Action sur l'enregistrement > TTable -> Next ( ) ; // actualisation de la table par appel implicite à Post ( )



Le fait de supprimer un enregistrement fait que le suivant prend sa place en tant qu'enregistrement courant. Il ne faut donc pas, dans ce cas là "passer à l'enregistrement suivant" car un enregistrement serait sauté et donc non traité.

Page XIII.3

Programmation d'applications orientées données

TMarques -> Open ( ) ; while ( ! TMarques -> Eof ) { if ( TMarquesMarque -> Value = TEdit -> Text ) TMarques -> Delete ( ) ; else TMarques -> Next ( ) ; } /* Chaque fois que le champ Marque a la valeur de la zone d'édition, l'enregistrement correspondant est effacé. L'enregistrement actif devient son successeur ... il ne faut donc pas réaliser TMarques-> Next ( ) dans ce cas */

13.14 : Événements associés à la gestion d'un ensemble de données Chaque ensemble de données peut réagir à plusieurs événements. La création de gestionnaires d'événements associés permet d'affiner le comportement de l'application : Événement BeforeOpen, AfterOpen BeforeClose , AfterClose BeforeInsert, AfterInsert BeforeEdit, AfterEdit BeforeCancel, AfterCancel BeforeDelete, AfterDelete OnNewRecord

Action Appelés avant ou après l'ouverture d'un ensemble de données Appelés avant ou après la fermeture d'un ensemble de données Appelés avant ou après le passage en mode Insert Appelés avant ou après le passage en mode Edit Appelés avant ou après l'annulation du mode précédent Appelés avant ou après la suppression d'un enregistrement Appelé lors de la création d'un nouvel enregistrement . Utile pour donner des valeurs par défaut.

Exemple : On peut définir un gestionnaire d'événement associé à l'événement BeforeDelete : void __fastcall TForm1::Table1BeforeDelete(TDataSet *DataSet) { if ( MessageDlg ( "Souhaitez-vous réellement supprimer cet enregistrement ? ", mtWarning, TMsgDlgButtons() << mbYes << mbNo , 0 )== mrNo ) Table1 -> Abort ( ) ; } /* Abort ( ) est une procédure qui permet de sortir de l'exécution prévue sans générer une exception */

Page XIII.4

Programmation d'applications orientées données

13.2 : Accès aux données par programmation On s'est contenté, jusqu'à présent d'utiliser les propriétés de base proposées par C++ Builder pour permettre l'accès aux données. Si celles-ci sont intéressantes, et permettent de réaliser certaines interfaces sans avoir pratiquement à coder quoi que ce soit, on se rend compte que tout cela reste limité tant que l'on ne peut accéder directement aux différents champs de l'enregistrement courant. Pour que cela soit possible, C++ Builder offre la possibilité de créer, à la demande, des objets spécifiques - appelés objets TField - qui encapsulent chaque champ d'un enregistrement. Une fois un objet TField créé, il peut être configuré finement, dès la phase de conception, à l'aide de l'inspecteur d'objet puis manipulé par programmation.

13.21 : Les objets TField Le fait de "double-cliquer" sur l'icône représentant un composant TTable sur la fiche de conception ouvre une boîte de dialogue ( appelée "éditeur de champ" ) qui permet de créer puis d'accéder à chaque champ de la table pris individuellement. Pour cela chaque champ est encapsulé dans un objet spécifique, de type TField , que l'on va pouvoir : - Initialiser lors de la phase conception ; - Modifier par programmation pendant l'exécution. Le fait de pouvoir définir des objets TField fait que l'on peut accéder aux différents champs d'une table sans utiliser de composants constituant une interface utilisateur.

Page XIII.5

Programmation d'applications orientées données

13.211 / L'éditeur de champ : Au départ la fenêtre de l'éditeur de champ est vide. En cliquant sur le bouton droit de la souris, une deuxième fenêtre apparaît, visualisant le nom de tous les champs contenus dans la table. Il faut alors sélectionner à la souris ceux que l'on souhaite utiliser, en tant que champ individualisé, dans l'application.



Il est possible de "supprimer" des objets TField voire de tous les effacer (cette suppression n'a, bien entendu, aucun effet sur la table).

Le fait de ne pas sélectionner un champ ( en créant un objet TField ) fait qu'il ne pourra pas être accessible ultérieurement par programmation. Par contre on peut créer un objet TField, associé à un champ donné, puis - on le verra plus loin - le rendre 'invisible'. Dans ce cas : - Il n'est pas affiché dans le composant TGrid associé à la table ; - On peut néanmoins y accéder par programmation. Cette possibilité est surtout intéressante lorsque l'on doit manipuler des clés numériques dont l'utilisateur n'a pas à avoir connaissance. Il ne faut créer les objets TField que si l'on en a réellement besoin, car ils gonflent l'exécutable résultant. Si on ne souhaite que réaliser de l'affichage simple, il ne sont pas nécessaires. Le nom de chaque objet champ est généré automatiquement : il est constitué par le nom logique de la table auquel C++ Builder a rajouté, sans espace, le nom du champ dans la table. C'est ce nom qui apparaît dans l'inspecteur d'objet lorsque le champ est sélectionné dans l'éditeur de champs. Exemple : Le champ Nom dans la table TMembres sera encapsulé dans un objet TField nommé TMembresNom.



Il est bien entendu possible de modifier - à l'aide de l'inspecteur d'objet - ce nom mais ce n'est en général pas utile et il est de plus facilement mémorisable.

13.212 / Différents types de composants TField : En fait, il existe plusieurs types de composants TField. Le type correspondant à un champ donné est détecté automatiquement par C++ Builder. Les principaux types d'objets TField sont : TStringField TIntegerField TSmallintField TWordField TFloatField TCurrencyField TBooleanField TDateTimeField TDateField TTimeField TBlobField TBytesField

Textes de taille fixe jusqu'à 255 caractères Nombres entiers compris entre -2 147 483 648 et 2147 483 647 Nombres entiers compris entre -32768 et 32767 Nombres entiers compris entre 0 et 65535 Nombres réels Valeurs monétaires. Valeur true ou false Valeur date et heure Valeur date Valeur heure Champ arbitraire de données sans limitation de taille Champ arbitraire de données sans limitation de taille Page XIII.6

Programmation d'applications orientées données TMemoField TGraphicField

Texte de taille arbitraire Graphique de taille arbitraire tel qu'un bitmap

13.213 / Principales propriétés : Une fois les objets champs ajoutés, il suffit de les sélectionner un par un pour pouvoir accéder, via l'inspecteur d'objet, à leurs propriétés propres : Alignment As.... Calculated DisplayLabel DisplayText DisplayWidth EditMask IsNull ReadOnly Value Visible

Permet de réaliser des alignements à l'affichage. Propriétés permettant de réaliser les conversions de types ( voir plus loin ) Indique si le champ est un champ calculé ou non Titre du champ. Dans un composant TGrid, le titre des colonnes peut ainsi être plus explicite - et contenir des accents et des espaces - que celui créé avec DBD. Valeur du champ tel qu'il est réellement affiché ( par exemple pour un champ de type entier, il s'agit de la chaîne alphanumérique correspondant au nombre ) Permet de donner des largeurs différentes aux colonnes d'un composant TGrid Permet de réaliser des masques de saisie. La syntaxe utilisée est la même que celle du composant TEditMask. Renvoie vrai si la valeur du champ de l'enregistrement courant est nulle. Rend le champ uniquement accessible en lecture ou non. Valeur réelle du champ Rend la colonne correspondant au champ visible ou non dans un composant TGrid.

La propriété Value (accessible seulement à l'exécution) est la propriété principale d'un objet TField car elle permet d'accéder en programmation à la valeur du champ de l'enregistrement courant. On pourra ainsi avoir des instructions de type : TMarquesMarque -> Value = "AUDI'";

ou : EMarques->Text

= TMarquesMarque->Value ;

Toute la puissance ultérieure développée par C++ Builder dans la gestion des données vient de sa capacité à gérer, par programmation les champs individuellement.

13.214 / Conversion d'une valeur affectée à un champ : Par défaut C++ Builder reconnaît automatiquement le type de valeur que l'on souhaite affecter à un champ et effectue les conversions possibles. S'il y a incompatibilité, une erreur est générée. Il est aussi possible de forcer une conversion de manière à ce que C++ Builder convertisse la valeur entrée dans le type souhaitée. Par exemple, pour un champ de type "chaîne", C++ Builder générera une erreur si l'on entre des valeurs numériques. Si l'on réalise la conversion de la valeur entrée en chaîne de caractères, il n'y aura pas d'erreur.

Page XIII.7

Programmation d'applications orientées données On convertit la valeur d'un composant TField en utilisant une de ses propriétés As ..... . Les possibilités de conversions sont résumées dans le tableau ci-dessous : Type de conversion Type de TField TStringField TIntegerField TSmallintField TWordField TFloatField TCurrencyField TBCDField TDateTimeField TDateField TTimeField

TBooleanField TBytesField TVarBytesField TBlobField TMemoField TGraphicField

AsString Type chaîne par définition Convertir en chaîne

AsInteger Convertir en entier si possible Type entier par définition

Convertir en chaîne

Arrondir à la valeur Type flottant par entière la plus définition proche Interdit Convertir date en nombre de jours depuis 01/01/0001. Convertir l'heure en fractions de 24 heures Interdit Interdit

Interdit

Interdit

Interdit

Convertir en chaîne. Le contenu dépend du format d'affichage du champ Convertir en chaîne "true" ou "false" Convertir en chaîne (n'a généralement lieu d'être que pour TMemoField)

AsFloat Convertir en flottant si possible Convertir en flottant

Interdit

AsDateTime Convertir en date si possible Interdit

Type date ou heure par définition. Aucune date ou heure en l'absence de spécification Interdit

Exemples : L'instruction suivante convertit la valeur du composant TField appelé Table1MonChampNum en une chaîne et l'affecte au texte du contrôle Edit1: Edit1 -> Text

= Table1MonChampNum -> AsString ;

Inversement, l'instruction suivante affecte le texte du contrôle Edit1 au champ Table1MonChampChaine en tant que chaîne : Table1MonChampChaine -> AsString = Edit1 -> Text ; Une exception se produit si une conversion non supportée est effectuée en mode exécution. Si le champ est du type de la valeur correspondant au champ, il n'est pas nécessaire d'utiliser les fonctions de conversion. Cependant l'utilisation de telles possibilités sécurise les entrées et évite d'avoir à utiliser la syntaxe : Table1MonChampChaine -> Value = Edit1 -> Text ;

12.215 / Différentes méthodes d'accès à un champ : En fait il est possible d'accéder aux différents champs selon diverses méthodes plus ou moins simples d'emploi :  Accès par l'objet TField : Cette méthode est la plus simple et c'est celle que l'on a étudié ci-dessus.

Page XIII.8

Programmation d'applications orientées données Une fois les différents objets champ créés, on peut accéder aux différentes valeurs des champs par la propriété Value.

 Accès par la propriété Fields [ i ] : Il est possible d'accéder aux différents champs d'une table en utilisant la propriété Fields [ i ] ( i étant le rang du champ dans la table, 0 correspondant au premier champ ). Cette propriété n'est accessible qu'à l'exécution et en mode lecture ( sauf en mode SetKey ). Elle nécessite la connaissance complète de la structure de la table. La propriété Fieldname de Fields permet de connaître le nom d'un champ. titre = Table1 -> Fields [ 1 ] -> FieldName ; /* Pour connaître les différents champs constituant une table, on peut réaliser une boucle comme celle-ci : while ( i <= Table1 -> FieldCount ) ListBox1 -> Items -> Add ( Table1 -> Fields[i] -> FieldName ); */

 Accès par la méthode FieldByName (const AnsiString FieldName ) : Cette méthode permet d'accéder à un champ en fournissant son nom. Exemple : Table1-> FieldByName ("CustNo" ) -> AsString = "1234"; /* Cette méthode est plus sûre que l'utilisation d'un indice de champ mais nécessite néanmoins de connaître les différents noms des champs. On aurait en effet pu avoir : Table1 -> Fields[0] -> AsString = "1234"; */

Il y a équivalence stricte entre toutes les méthodes d'accès à un champ : Si on a une table Identite comportant le champ Nom en deuxième position on peut accéder à ce champ par les instructions suivantes : AnsiString chaine; .... chaine = TIdentiteNom -> Value ; chaine = TIdentite -> FieldByName ("Nom") -> AsString ; chaine = TIdentite -> Fields [ 1 ] ->AsString ; Il faut noter que les deux dernières méthodes ne nécessitent pas la création d'un objet TField et que, en toute rigueur, la dernière - accès par un indice - est la plus rapide.

13.22 : Modification d'enregistrements complets

Page XIII.9

Programmation d'applications orientées données Il est possible d'initialiser ou de modifier champ par champ un enregistrement ( en appelant chacun d'entre eux avec la propriété Value). Cette méthode devient lourde dès lors qu'il y a beaucoup de champs à modifier. On peut alors utiliser trois méthodes permettant de réaliser globalement les modifications souhaitées : AppendRecord ([ Tableau de valeurs ])

Ajoute un enregistrement en fin de table et effectue un appel à Post. InsertRecord ([ Tableau de valeurs ])

Idem mais l'insère à l'emplacement du curseur interne. SetFields ([ Tableau de valeurs ] )

Modifie les valeurs des champs de l'enregistrement actif. Ne fait pas appel à Post ( ). Exemples : TMarques -> AppendRecord ([ 9999, "Daewood" ] ); TIdentite -> SetFields ( [ NULL , NULL, 30 ] );

Pour pouvoir utiliser ces méthodes il faut connaître parfaitement la structure de la table (ordre et nature des différents champs). Dans le deuxième exemple, le fait d'initialiser certains paramètres à 'NULL' fait que les valeurs courantes correspondantes ne sont pas modifiées . Pour utiliser la méthode SetFields ( ), il faut au préalable mettre la table dans le mode Edit puis, à l'issue, réaliser un Post ( ).

13.23 : Création de champs calculés Un champ calculé est un champ qui n'existe pas physiquement dans une base de données mais qui est construit à partir de données élémentaires contenues dans celle-ci. Par exemple on peut avoir une table CLIENTS contenant un champ Nom ( de 30 caractères ) et un champ Prenom ( de 30 caractères ). On peut utiliser ces champs tels quels. Mais, à l'affichage, il peut être peut intéressant de devoir afficher les deux colonnes correspondant à ces deux champs, cela prend trop de place. On peut souhaiter disposer d'un champ NomPrenom concaténant les deux informations et considéré comme un seul champ. Ce champ, qui n'existe pas physiquement dans le base, est un champ calculé.

Pour créer un champ calculé, il faut : - Créer les composants TField correspondant aux champs qui sont nécessaires à la création du champ calculé ( dans l'exemple : TClientsNom et TClientsPrenom ). - Créer un nouveau champ à partir de l'éditeur de champ ( qui apparaît en double cliquant sur le composant Table concerné ). Une boite de dialogue apparaît :

Page XIII.10

Programmation d'applications orientées données

-

-

Dans la zone de saisie 'Nom' il faut donner un nom au champ calculé. Un type est proposé qu'il est possible de modifié. S'il s'agit d'une chaîne de caractères on peut indiquer une taille ( qui peut être différente de celle de la somme des champs originaux ). S'assurer que le radio bouton 'Calculé' est validé. Valider l'ensemble en cliquant sur 'OK'. Un nouveau composant TField est créé, il a les même caractéristiques que les autres champs.

-

A la sortie de la boite de dialogue, une nouvelle colonne apparaît dans la TGrid associée ( si celle-ci est déposée et connectée sur la feuille ). Mais cette colonne est vide. En effet il n'a pas été spécifié comment le nouveau champ va être calculé.

-

Le calcul ne peut être réalisé qu'à l'exécution du programme en fonction des valeurs réellement contenues dans les différents champs. Ces calculs sont réalisés à l'aide de l'événement OnCalcField de la table concernée. Dans l'exemple précédent cela donne : void __fastcall TForm1::TClientsCalcFields(TDataSet *DataSet) { TClientsNomPrenom -> Value = TClientsNom->Value + " " + TClientsPrenom->Value ; } /* chaque champs Nomprenom de chaque enregistrement de la "vue" prend la valeur des deux champs Nom et Prenom, plus un espace entre les deux */



Si un index secondaire complexe à été constitué, dans DBD, en utilisant les champs Nom et Prenom il est alors possible de réaliser des affichages en fonction de ce champ calculé. Page XIII.11

Programmation d'applications orientées données



Un champ calculé n'est pas "éditable": il ne peut être modifié directement.

13.3 : Modification de l'indexation d'une table Par défaut, une table est gérée, et affichée, selon son index primaire ( c'est à dire le premier champ de sa structure, qui est en général sa clé ). Si l'on souhaite gérer et afficher une table selon un ordre il faut demander à BDE de réaliser une "vue" de la table en fonction de l'index spécifié. Cela peut se faire : - Dès la phase de conception en initialisant la propriété TTable -> IndexName ( une combo-box propose tous les index disponibles, préparés lors de la phase de construction de la table dans DBD ).

- En cours d'exécution grâce à une instruction de type : TTable -> IndexName = "IdNom" ;

Il est ainsi possible de modifier les "vues" d'une même table en fonction des besoins du programme ( d'où la nécessité de prévoir dès la phase de conception les différents index secondaires qui seront utilisés ensuite ).



Si le programmeur gère lui même une clé et que la table est affichée selon un index secondaire ( par exemple par ordre alphabétique sur le champ 'Nom ' ), le programmeur ne peut atteindre la fin "réelle" de la table. En effet les instructions du type TTable -> Next ( ), TTable ->Last ( ) ou autres déplacent le curseur interne au sein de la "vue" de la table constituée avec l'index et non en fonction de sa constitution physique.



Si l'on souhaite revenir à une vue de la table s'appuyant sur l'index primaire il faut écrire une instruction de type : TTable -> IndexName = "" ; // chaine vide

Exemple : On souhaite insérer une nouvelle marque à la table TMarques. Cette table est actuellement gérée par un index secondaire l'affichant par ordre alphabétique et utilise un identifiant créé par programmation ( utilisé pour pouvoir accéder aux différentes tables secondaires de la base de données). Pour insérer un nouvel enregistrement il va falloir réaliser les opérations suivantes : { int Index; TMarques -> Open ( ) ; TMarques -> IndexName = "" ; // suppression de l'index secondaire TMarques -> Last ( ) ; // positionnement en fin de table Index = TMarquesIdMarque -> Value ; TMarques -> Append ( ) // ajout en queue d'un nouvel enregistrement TMarquesMarque -> Value = "NOUVELLE MARQUE" ; TMarquesIdMarque -> Value = Index + 1 ; Page XIII.12

Programmation d'applications orientées données TMarques -> Post ( ) ; Tmarques -> IndexName = "IdMarque" ; // réactivation de l'index secondaire }

13.4 : Recherches dans une table On a vu qu'il était possible de rechercher de manière itérative une information dans une table pour réaliser un traitement quelconque. Bien entendu, si la table est importante la recherche peut être longue. Le composant TTable dispose de plusieurs méthodes permettant de rechercher rapidement des valeurs.

13.41 : Utilisation des méthodes GoTo... Les méthodes GotoKey ( ) et GoToNearest ( ) permettent de réaliser une recherche dans une table utilisant une clé.



Sous Paradox, donc BDE, les champs clés doivent être placés en tête de la table.

Pour pouvoir effectuer une recherche avec ces méthodes il faut, au préalable, placer la table en 'mode de recherche' par la méthode SetKey ( ) (ce qui a pour effet d'empêcher les modifications sur les champs ). On donne la valeur à rechercher dans le champ clé et on réalise la recherche en appelant la méthode GoToKey ( ) - recherche stricte - ou GoToNearest ( ) - recherche les correspondances proches -. On a alors la routine suivante : TTable TTable TTable TTable /*

-> -> -> -> ou

Close ( ); Open ( ) ; // vidage des buffers SetKey ( ) ; Fields -> Fields [ 0 ] -> AsString = Edit1 -> Text ; TTable -> FieldByName ( "Nom" ) ->AsString = Edit1 -> Text */ if ( ! TTable -> GotoKey ( ) ) ShowMessage ( "Enregistrement non trouvé "); ...

La recherche débute au début de la table et le curseur se positionne sur l'enregistrement correspondant à la valeur passée en paramètre via l'instruction TTable -> Fields [ 0 ] -> AsString = . GotoKey ( ) est une fonction booléenne qui renvoie true quand la recherche s'est bien passée ( d'où la possibilité de tester cette valeur de retour ).



L'instruction TTable -> Fields [ 0 ] -> AsString = Edit1 -> Text ne modifie pas la valeur du champ dans l'enregistrement courant. Dans le mode SetKey elle sert juste à initialiser la recherche.



Si la clé est composée de plusieurs champs et que l'on ne désire réaliser la recherche que sur un des champs, il faut initialiser la propriété KeyFieldCount avec l'indice du champ souhaité ( 1 = 1° champ ). Page XIII.13

Programmation d'applications orientées données

13.42 : Utilisation des méthodes Find... Les méthodes FindKey ( ) et FindNearest ( ) combinent en un seul appel les actions réalisées par les séquences d'instructions de la famille précédente. Les prototypes de ces deux méthodes sont : bool __fastcall FindKey ( const System::TVarRec *KeyValues, const int KeyValues_Size); void __fastcall FindNearest( const System::TVarRec * KeyValues, const int KeyValues_Size);

Le type TVarRec est une structure prédéfinie utilisée à l'intérieur d'une fonction avec un type de paramètre de tableau de const. Une variable de ce type peut être créée avec la macro ARRAYOFCONST. Cette macro est définie dans le fichier sysdefs.h. Exemple : void __fastcall TForm1::Edit1Change(TObject *Sender) { Table1 -> FindNearest ( ARRAYOFCONST ( ( Edit1->Text ) ) ); } /* Dans ce cas le tableau de constante est réduit à une seule chaîne de caractères */ a le même effet que : TTable -> Setkey ( ) ; TTable -> Fields [ 0 ] -> AsString TTable -> GoToNearest ( ) ;

= Edit1 -> Text ;

13.43 : Recherche à l'aide d'un index secondaire Pour effectuer une recherche sur un champ qui n'est pas la clé primaire on doit spécifier le nom du champ à utiliser par la propriété IndexName. TMembres TMembres TMembres TMembres TMembres TMembres



-> -> -> -> -> ->

Close ( ) ; IndexName = "IdNoms" ; Open ( ) ; SetKey ( ) ; FieldByName ("Nom")-> AsString := Edit.Text ; GoToNearest ( ) ;

Il faut que l'index secondaire ait été créé au préalable dans DBD ( son nom apparaît alors dans l'inspecteur d'objet dans le champ IndexName ).

Page XIII.14

Programmation d'applications orientées données



Si entre deux recherches on change d'index il faut fermer la table avant de réaliser le changement d'index ( propriété IndexName de la table ).

13.5 : Utilisation de possibilités "Lookup" Sans avoir recours aux possibilités offertes par le langage SQL ( qui sera étudié dans le chapitre suivant ), il est parfois difficile d'afficher de manière claire les informations contenues dans les tables. Celles-ci contiennent en effet de nombreux champs contenant des valeurs numériques qui sont en fait des clés permettant d'accéder à des tables de référence où sont stockées les valeurs "lisibles" correspondantes. Ce n'est que l'application stricte du modèle référentiel pilier des SGBDR. De fait : Il n'est pas possible d'afficher simplement des tables qui dépendent d'une ou plusieurs tables de référence ; Il n'est pas possible de réaliser simplement des formulaires de saisie sur ces tables car l'utilisateur n'a pas à connaître les valeurs des clés utilisées et les liens existant entre ces tables.

Affichage d'une table contenant un champ clé ( Id ) et deux champs liés à des tables de référence ( IdMarque et Id Type ). Elle n'est que très peu utilisable telle quelle.

13.51 : Création de champs de référence Il est possible de créer des champs de référence permettant d'afficher le "clair" des champs connus par des clés. Pour cela il faut, comme pour des champs calculés créer un nouveau composant TField à partir de l'éditeur de champ.

Page XIII.15

Programmation d'applications orientées données En reprenant l'exemple précédent ( Base de données Auto ) on réalise les opérations suivantes : Renseigner la boite de dialogue 'Nouveau champ' : donner un nom, déterminer un type et éventuellement la taille du champ. Sélectionner le bouton radio 'Référence'.

-

Dans ce cas, et dans ce cas seul, il faut alors renseigner les différents champs de saisie contenus dans le groupe 'Définition de la référence' ( seuls deux champs sont actifs au début ). . 'Champs clé' doit contenir l'indication du champ qui lie les deux tables ( ici : IdMarque ). . 'Dataset' doit contenir le nom de la table dans laquelle on va chercher l'indication en clair ( ici : TMarques ). Les deux autres zones de saisies s'activent à ce moment là. . 'Clés de référence' doit contenir le champ clé de la table de référence ( ici : IdMarque ). . 'Champ résultat' doit contenir le nom du champ contenant les données que l'on souhaite afficher en "clair" ( ici : Marque ).

Un nouveau composant TField aux caractéristiques souhaitées apparaît alors et peut être visualiser dans un composant TDBGrid à l'exécution. Ses caractéristiques apparaissent dans l'inspecteur d'objet comme suit :

Page XIII.16

Programmation d'applications orientées données

-

On peut alors rendre le composant TField TModelesIdMarque invisible (propriété Visible = false ) de manière à ce qu'il n'apparaisse pas dans la grille d'affichage. On peut renouveler l'opération avec le champ IdType.



Contrairement à un champ calculé les valeurs du champ référence apparaissent dans une grille même en mode conception.



Un champ référence peut ensuite être utilisé comme source de données d'un composant TDBEdit.



Dans le cas de l'utilisation d'une grille, à l'exécution, en sélectionnant le champ de référence puis en appuyant sur la touche F2, il est possible de faire apparaître la flèche d'une combo-box qui, lorsqu'elle est activée donne accès à toutes les valeurs disponibles du champ correspondant de la table de référence. Il est alors possible de sélectionner une nouvelle valeur dont l'index correspondant sera pris en compte comme nouvelle valeur. Cette possibilité, aussi spectaculaire qu'elle soit, n'est à utiliser qu'avec parcimonie car, comme toute édition de table à partir d'une grille, de nombreuses erreurs de saisie peuvent survenir.

Page XIII.17

Programmation d'applications orientées données



En toute rigueur, s'il ne s'agit que de faire de l'affichage, on peut arriver au même résultat en réalisant une requête SQL adaptée. Ce dernier mécanisme, via la possibilité de réaliser des jointures est finalement plus facile à mettre en œuvre.

13.52 : TDBLookupComboBox et TDBLookupListBox Ces composants sont similaires aux composants TDBComboBox et TDBListBox mais la liste de valeurs proposées à l'utilisateur est déterminée dynamiquement à partir d'un deuxième ensemble de données ( table de référence ). Ils sont à utiliser prioritairement dans les formulaires de saisie où l'on souhaite proposer à l'utilisateur le "clair" des données dont seules les valeurs des clés seront stockées dans la table en cours de modification.



Les possibilités de ces composants sont supérieures à celles réalisées par des requêtes SQL qui, par défaut, ne remontent que des "vues" accessibles en "lecture".



On utilise plus fréquemment le composant TDBLookupComboBox que le composant List équivalent car il est plus pratique d'emploi.

Pour ces composants, de nouvelles propriétés doivent être renseignées : ListField ListFieldIndex ListSource DataField DataSource KeyField

Champ de la table de référence dont les valeurs seront stockées dans la combo box ou la list box.. Indique quel champ de la propriété ListField est utilisé pour la recherche incrémentale. Indique le composant DataSource associé à la table de référence. Identifie le champ de la table courante lié à la table de référence. Indique le composant DataSource associé à la table courante Champ de la table de référence servant de lien avec la table courante.

Exemple ( utilisant la base de données AUTO ) Un formulaire de saisie type, permettant de renseigner la table MODELES – table courante -, serait constitué comme suit : Déposer un composant Panel simulant le formulaire de saisie de la table TModeles. Déposer sur le panel, un composant TDBEdit connecté sur la table TModeles, via le datasource DSModeles. Initialiser sa propriété DataField à Nom ( Les renseignements à saisir dans le champ Nom sont directement entrés par l'utilisateur et sont stockés tels quels dans la table MODELES ). Éventuellement déposer un composant Label pour indiquer l'utilité de cette zone d'édition Déposer deux composants TDBLookupComboBox ( et deux labels associés ). Les nommer TDBLCBMarques et TDBLCBTypes et les initialiser comme suit ( cas de la TDBlookUpComboBox associée à la table Marques :

Page XIII.18

Programmation d'applications orientées données

A l'exécution le fait de sélectionner une marque "en clair" dans le composant TDBLookUpComboBox fera que la valeur du champ "clé" associé sera pris en compte dans le champ concerné de la table courante.

13.53 : Composant DBCtrlGrid Ce composant n'est apparu que dans les dernières versions de Delphi et C++ Builder. Il permet, à la différence d'un composant TDBGrid, d'afficher l'ensemble des enregistrements d'une table sous forme libre. Il suffit de créer un modèle du "formulaire" type souhaité pour que, à l'exécution une succession de formulaires apparaissent sous forme d'une liste accessible par un ascenseur. Il faut initialiser la propriété DataSource du composant DBCtrlGrid du nom de la datasource associée à la table manipulée. Ensuite il n'y a plus qu'à initialiser les propriété DataField des différents contrôles utilisés pour réaliser le formulaire type.

13.6 : Possibilités avancées 13.61 : Pose de marques

Page XIII.19

Programmation d'applications orientées données Il est parfois nécessaire de pouvoir marquer un enregistrement courant dans une table, puis de se déplacer dans celle-ci tout en conservant la possibilité de revenir à l'enregistrement de départ rapidement. Pour ce faire on utilise les possibilités de marquages offertes par C++ Builder en appelant les méthodes GetBookmark ( ), GoToBookmark () et FreeBookMark () du composant TTable.

-

GetBookMark ( ) est utilisé pour poser une marque sur un enregistrement particulier. Elle renvoie une donnée ( de type TBookMark ) qui est un pointeur sur un enregistrement. La fonction renvoie null si l'appel échoue. GoToBookMark ( ) permet de se déplacer vers une marque préalablement posée. FreeBookMark ( ) libère une marque posée.

 Il

est possible de poser plusieurs marques dans une même table. Mais il faudra penser ultérieurement à les libérer une à une. Exemple : TBookmark Bookmark ; // Déclaration d'une marque (variable globale) if ( ! BookMark ) // vérification que la marque n'est pas utilisée Bookmark = Table1 -> GetbookMark ( ) ; // Pose d'une marque < suite du programme >

if ( BookMark ) // Va à la marque si elle existe { Table1 -> GoToBookMark ( BookMark ); // Déplacement vers la marque < Suite du programme > Table1 -> FreeBookMark ( BookMark ); BookMark = NULL ; }

13.62 : Définition de filtres Il est possible d'appliquer des filtres à une table de manière à ce que seuls certains enregistrements soient affichés. Cela sans avoir à utiliser une requête SQL. Pour cela il faut utiliser les méthodes SetRangeStart ( ), SetRangeEnd (ou SetRange ()), ApplyRange et CancelRange ( ) du composant TTable. Un filtre peut être constitué de deux valeurs (une maximale et une minimale) indiquant les limites des valeurs affichées pour un champ donné. Exemple : TTable1 -> SetRangeStart ( ) ; TTable1 -> FieldByName ( "prix" ) -> AsInteger = 100 ; TTable1 -> SetRangeEnd ( ) ; Page XIII.20

Programmation d'applications orientées données TTable1 -> FieldByName ( "prix " ) -> AsInteger = 1000 ; TTable1 -> ApplyRange ( ) ; On aurait pu aussi faire : TTable1->SetRange(ARRAYOFCONST(("100")),ARRAYOFCONST(("1000"))); /* Dans cet exemple seuls les enregistrements dont la valeur du champ 'prix' est comprise entre 100 et 1000 seront affichés */

La méthode CancelRange ( ) supprime toutes les contraintes d'étendue appliquées préalablement avec les méthodes SetRange…

Page XIII.21

Related Documents

Programmation Bd
June 2020 16
Bd
November 2019 46
Bd
June 2020 27
Bd
November 2019 39
Bd
May 2020 34
Bd
November 2019 34