Gestion d’un calepin


Comment développer une application de gestion de données avec DELPHI
Image non disponible

L’application que nous vous proposons est une gestion de calepin. Le code source complet étant fourni sur la disquette, nous ne détaillerons que certains aspects essentiels du développement avec DELPHI. Vous apprendrez, par exemple, comment mettre en œuvre une recherche incrémentale et comment utiliser au mieux les composants et leurs événements.

Article lu   fois.

L'auteur

Site personnel

Liens sociaux

Viadeo Twitter Facebook Share on Google+   

I. Commencer une nouvelle application

Une fois DELPHI lancé, choisissez dans le menu « Fichier | Nouveau Projet ».

La première étape consiste à configurer la fiche selon le cahier des charges de l’application. Par exemple, il est inutile ici que la taille de la fiche puisse être changée au runtime. Vous passerez alors la propriété BorderStyle à bsSingle. Dans le même esprit, il est agréable que l’application apparaisse centrée à l’écran, quelles que soient les dimensions de ce dernier. Cela se fait en affectant la valeur poScreenCenter à la propriété Position.

Tous les objets posés sur une Form héritent par défaut de la fonte de celle-ci. Ainsi, afin de ne pas avoir à changer la fonte de chaque composant, vous choisirez directement ses caractéristiques au niveau de la Form elle-même. Pour notre application vous utiliserez Arial 8. Enfin, il faut choisir le texte qui sera affiché dans le bandeau supérieur de la fenêtre en modifiant la propriété Caption. Il s’agit généralement du titre de l’application.

I-A. Gérer l’espace de travail

La seconde étape consiste à partager l’espace de la fenêtre en unités plus petites. Vous devez utiliser des Panels en « jouant » sur la propriété gérant leur alignement. Le calepin utilise trois zones différentes. Vous poseriez ainsi trois Panels sur la fiche. Le premier étant aligné sur le haut de la fenêtre (propriété Align = alTop), le second sur le bas (Align = alBottom) et le troisième prenant tout l’espace restant (Align = alClient).
Attention, lorsque vous posez des Panels, faites-le sur le fond de la fiche et non les uns sur les autres… Les Panels peuvent contenir d’autres Panels, mais ceux-ci se trouvent alors limités à l’espace du Panel parent, ce qui n’est pas toujours souhaitable.
Les Panels possèdent une propriété Caption, celle-ci n’ayant pas d’intérêt lorsque les Panels servent de « conteneurs », effacez-les (plusieurs objets peuvent être sélectionnés en même temps en maintenant la touche Shift enfoncée pendant la sélection par le clic gauche de la souris).

I-B. Penser déjà à la sortie…

DELPHI propose, dans la palette « supplément », des boutons avec bitmap (type TbitBtn ou TSpeedButton). Utilisez un bouton de type TBitBtn et exploitez les styles prédéfinis en modifiant la propriété « Kind » du bouton. Il faut choisir « bkClose » pour obtenir à la fois le bitmap et le texte d’un bouton « Fermer ». Vous pouvez personnaliser le bouton en chargeant une autre bitmap et en changeant le texte.
Dans la page événement de l’inspecteur d’objets double-cliquez dans la zone de saisie de « OnClick » du bouton. DELPHI écrivant l’entête de la procédure automatiquement, vous ne tapez ici qu’une seule instruction : « Close ; » méthode de l’objet Tform effectuant la fermeture de la fiche.

I-C. Créer les fichiers

