Ma première base de donnée avec C++ Builder

Le but de ce tutorial est la réalisation d'une petite application incorporant une base de donnée. J'ai choisi aléatoirement comme sujet de faire un petit carnet d'adresse. Il s'agit en fait de la première application que j'ai fait avec C++Builder (à l'époque en version 3) pour interfacer une base donnée. Le format de base de données choisi est Paradox, parce qu'il est à mon avis le plus simple à mettre en oeuvre pour un débutant.

Article lu   fois.

L'auteur

Liens sociaux

Viadeo Twitter Facebook Share on Google+   

Avant-propos

Les pré-requis pour ce tutorial sont :

  • savoir créer et sauvegarder un projet
  • la connaissance des composants TButton, TEdit, TMaskEdit, TPanel et TMainMenu, la classe AnsiString
  • avoir entendu parler de base de données et si possible en avoir utilisé
  • il est nécessaire d'avoir le BDE d'installé (pour C++ Builder 5, il faut donc la version professionnelle minimum...)

Dans ce tutorial, je n'utiliserai pas les contrôles visuels orientés "Base de données" comme les TDBEdit, TDBMemo, TDBNavigator, ... Néanmoins, j'y ai placé un TDBGrid mais en lecture seule, qui ne sert qu'à visualiser les opérations que je fais sur la table. Le but de ce tutorial est de voir comment on manipule par du code C++ une base de données, par l'intermédiaire du composant TTable.

1. Rappel sur les bases de données

C'est une façon de stocker l'information et d'établir des relations entre les différentes données. Par exemple, on a tous pester par les publicités qu'on reçoit dans notre boîte aux lettres, nous précisant que l'on vient de gagner une machine à laver... Ce qui est surprenant, c'est que la société émettrice possède nos coordonnées précises, ainsi que celles de nos voisins, amis, famille, etc...
Dans sa forme la plus basique, nous pouvons l'imaginer comme un tableau. Chaque colonne serait un renseignement comme "Nom, Prénom, adresse..." et chaque ligne correspondrait à une personne.
D'un point de vue informatique, une base de données est constituée de un ou plusieurs fichiers constitués de une ou plusieurs tables (= nos tableaux vus plus haut). Car suivant la complexité des renseignements et leur caractères répétitifs, on peut facilement envisager de relier deux tables entre elles. On parle alors de base de données relationnelle.
Bien sur il n'existe pas un seul format de base de données sur le marché ... Interbase, Oracle, Access, Paradox sont des noms très répandus dans les entreprises. On les choisit suivant le degré de complexité de la base, le nombre d'utilisateurs simultanés à utiliser la base ... et bien sur la grosseur du porte monnaie !
Pour notre exemple j'ai choisi Paradox pour sa simplicité : un fichier correspond à une seule et unique table, au contraire d'Access par exemple où toutes les tables sont stockées dans le même fichier ...
Une base de données Paradox est constituée d'un ou plusieurs fichiers Paradox, normalement situés dans le même répertoire.
Parfois suivant les ouvrages consacrés aux produits Borland, on parlera d'Alias de base de données. C'est une notion que j'ai choisi d'ignorer, car avant de jongler avec les différentes identités d'une base de données, il est préférable de voir comment elle fonctionne.

2. Première étape : la création du fichier Paradox

Pour cela nous utiliserons l'utilitaire fourni par Borland, appelé "Module Base de données" (pour ceux qui ont une version anglaise : Database Desktop).

On choisit le menu "Fichier->Nouveau->Table...". On doit avoir l'écran suivant :

Image non disponible

On choisit le format Paradox 7 et on valide.

Une nouvelle boite de dialogue apparaît :

Image non disponible

Elle sert à spécifier les champs de notre base de données.
Il faut remplir de manière à obtenir ceci :

Image non disponible

Une fois encore, j'ai choisi que tous les champs seraient du type alphanumérique dans un soucis de facilité. Seul le premier champs est du type "numérique auto-incrémenté".
On enregistre la table sous le nom "Data.db" et on la place dans le même répertoire que notre futur projet.

3. Deuxième étape : la création du projet et sa mise en forme

Sous C++ Builder, on va créer un nouveau projet et tout de suite l'enregistrer (un bon réflexe !). J'ai nommé le projet "Carnet" mais chacun fait ce qu'il veut ... L'unité principale est sauvegardée sous "UnitMain". Ma Form s'appelle "Main".

Voici son aspect général :

Image non disponible