Le moteur de base de données étant relationnel, il faudrait d’ailleurs dire « tables » plutôt que fichiers. Il est généralement préférable d’avoir créé toutes les tables avant de débuter l’application, mais la flexibilité de DELPHI nous permet d’être moins « scolaire ».
Depuis le menu de DELPHI choisissez « Outils | Base de données » pour appeler le Database Desktop. Une fois cet utilitaire chargé, dans son menu, choisissez « Fichier | Nouveau | Table ». Le type de la table utilisée par le calepin est « Paradox 5.0 pour Windows », c’est un format performant, si vous n’avez pas de contraintes de développement particulières préférez-le au format dBase, plus « rustique ». Pour les perfectionnistes le moteur Interbase Local fourni avec DELPHI est un excellent choix. De plus, l’application sera portable en Client/Serveur très facilement.
Saisissez les champs, les index, puis enregistrez le fichier dans un répertoire ou dans un Alias.
Il n’est pas obligatoire de travailler avec les Alias, même si cela est préférable pour de nombreuses raisons (et obligatoire avec des bases SQL). Le calepin n’utilise pas d’Alias pour démontrer cette possibilité.
La définition de la table CALEPIN.DB se trouve sur la disquette dans le fichier « Table.txt »
(voir en fin de document).

L’index principal d’une table Paradox doit être une clé unique. Comme vous devez pouvoir saisir des homonymes dans votre calepin, il est plus malin de créer une clé primaire automatique. C’est la raison d’être du champ « Code Interne » de type auto-incrémental qui est marqué comme clé principale de la table. Ce champ n’est pas utilisé par l’application.

II. Placer les premiers composants

Une fois les tables créées vous placerez les composants de la fiche. Ici il s’agit de placer les objets en les groupant par région afin de se faire une idée de l’aspect général. Ne connectez pas les objets entre eux à ce stade, focaliser votre attention sur la disposition, sur le « look and feel » de votre application.

Vous placerez un couple Ttable/TDataSource par table sur la fiche, le DataSource étant relié à la table. La configuration de la table se limite généralement à entrer un Alias et à choisir la table dans la liste proposée par DELPHI. Le calepin n’utilise pas d’Alias pour démonter cette possibilité.

Offrir une sélection simple à l’utilisateur se fait en plaçant une grille de données (TDBGrid) sur la fiche. Si la grille n’est pas destinée à la saisie directe des données, ce qui est le cas du calepin, la propriété ReadOnly doit être à True et dans les options (marquées d’un signe plus) Editing doit être mis à False.

II-A. Gérer l’affichage d’une grille de données

La grille elle-même ne possède pas de paramètres pour choisir les champs de la table à visualiser. Il faut instancier des champs Tfields pour ce faire. Les Tfields sont des objets dont le rôle est d’effectuer une liaison entre chaque colonne de la table et l’application. Par défaut, lors de l’ouverture d’une table, DELPHI instancie automatiquement un Tfield par colonne. L’avantage est évident puisque c’est toujours cela de moins à programmer. Toutefois, il est généralement préférable de gérer les Tfields soi-même. Par exemple, si la structure de la table est modifiée, la méthode automatique de DELPHI ne le détectera pas. Dans certain cas une telle souplesse est souhaitable, mais que se passe-t-il si vous faites des calculs sur un champ que vous croyez être à virgule flottante alors qu’il est devenu un entier ?
Pour protéger vos applications, la création des Tfields lors du design de l’application est la meilleure solution. Si, à l’exécution, DELPHI découvre que les champs ne correspondent pas à ceux que vous avez créés, un message avertira l’utilisateur et la table ne sera pas ouverte. L’exception (erreur) qui sera générée pourra aussi être traitée plus finement.
Le type Tfield est un type générique décliné en sous-classes adaptées à chaque type de donnée géré par le moteur BDE. TStringField est le Tfield spécialisé pour les chaînes, TCurrencyField pour les zones monétaires, etc. Chaque Tfield possède des paramètres (propriétés) communs à toute la famille plus d’autres spécifiques au type de champ traité (par exemple un TIntegerField possède les propriétés MinValue et MaxValue pour créer des bornes de validité).
L’objet Tfield possède aussi des événements pour déclencher une notification lors d’un changement de contenu, pour transcoder les informations, pour gérer des validations sophistiquées, etc.
Accessoirement, les Tfields gèrent l’ordre d’affichage dans les grilles ainsi que le libellé de colonne (les noms de champs ne sont parfois pas très éloquents pour l’utilisateur).
Pour le calepin nous avons utilisé les propriétés Index et Visible des Tfields. La première gère l’ordre des colonnes dans une grille, la seconde la présence d’une colonne dans cette même grille.

II-B. Instancier des Tfields

Pour créer les Tfields lors du design vous pouvez sélectionner l’objet Ttable puis afficher le menu popup par un clic droit et choisir « Éditeur de champ » ou bien simplement double-cliquer sur l’objet Ttable directement.

Une première fenêtre (vide au départ) sera affichée, elle contient la liste des Tfields créés et permet de les afficher dans l’inspecteur d’objets. Choisissez « Ajouter ». Une seconde fenêtre s’ouvre alors. Elle contient toutes les colonnes de la table qui n’ont pas encore de Tfield associé.

Image non disponible

Une fois les Tfields créés, vous pouvez tester la grille pour la dimensionner en fonction des champs affichés.
DELPHI permet l’ouverture d’une table en mode design, utilisez cette facilité en passant la propriété « Active » de la table à True. Vous pouvez ainsi régler la largeur des colonnes, celle de la grille, modifier les libellés affichés, changer les fontes, tout en voyant les véritables données ou, au moins, les entêtes si la table est vide.
En ce sens DELPHI est plus puissant que tous les L4G qui ne permettent pas de voir les données pendant la création d’une fiche.
Une fois les réglages effectués, fermez la table, car il est préférable de gérer son ouverture par programmation. Toutefois dans un logiciel simple comme le calepin, il serait possible de laisser la table ouverte depuis le mode design.

III. Placer les autres composants

Il faut maintenant placer tous les champs de la table sur la fiche. Vous placerez aussi des Tlabel qui serviront à indiquer le nom de chaque champ à l’utilisateur.
Placez enfin un TDBNavigator en bas de la fiche. Selon les besoins de vos applications, n’oubliez pas qu’il est possible de n’afficher que certains boutons.
Dans le calepin la plupart des champs sont gérés avec des composants TDBEdit. Sauf les mémos (TDBMemo) et les champs « Civilité » et « Type de contact » respectivement gérés avec un TDBComboBox et un TDBRadioGroup.

Pour parfaire le placement, il faudra :

  • ajouter un ascenseur aux mémos (propriété ScrollBars) ;
  • créer la liste des choix pour les TDBCombo ;
  • faire de même pour les TDBRadioGroup et changer la propriété Columns par exemple ;
  • placer des masques d’édition dans les Tfields gérant les téléphones, le code postal ;
  • changer la couleur des fontes pour mettre en évidence certains champs ;
  • marquer ReadOnly les champs comme les dates de création et dernière modification ;
  • supprimer ces champs de la liste des tabulations (propriété TabStop = False) ;
  • ne pas oublier de connecter tous les composants à la base de données (DataSource et nom de champ) ;
  • … plus tous les changements d’aspect qui vous feront plaisir !

Le calepin gère deux index. Dans certaines applications les changements dépendent de conditions internes, dans le calepin c’est l’utilisateur qui fait ce choix. Pour gérer les deux index, il faudra ajouter un composant TRadioGroup de la palette Standard de DELPHI. Il contient deux items sur deux colonnes : Nom et Société. Sa propriété ItemIndex est initialisée à zéro (1er élément de la liste) puisque la table est affichée par ordre des noms par défaut.
Dans tous les cas semblables, n’oubliez pas d’initialiser la propriété IndexName de la table avec le nom de l’index par défaut (index par nom IX_Nom dans notre application).

IV. Les événements importants

Il est bon de noter que jusqu’ici vous n’avez pas entré une seule ligne de code, sauf l’ordre Close; il est vrai… Si l’application ne gérait pas deux index, elle pourrait même fonctionner dans l’état. C’est encore un point fort de DELPHI, car moins il y a de code, moins il y a de risque de trouver des bogues…

IV-A. Les dates automatiques