Voici la déclaration des composants telle qu'on pourrait la voir dans le header "Main.h".

 
Sélectionnez

class TMain :  public TForm
{
__published:      // Composants gérés par l'EDI

TDBGrid *DBGrid1;
TGroupBox *GroupBox1;
TTable *Table;
TDataSource *DataSource1;
TLabel *Label1;
TLabel *Label2;
TLabel *Label3;
TLabel *Label4;
TLabel *Label5;
TLabel *Label6;
TLabel *Label7;
TLabel *Label8;
TButton *BoutonValider;
TButton *BoutonAnnulerSaisie;
TButton *BoutonSupprimer;
TButton *BoutonNouveau;
TButton *BoutonModifier;
TButton *BoutonRecherche;
TMaskEdit *TelMaskEdit;
TMaskEdit *CPMaskEdit;
TEdit *VilleEdit;
TEdit *AdresseEdit;
TEdit *PrenomEdit;
TEdit *NomEdit;
TEdit *FiltreNom;
TEdit *RechercheNomEdit;
TMainMenu *Menu;
TMenuItem *Fichier1;
TMenuItem *Nouveau1;
TMenuItem *Modifier1;
TMenuItem *Quitter1;
TMenuItem *Aide1;
TMenuItem *Apropos1;
TButton *BoutonSuivant;
TButton *BoutonPrecedant;
TButton *BoutonFirst;
TButton *BoutonLast;
...

Je recommande vivement pour la compréhension du tutorial de nommer les composants de la même façon.

La Form Main fait 650 en largeur (Width) et 230 en hauteur (Height).

Les propriétés importantes du TTable sont :

  • Active : cette propriété détermine si effectivement l'application est reliée à la table ou non.
  • DatabaseName : c'est le nom de la base de données ou de son Alias.
  • TableName : c'est le nom de la table. Chaque composant TTable ne peut être relié qu'à une seule et unique table. Dans notre cas (le format Paradox) la table correspond à un fichier.

Pour paramétrer notre TTable ("Table") :