Il est souvent souhaitable de garder une trace des dates de création et de modification des données. Le calepin démontre la simplicité de mise en œuvre d’une telle technique.
La date de création ne doit être renseignée qu’une seule fois lors de la création d’un nouvel enregistrement. Lorsque vous devrez gérer de telles initialisations, il faudra utiliser l’événement « OnNewRecord » de l’objet Ttable.
Une seule ligne de code suffira : MonChampDateHeure.value :=Now;
Le nom du champ dépend de l’application et de la table bien entendu.
La propriété Value d’un Tfield est toujours du type de la colonne. Ainsi, la propriété Value d’un TIntegerField est de type LongInt, celle d’un TStringField est de type String, et celle d’un champ Date/Heure de type TdateTime.
« Now » est une fonction standard de DELPHI qui renvoie la date et l’heure courante.

Pour la date de dernière modification, il faut qu’elle soit modifiée à chaque fois que la fiche elle-même l’est. Toute modification passe par une validation, donc un « Post ». Vous utiliserez ici l’événement « BeforePost » du composant Ttable qui est déclenché juste avant que l’écriture réelle dans la table ne soit effectuée.
La ligne de code est identique à celle de la date de création.

IV-B. Gérer le changement d’index par l’utilisateur

Il faut gérer l’événement « OnClick » de TRadioGroup (qui offre les deux options « nom » et « société » dans le calepin). Le code consiste à changer la propriété IndexName de la table suivant la valeur de la propriété ItemIndex du TradioGroup.
Le changement d’index sera accompagné d’un changement de l’ordre d’affichage des trois colonnes dans la grille, de telle sorte que le champ utilisé comme clé soit toujours en colonne 1.

IV-C. Les valeurs par défaut

Certains champs d’une table peuvent avoir des valeurs par défaut. Vous placerez ce type d’initialisation dans l’événement « OnNewRecord » de la table déjà utilisé plus haut. Par exemple, le type de contact pourra être forcé à « personnel » ou à « professionnel » selon l’utilisation la plus fréquente qui sera faite du logiciel.

IV-D. L’ouverture d’une table et sa fermeture

La table doit être ouverte au lancement de l’application. Vous utiliserez l’événement « OnCreate » de la Form en y plaçant MaTable.Open;.
La fermeture doit s’effectue à la sortie de l’application. Ici vous utiliserez l’événement « OnDestroy » de la Form en y plaçant MaTable.Close;.
Difficile de concevoir programmation plus simple…

IV-E. Gérer une recherche incrémentale

Atteindre facilement un élément dans une table est un besoin de chaque instant. Proposer plusieurs index est une chose, encore faut-il les exploiter complètement. Vous y arriverez en plaçant un composant Tedit sur votre fiche et en connectant sur son événement « OnChange » le code suivant :

 
Sélectionnez
MaTable.SetRangeStart ; MaTable.ChampIndex.AsString :=Edit1.Text ; MaTable.ApplyRange ;

Le calepin gère deux index et s’adapte automatiquement. L’étude du code vous démontrera que cela reste très simple.

V. Améliorations diverses

Il reste encore des petits détails à gérer pour rendre une application « professionnelle ». La gestion du focus de Windows en fait partie. Par exemple, lors de la création d’un nouvel enregistrement il est intéressant de passer le focus au premier champ à saisir. Il faut aussi rendre inactifs les champs inutiles. Dans le calepin le Edit qui gère le nom de la société est désactivé si le type de contact est « personnel ».

VI. Pour conclure

Si les explications de cet article et l’étude du code de l’application calepin vous apprendront, nous l’espérons, certains « trucs » qui feront de vous des convaincus de DELPHI, rappelez-vous que c’est en bûchant qu’on devient bûcheron… À vos claviers !

Textes supplémentaires suivant place disponible en fonction de la pagination

Des copies d’écran Ecran1,2, 3 et 4 au format BMP sont jointes au présent document.

Voici la définition de la table CALEPIN. DB :

Champ

Type

Taille

Clé Interne

Integer+

4 *

Nom

String

25

Prénom

String

25

Civilité

String

10

Type Contact

String

15

Société

String

30

Téléphone

String

18

Télécopie

String

18

Email

String

30

Adresse

Memo

120

Code Postal

String

5

Ville

String

25

Commentaire

Memo

1

Date de Création

DateTime

8

Dernière modification

DateTime

8

     

Index

   

IX_Nom

= Nom + Prénom

 

IX_Société

= Société + Nom + Prénom

 

Index principal

= Code Interne

 

VI-A. Création de la table sous Database Desktop

Image non disponible
Image non disponible

Source de l’application : calapp.zip

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
57.
58.
59.
60.
61.
62.
63.
64.
65.
66.
67.
68.
69.
70.
71.
72.
73.
74.
75.
76.
77.
78.
79.
80.
81.
82.
83.
84.
85.
86.
87.
88.
89.
90.
91.
92.
93.
94.
95.
96.
97.
98.
99.
100.
101.
102.
103.
104.
105.
106.
107.
108.
109.
110.
111.
112.
113.
114.
115.
116.
117.
118.
119.
120.
121.
122.
123.
124.
125.
126.
127.
128.
129.
130.
131.
132.
133.
134.
135.
136.
137.
138.
139.
140.
141.
142.
143.
144.
145.
146.
147.
148.
149.
150.
151.
152.
153.
154.
155.
156.
157.
158.
159.
160.
161.
162.
163.
164.
165.
166.
167.
168.
169.
170.
171.
172.
173.
174.
175.
176.
{ CALEPIN.EXE
 Gestion d'un calepin
 Copyright Olivier Dahan
 CompuServe: 100531,163
 V 1.0 Mars 1996
}
unit Unit1;
interface
uses
 SysUtils, WinTypes, WinProcs, Messages, Classes, Graphics, Controls,
 Forms, Dialogs, DB, DBTables, Grids, DBGrids, StdCtrls, Buttons,
ExtCtrls,
 DBCtrls, Mask;
type
 TForm1 = class(TForm)
 Panel1: TPanel;
 Panel2: TPanel;
 Panel3: TPanel;
 BitBtn1: TBitBtn;
 T_calepin: TTable;
 ds_Calepin: TDataSource;
 DBGrid1: TDBGrid;
 T_calepinNom: TStringField;
 T_calepinSocit: TStringField;
 T_calepinTlphone: TStringField;
 DBEdit1: TDBEdit;
 DBEdit2: TDBEdit;
 DBComboBox1: TDBComboBox;
 DBRadioGroup1: TDBRadioGroup;
 DBEdit3: TDBEdit;
 DBEdit4: TDBEdit;
 DBEdit5: TDBEdit;
 DBEdit6: TDBEdit;
 DBMemo1: TDBMemo;
 DBEdit7: TDBEdit;
 DBEdit8: TDBEdit;
 DBMemo2: TDBMemo;
 DBNavigator1: TDBNavigator;
 Label1: TLabel;
 Label2: TLabel;
 Label3: TLabel;
 Label4: TLabel;
 Label5: TLabel;
 Label6: TLabel;
 Label7: TLabel;
 Label8: TLabel;
 Bevel1: TBevel;
 Label9: TLabel;
 Edit1: TEdit;
 RadioGroup1: TRadioGroup;
 Label10: TLabel;
 Bevel2: TBevel;
 T_calepinClInterne: TIntegerField;
 T_calepinPrnom: TStringField;
 T_calepinCivilit: TStringField;
 T_calepinTypeContact: TStringField;
 T_calepinTlcopie: TstringField;
 T_calepinEmail: TStringField;
 T_calepinAdresse: TMemoField;
 T_calepinCodePostal: TStringField;
 T_calepinVille: TStringField;
 T_calepinCommentaire: TMemoField;
 T_calepinDatedeCration: TDateTimeField;
 T_calepinDerniremodification: TDateTimeField;
 DBEdit9: TDBEdit;
 DBEdit10: TDBEdit;
 Label11: TLabel;
 Label12: TLabel;
 Label13: TLabel;
 procedure BitBtn1Click(Sender: TObject);
 procedure T_calepinNewRecord(DataSet: TDataset);
 procedure T_calepinBeforePost(DataSet: TDataset);
 procedure RadioGroup1Click(Sender: TObject);
 procedure FormCreate(Sender: TObject);
 procedure FormDestroy(Sender: TObject);
 procedure Edit1Change(Sender: TObject);
 procedure DBRadioGroup1Click(Sender: TObject);
 procedure ds_CalepinDataChange(Sender: TObject; Field: TField);
 private
 ChampIndex : String; { recherche incrémentale multiindex }
 end;