  1. On détermine le nom de notre base de données dans la propriété DatabaseName. Par soucis de simplicité, nous allons laisser la base de données dans le même répertoire que notre application. Cette propriété restera donc vide, ce qui signifie au BDE qu'il doit chercher la base de données dans le répertoire de l'application.
  2. On choisit la table que l'on va contrôler par le composant. Si notre propriété DatabaseName est correctement remplie, le nom de notre fichier créé précédemment devrait apparaître dans la listbox. On le sélectionne.
  3. On met la propriété Active à true.

Si tout est correctement configuré, il ne se passe... rien (par contre il y aura des messages d'erreur dans le cas contraire). Voici une vue de l'inspecteur d'objets sur le TTable :

Image non disponible

On en profite pour paramétrer le TDBGrid qui va nous servir à visualiser nos manipulations. Pour cela, il faut paramétrer d'abord le TDataSource ! Il sert de lien entre les composants visuels comme les TDBEdit, TDBMemo ... et les dérivés de TDataSet comme TTable, TQuery, TADOTable ...

Les propriétés importantes du TDataSource sont :

  • AutoEdit : sert à déterminer si tous les contrôles visuels peuvent on non modifier la base de données. Dans notre cas, la réponse est false car la TDBGrid ne sert qu'à visualiser.
  • DataSet : sert à déterminer le composant qui est "relié" à la table, dans notre cas le TTable. Il doit logiquement apparaître dans la listbox. Il faut le selectionner bien sur.

Enfin dans la TDBGrid, il faut lui signaler par sa propriété DataSource sur quel composant DataSource elle doit se connecter. Prudence étant mère de sûreté, j'ai mis la propriété ReadOnly à true.
Sur l'exemple j'ai "enjolivé" la grille en modifiant ses propriétés dans ses propriétés Columns et Options. Là le bon goût étant subjectif, je laisse à chacun le soin de faire les modifications qu'il jugera nécessaires...

Voilà la partie visuelle terminée, on va pouvoir passer à ...

4. Troisième partie : codage de la partie fonctionnelle

Voyons un peu les principes de fonctionnement de l'application. Son but est de gérer un petit agenda et numéro de téléphone. Nous devons donc prévoir :

  • un moyen de saisir les données (pour chaque nouvelle personne que nous rencontrons ...)
  • un moyen de supprimer les données (en cas de brouilles mortelles et irrémédiables ...)
  • un ou plusieurs moyen(s) de retrouver les données (si on ne peut pas le consulter, un carnet ne sert à rien ...)
  • un moyen de modifier les données (en cas de déménagement ...)

Je rappelle que j'ai fait cette petite application dans un but didactique, mais aussi dans l'optique de m'en servir (et elle m'a servi longtemps...). J'avais donc rajouté quelques fioritures, comme par exemple rétracter la zone de saisie.

C'est pour cela que j'ai placé dans l'événement OnCreate de la Form le code suivant :

 
Sélectionnez

void __fastcall  TMain::FormCreate(TObject *Sender)
{
   Height=235; // Retracte la fenêtre ..
   FiltreNom->Text=""; // Vide le texte de FiltreNom
   RechercheNomEdit->Text=""; // Vide le texte de RechercheNomEdit
}

Notre application doit comporter des méthodes de modification, insertion, navigation dans les données.

Avant d'aller plus loin, il est intéressant de comprendre comment le TTable accède aux données.
Le TTable est un objet qui dérive de l'objet TDataSet. Il implémente donc des procédures de navigation et d'ajout/suppression d'enregistrements.
Comme on l'a vu dans notre rappel sur les bases de données, on peut facilement visualiser une table comme un tableau où les titres des colonnes seraient les noms des champs. C'est ainsi que notre composant TDBGrid représente notre fichier Paradox. Sur cette même TDBGrid, on remarque sur la gauche du composant un petit curseur. Il indique l'enregistrement sur lequel pointe le TTable. Car le composant TTable ne pointe que sur un seul et unique enregistrement. Donc il faudra impérativement positionner le TTable sur l'enregistrement que l'on veut modifier. Il y a pour cela plusieurs façon de faire. La TDBGrid en implémente une : il suffit de selectionner l'enregistrement pour que le TTable se positionne dessus. Toutefois si cette méthode est simple à mettre en oeuvre, elle est frustrante pour le développeur qui ne maîtrise pas le comportement du TTable.
Sur la Form, nous avons placé quatre boutons : Suivant, Précédent, Premier Enregistrement, Dernier Enregistrement, respectivement nommés : BoutonSuivant, BoutonPrecedent, BoutonFirst, BoutonLast.

Voici le code qu'il faut placer dans chaque événement OnClick de ces boutons :

 
Sélectionnez

void __fastcall TMain::BoutonSuivantClick(TObject *Sender)
{
  Table->Next(); // Déplace le curseur sur l'enregistrement suivant 
}
//---------------------------------------------------------------------------

void __fastcall TMain::BoutonPrecedantClick(TObject *Sender)
{
  Table->Prior(); // Déplace le curseur sur l'enregistrement précédent
}
//---------------------------------------------------------------------------

void __fastcall TMain::BoutonFirstClick(TObject *Sender)
{
  Table->First(); // Déplace le curseur sur le premier enregistrement 
}
//---------------------------------------------------------------------------

void __fastcall TMain::BoutonLastClick(TObject *Sender)
{
  Table->Last(); // Déplace le curseur sur le dernier enregistrement
}

A l'exécution (et sous réserve que votre table contienne des enregistrements !), on visualise très bien le résultat de ces méthodes grâce au curseur de la TDBGrid. Les méthodes Next() et Prior servent à se déplacer d'enregistrement en enregistrement dans le sens croissant ou décroissant. Les méthodes First() et Last() quant à elles servent à se positionner respectivement sur le premier et le dernier enregistrement.

Maintenant que nous savons nous déplacer dans les données, nous allons pouvoir y accéder, c'est à dire récupérer les valeurs pour les utiliser dans nos programmes. Pour cela regardons de plus près le code placé dans l'événement OnClick de BoutonModifier. Dans notre petit programme, ce bouton sert à afficher les données que nous voulons modifier.

 
Sélectionnez

void __fastcall TMain::BoutonModifierClick(TObject *Sender)
{
  // Boucle qui enclenche la procédure de modification d'une fiche client
  if((Table->RecordCount==0)&&(Table->State!=dsInsert))
   return; // quitte la procédure si la table est vide et qu'on est pas en insertion
  BoutonSupprime->Enabled = true; // Activation du bouton Supprimer
  BoutonNouveau->Enabled= false; // Désactive le bouton OK
  Fichier1->Enabled = false; // Désactive le menu fichier
  Height = 340; // Aggrandi la fenêtre
  NomEdit->Text=Table->FieldByName("Nom")->AsString; // Récupère le nom et le place dans le TEdit
  PrenomEdit->Text=Table->FieldByName("Prenom")->AsString; /* Récupère le prénom et le place dans 
  le TEdit */
  AdresseEdit->Text=Table->FieldByName("Adresse")->AsString; /* Récupère l'adresse et le place dans 
  le TEdit */
  VilleEdit->Text=Table->FieldByName("Ville")->AsString; // Récupère la ville et le place dans le TEdit
  TelMaskEdit->Text = Table->FieldByName("Tel")->AsString; /* Récupère le code postal et le place dans 
  le TMaskEdit */
  CPMaskEdit->Text = Table->FieldByName("CP")->AsString; // Récupère le téléphone et le place dans 
  le TMaskEdit */
}

Cette partie du code sert uniquement à remplir les TEdit avec le contenu du champs. Pour cela on utilise la méthode *TField __fastcall FieldByName(const AnsiString FieldName) de la classe TTable. Elle renvoie un pointeur sur un objet TField. Comme l'objet TField sert à accéder à un champs d'une base de données... à partir de cet objet on récupère la propriété AsString que l'on copie dans la propriété Text du TEdit (ou TMaskEdit...).

Cela sous-entend que l'on s'est positionné avant de vouloir lire les données.

Bien sur il y a aussi quelques petites fioritures aussi avec les activations de boutons et l'aggrandissement de la fenêtre pour afficher les zones de modifications.
On remarquera aussi le test qui vérifie qu'il y a quelque chose à modifier. La propriété RecordCount donne le nombre d'enregistrements dans la table. Si la table est vide il n'y a rien à modifier, sauf si on insère le premier enregistrement vide. C'est pour cela que l'on teste aussi la propriété Table->State pour savoir si on est en mode insertion ... Dans le cas contraire on quitte la boucle purement et simplement.

Une fois les données affichées et les modifications faites, il nous faut enregistrer notre travail. Pour cela, nous allons ajouter sur l'événement OnClick de BoutonValider le code suivant :

 
Sélectionnez

void __fastcall TMain::BoutonValiderClick(TObject *Sender)
{
  Table->Edit(); // Positionne la table en mode Edition
  Table->FieldByName("Nom")->Value=NomEdit->Text; //Place le nom dans le TEdit dans la table
  Table->FieldByName("Prenom")->Value = PrenomEdit->Text ; //Place le prénom dans le TEdit dans la table
  Table->FieldByName("Adresse")->Value = AdresseEdit->Text ; //Place l'adresse dans le TEdit dans la table
  Table->FieldByName("Ville")->Value = VilleEdit->Text; //Place la ville dans le TEdit dans la table
  Table->FieldByName("Tel")->Value = TelMaskEdit->Text; //Place le téléphone dans le TEdit dans la table
  Table->FieldByName("Cp")->Value = CPMaskEdit->Text; //Place le code postal dans le TEdit dans la table
  Table->Post(); // Valide l'édition dans la base de données

  Fichier1->Enabled = true; // Active le menu

  BoutonSupprime->Enabled= true; //Active le bouton Supprimer
  BoutonNouveau->Enabled= true; //Active le bouton Nouveau
  BoutonModifier->Enabled= true; //Active le bouton Modifier
  Height = 235; // Retracte la fenêtre
}

Ici nous accédons au champs par la propriété Value. Ce qu'il faut surtout remarquer, c'est que l'on place d'abord la table en mode "édition" avec la méthode Table->Edit(). On signifie ainsi que les valeurs que nous plaçons dans les champs vont remplacer celles qui étaient stockées dans le fichier. Une fois que l'on a placé toutes nos valeurs, on signale au composant TTable qu'il doit valider les changements par la méthode Table->Post().

Occupons nous de la partie "Création d'un nouvel enregistrement". Pour cela sur l'événement OnClick de BoutonNouveau on rajoute la procédure suivante :

 
Sélectionnez

void __fastcall TMain::BoutonNouveauClick(TObject *Sender)
{
  Table->Append(); //Ajoute un enregistrement vide et se positionne dessus
  BoutonModifierClick(this); // appelle la méthode de modification
}

La méthode void Append(void) rajoute un enregistrement vide à la fin de la table et surtout positionne le curseur sur le nouvel enregistrement (c'est d'un compliqué ...).
Une fois le nouvel enregistrement créé, on appelle la méthode BoutonModifierClick qu'on a définie plus haut.

Enfin rajoutons maintenant la suppression d'enregistrements. On place dans l'événement OnClick de BoutonSupprimer le code suivant :

 
Sélectionnez

void __fastcall  TMain::BoutonSupprimerClick(TObject *Sender)
{
 // Appel de la fonction API Win32 boite de message pour confirmation
 int rep= MessageBox(Handle,"Etes-vous sûr de vouloir supprimer cette personne ?",
 "Avertissement",MB_OKCANCEL|MB_ICONSTOP ) ;
 if (rep ==IDOK) // test si réponse OK
   Table->Delete(); // Suppression de l'enregistrement

 BoutonNouveau->Enabled= true; //Activation du bouton nouveau
 Fichier1->Enabled = true; // active l'item du menu Fichier
 BoutonSupprime->Enabled = false; // Desactive le Bouton Supprime
 Height = 235; // Taille de la fiche à 220 pixels
}

La suppression de l'enregistrement se fait par la méthode Table->Delete(). Bien évidemment il faut se positionner d'abord sur l'enregistrement à supprimer.
De plus, le BDE ne demandant aucune confirmation pour éviter les erreurs, c'est à nous de le coder ! Pour cela on utilise la fonction de l'API MessageBox(...) qui invoque une boîte de dialogue.

Il existe deux autres fonctionnalités interessantes du composant TTable. Imaginons que nous avons un carnet d'adresse de Ministre. Pour retrouver le numéro de téléphone du coiffeur, si il faut appuyer quelques dizaines de fois sur le bouton "Suivant", on va se lasser très vite.

On va donc étudier la méthode Locate(...) à l'aide du code suivant que l'on saisit dans l'événement OnClick de BoutonRecherche (celui où est marqué Go !).

 
Sélectionnez

void __fastcall TMain::BoutonRechercheClick(TObject *Sender)
{

 if (!RechercheNomEdit->Text.IsEmpty()) // test pour voir si le Edit est vide
  {
   TLocateOptions Option; // Creer un objet option
   Option << loCaseInsensitive; // Rajout de l'option "ne fait pas attention à la casse"
   Table->Locate("Nom",RechercheNomEdit->Text,Option); /* Recherche l'enregistrement suivant
   les critères */
  }
}  

La méthode Locate permet de trouver un enregistrement à partir d'un champs. Ici nous recherchons un nom. Donc quand on va appeler la méthode Locate, le composant TTable va se positionner sur le premier enregistrement dont le champ "Nom" a pour valeur le nom que l'on a saisi dans le TEdit. La partie la plus délicate est l'instanciation d'un objet TLocateOptions. Cet objet sert à déterminer les options de tris. Deux choix sont possibles : ignorer la casse, et regarder partiellement la valuer. Les deux choix peuvent être simultanés.

La deuxième fonctionnalité interessante est le filtre. Nous allons implémenter dans notre petit programme un filtre sur les noms.
Plaçons ce code dans les événements OnChange et OnExit du TEdit FiltreNom.

 
Sélectionnez

void __fastcall TMain::FiltreNomChange(TObject *Sender)
{
   String Filtre = "Nom = '" + FiltreNom->Text + "*' "; /* Construction du Filtre avec le texte saisi dans 
   la zone TEdit*/
   if (FiltreNom->Text.IsEmpty()) // Test si il n'y a rien dans le TEdit
    {
      Table->Filtered=false; // Arrête le filtrage de la table
      return; // quitte la methode
     }
   Table->Filter = Filtre; // Mise en place du filtre
   Table->Filtered = true; // Activation du filtre
}

La construction du filtre se base sur le modèle suivant : Nom du champs, Opérateur de comparaison, Valeur de test.
Concrêtement le champs est une chaîne de caractères AnsiString que l'on place dans la propriété Filter du composant TTable.
Une fois la propriété Filter renseignée, il faut activer le filtre en mettant la propriété Filtered à true.
On remarquera ici l'usage du caractère '*' dans le filtre qui est un caractère générique.

Et voila il n'y a plus qu'à compiler le tout et observer.

Normalement on doit obtenir ceci :

Image non disponible

On va maintenant remplir notre carnet d'adresse, on clique sur le bouton Nouveau

Image non disponible

Une fois que nous aurons entré plus d'enregistrements, nous pourrons tester une à une les méthodes que nous avons implémentées.

Bon courage

Sources

, consultant
(TeamB-fr)

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

  

Copyright © 2001 Laurent Berne. Aucune reproduction, même partielle, ne peut être faite de ce site et 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.