var
 Form1: TForm1;
implementation
{$R *.DFM}
procedure TForm1.BitBtn1Click(Sender: TObject);
begin
{ fermeture de la fiche, donc ici de l'application }
close;
end;
procedure TForm1.T_calepinNewRecord(DataSet: TDataset);
begin
{ Date/heure de création }
T_calepinDatedeCration.value:=Now;
{ type de contact par défaut }
T_CalepinTypeContact.Value:=DBRadioGroup1.Items[0];
{ Focus sur le nom à saisir }
DBEdit1.SetFocus;
end;
procedure TForm1.T_calepinBeforePost(DataSet: TDataset);
begin
{ Date/heure de modification }
T_calepinDerniremodification.value:=now;
end;
procedure TForm1.RadioGroup1Click(Sender: TObject);
begin
{ Annulation de la recherche incrémentale éventuellement en cours }
T_Calepin.CancelRange;
{ Vidage de l'Edit servant à la recherche }
Edit1.Text:='';
{ gestion du changement d'index }
case RadioGroup1.ItemIndex of
0: begin
 { index par nom }
 T_calepin.IndexName:='IX_Nom';
 { placement des champs dans la grille }
 T_CalepinNom.index:=0;
 T_CalepinSocit.index:=1;
 T_CalepinTlphone.index:=2;
 { nom du champ index en cours }
 ChampIndex := 'Nom';
 end;
1: begin
 { index par société }
 T_Calepin.IndexName:='IX_Société';
 { placement des champs dans la grille }
 T_CalepinSocit.index:=0;
 T_CalepinNom.index:=1;
 T_CalepinTlphone.index:=2;
 { nom du champ index en cours }
 ChampIndex:='Société';
 end;
end;
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
{ La table se trouve toujours dans le répertoire de l'EXE }
T_Calepin.TableName:=ExtractFilePath(Application.Exename)+'CALEPIN.DB';
{ ouverture de la table }
T_Calepin.Open;
{ initialisation du champ d'index en cours }
ChampIndex := 'Nom';
{ initialisation du champ de recherche incrémentale }
Edit1.text := '';
end;
procedure TForm1.FormDestroy(Sender: TObject);
begin
{ fermeture de la table }
T_Calepin.Close;
end;
procedure TForm1.Edit1Change(Sender: TObject);
begin
{ Edit1 à changé, on positionne la table au plus proche }
{ début d'un "range" du BDE }
T_calepin.SetRangeStart;
{ on affecte au champ index en cours le texte de Edit1 }
T_Calepin.FieldByName(ChampIndex).AsString:=Edit1.Text;
{ fin de l'opération "range" et application du résultat }
T_Calepin.ApplyRange;
end;
procedure TForm1.DBRadioGroup1Click(Sender: TObject);
begin
{ si "Société" est choisi alors DBedit3 est disponible }
DBEdit3.Enabled:= (dbradiogroup1.itemindex=1);
end;
procedure TForm1.ds_CalepinDataChange(Sender: TObject; Field: TField);
begin
{ Field=nil teste le changement de tous les champs en même temps,
 donc un changement d'enregistrement...
 On peut ainsi mettre en/hors le champ de saisie "société" suivant
 le contenu du champ qui vient juste d'être lu. }
if Field=Nil
 then DBEdit3.Enabled:=
 T_CalepinTypeContact.value='Travail';
end;
end.

Vous avez aimé ce tutoriel ? Alors partagez-le en cliquant sur les boutons suivants : Viadeo Twitter Facebook Share on Google+   

  

Copyright © Mar 1996 Olivier Dahan. Aucune reproduction, même partielle, ne peut être faite de ce site ni de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts.