Embarcadero C++Builder 6 Manuel utilisateur

Ajouter à Mes manuels
1420 Des pages
Embarcadero C++Builder 6 Manuel utilisateur | Fixfr
Guide du développeur
Borland ®
C++Builder 6
™
pour Windows
®
Reportez-vous au fichier DEPLOY situé dans le répertoire racine de votre produit C++Builder pour obtenir la liste
complète des fichiers que vous pouvez distribuer en accord avec les termes du contrat de licence de C++Builder.
Les applications mentionnées dans ce manuel sont brevetées ou en attente de brevet. Reportez-vous au CD du
produit ou à la boîte de dialogue A propos. Ce document ne donne aucun droit sur ces brevets.
COPYRIGHT © 1983-2002 Borland Software Corporation. Tous droits réservés. Tous les produits Borland sont des
marques commerciales ou des marques déposées de Borland Software Corporation aux Etats-Unis ou dans
les autres pays. Toutes les autres marques sont la propriété de leurs fabricants respectifs.
CPE1360WW21001 6E4R0102
02030405-9 8 7 6 5 4 3 2 1
PDF
Table des matières
Chapitre 1
Chapitre 4
Introduction
1-1
Contenu de ce manuel . . . . . . . . . . . . . . 1-1
Conventions typographiques. . . . . . . . . . . 1-3
Support technique . . . . . . . . . . . . . . . . . 1-3
Partie I
Programmation C++Builder
Chapitre 2
Développement d’applications
avec C++Builder
L’environnement de développement
Conception d’applications . . . . . .
Création des projets . . . . . . . . . .
Modification du code . . . . . . . . .
Compilation des applications . . . .
Débogage des applications . . . . . .
Déploiement des applications . . . .
2-1
intégré.
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
.
.
.
.
.
.
.
2-1
2-2
2-3
2-4
2-4
2-5
2-5
Chapitre 3
Utilisation des bibliothèques
de composants
Présentation des bibliothèques de classes .
Propriétés, méthodes et événements . .
Propriétés . . . . . . . . . . . . . . . .
Méthodes . . . . . . . . . . . . . . . .
Evénements . . . . . . . . . . . . . . .
Evénements utilisateur. . . . . . . . .
Evénements système . . . . . . . . . .
Objets, composants et contrôles . . . . . . .
Branche TObject . . . . . . . . . . . . . .
Branche TPersistent . . . . . . . . . . . .
Branche TComponent . . . . . . . . . . .
Branche TControl. . . . . . . . . . . . . .
Branche TWinControl/TWidgetControl .
3-1
.
.
.
.
.
.
.
.
.
.
.
.
.
. 3-1
. 3-2
. 3-2
. 3-3
. 3-3
. 3-3
. 3-4
. 3-4
. 3-6
. 3-7
. 3-7
. 3-9
. 3-10
i
Utilisation de BaseCLX
4-1
Utilisation des flux . . . . . . . . . . . . . . . .
Utilisation des flux pour lire ou écrire
des données . . . . . . . . . . . . . . . . .
Méthodes de flux pour la lecture
et l’écriture . . . . . . . . . . . . . . . .
Lecture et écriture de composants. . . .
Copie de données d’un flux vers un autre
Spécification de la position et de la taille
du flux . . . . . . . . . . . . . . . . . . . .
Déplacement sur une position
particulière . . . . . . . . . . . . . . . .
Utilisation des propriétés de position
et de taille. . . . . . . . . . . . . . . . .
Utilisation des fichiers . . . . . . . . . . . . . .
Approches des E/S fichier . . . . . . . . . .
Utilisation de flux de fichier . . . . . . . . .
Création et ouverture de fichiers
en utilisant des flux de fichier . . . . .
Utilisation du handle de fichier . . . . .
Manipulation de fichiers . . . . . . . . . . .
Suppression d’un fichier . . . . . . . . .
Recherche d’un fichier . . . . . . . . . .
Modification d’un nom de fichier . . . .
Routines date-heure de fichier . . . . . .
Copie d’un fichier . . . . . . . . . . . . .
Utilisation des fichiers ini et du registre. . . .
Utilisation de TIniFile et TMemIniFile .
Utilisation de TRegistryIniFile . . . . . .
Utilisation de TRegistry. . . . . . . . . .
Utilisation des listes . . . . . . . . . . . . . . .
Opérations de listes courantes. . . . . . . .
Ajout d’éléments de liste . . . . . . . . .
Suppression d’éléments de liste . . . . .
Accès aux éléments de la liste . . . . . .
Réorganisation d’éléments de liste . . .
Listes persistantes . . . . . . . . . . . . . . .
Utilisation des listes de chaînes. . . . . . . . .
Lecture et enregistrement
des listes de chaînes. . . . . . . . . . . . .
. 4-2
. 4-2
. 4-2
. 4-3
. 4-3
. 4-4
. 4-4
. 4-4
. 4-5
. 4-5
. 4-6
. 4-6
. 4-7
. 4-7
. 4-8
. 4-8
4-10
4-10
4-10
4-11
4-11
4-13
4-13
4-14
4-14
4-15
4-15
4-15
4-16
4-16
4-17
4-17
Création d’une nouvelle liste de chaînes .
Listes de chaînes à court terme . . . . .
Listes de chaînes à long terme . . . . .
Manipulation des chaînes d’une liste . . .
Comptage des chaînes d’une liste . . .
Accès à une chaîne spécifique . . . . .
Recherche d’éléments
dans une liste de chaînes . . . . . . .
Parcours des chaînes d’une liste . . . .
Ajout d’une chaîne à une liste . . . . .
Déplacement d’une chaîne
dans une liste . . . . . . . . . . . . . .
Suppression d’une chaîne d’une liste .
Association d’objets
à une liste de chaînes . . . . . . . . .
Utilisation des chaînes . . . . . . . . . . . . .
Routines manipulant les caractères
étendus . . . . . . . . . . . . . . . . . . .
Routines couramment utilisées
pour les AnsiStrings . . . . . . . . . . . .
Routines couramment utilisées
pour les chaînes à zéro terminal . . . . .
Impression . . . . . . . . . . . . . . . . . . . .
Conversion de mesures . . . . . . . . . . . . .
Exécution des conversions . . . . . . . . .
Exécution des conversions simples. . .
Exécution des conversions complexes .
Ajout de nouveaux types de mesure . . .
Création d’une famille de conversion
simple et ajout d’unités . . . . . . . . . .
Déclaration des variables . . . . . . . .
Recensement de la famille
de conversion . . . . . . . . . . . . . .
Recensement des unités de mesure . .
Utilisation des nouvelles unités . . . .
Utilisation d’une fonction de conversion .
Déclaration des variables . . . . . . . .
Recensement de la famille
de conversion . . . . . . . . . . . . . .
Recensement de l’unité de base . . . .
Ecriture des méthodes de conversion
à destination et à partir
de l’unité de base . . . . . . . . . . . .
Recensement des autres unités . . . . .
Utilisation des nouvelles unités . . . .
Utilisation d’une classe pour gérer
les conversions . . . . . . . . . . . . . . .
Création de la classe de conversion . .
Déclaration des variables . . . . . . . .
. 4-18
. 4-18
. 4-18
. 4-19
. 4-20
. 4-20
Recensez la famille de conversion
et les autres unités . . . . . . . . . . . . 4-34
Utilisation des nouvelles unités . . . . . 4-35
Création d’espaces de dessin . . . . . . . . . . 4-36
Chapitre 5
Utilisation des composants
. 4-20
. 4-20
. 4-20
5-1
Initialisation des propriétés d’un composant . . 5-2
Initialisation des propriétés à la conception . 5-2
Utilisation des éditeurs de propriétés. . . 5-3
Initialisation des propriétés à l’exécution . . 5-3
Appel de méthodes. . . . . . . . . . . . . . . . . 5-3
Utilisation des événements
et des gestionnaires d’événements . . . . . . . 5-4
Génération d’un nouveau
gestionnaire d’événement . . . . . . . . . . 5-4
Génération du gestionnaire de l’événement
par défaut d’un composant . . . . . . . . . 5-4
Recherche de gestionnaires d’événements . . 5-5
Association d’un événement à un gestionnaire
d’événement existant . . . . . . . . . . . . . 5-5
Utilisation du paramètre Sender. . . . . . 5-6
Affichage et codage d’événements
partagés . . . . . . . . . . . . . . . . . . . 5-6
Association d’événements de menu
à des gestionnaires d’événements . . . . . . 5-6
Suppression de gestionnaires d’événements 5-7
Composants multiplates-formes ou non
multiplates-formes . . . . . . . . . . . . . . . . 5-7
Ajout de composants personnalisés
à la palette des composants . . . . . . . . 5-10
. 4-21
. 4-21
. 4-21
. 4-22
. 4-22
. 4-23
. 4-26
. 4-27
. 4-28
. 4-28
. 4-28
. 4-29
. 4-29
. 4-29
. 4-30
Chapitre 6
. 4-30
. 4-30
. 4-30
. 4-31
. 4-31
Manipulation des contrôles
Implémentation du glisser-déplacer
dans les contrôles . . . . . . . . . . . . .
Début de l’opération glisser-déplacer
Acceptation des éléments à déplacer .
Déplacement des éléments. . . . . . .
Fin de l’opération glisser-déplacer . .
Personnalisation du glisser-déplacer
avec un objet déplacement . . . . . .
Changement du pointeur de la souris
Implémentation du glisser-ancrer
dans les contrôles . . . . . . . . . . . . .
Transformation d’un contrôle fenêtré
en un site d’ancrage. . . . . . . . . .
Transformation d’un contrôle
en un enfant ancrable . . . . . . . . .
. 4-31
. 4-31
. 4-32
. 4-32
. 4-32
. 4-32
. 4-33
. 4-34
ii
6-1
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
. 6-1
. 6-1
. 6-2
. 6-3
. 6-3
. . . . 6-4
. . . . 6-4
. . . . 6-4
. . . . 6-5
. . . . 6-5
Contrôle de l’ancrage des contrôles enfant . 6-6
Contrôle du désancrage
des contrôles enfant . . . . . . . . . . . . . 6-6
Contrôle de la réponse des contrôles enfant
aux opérations glisser-ancrer . . . . . . . . 6-7
Manipulation du texte dans les contrôles . . . 6-7
Définition de l’alignement du texte . . . . . 6-7
Ajout de barres de défilement
en mode exécution . . . . . . . . . . . . . . 6-8
Ajout de l’objet Clipboard . . . . . . . . . . 6-9
Sélection de texte. . . . . . . . . . . . . . . . 6-9
Sélection de la totalité d’un texte . . . . . . 6-9
Couper, copier et coller du texte . . . . . . . 6-10
Effacement du texte sélectionné . . . . . . . 6-10
Désactivation des éléments de menu . . . . 6-10
Ajout d’un menu surgissant . . . . . . . . . 6-11
Gestion de l’événement OnPopup . . . . . . 6-12
Ajout de graphiques à des contrôles . . . . . . 6-12
Spécification du style dessiné
par le propriétaire . . . . . . . . . . . . . . 6-13
Ajout d’objets graphiques
à une liste de chaînes . . . . . . . . . . . . 6-14
Ajout d’images à une application . . . . 6-14
Ajout d’images à une liste de chaînes . . 6-14
Dessiner des éléments dessinés
par le propriétaire . . . . . . . . . . . . 6-15
Dimensionnement des éléments dessinés
par le propriétaire . . . . . . . . . . . . . . 6-15
Dessin des éléments par le propriétaire. . . 6-16
Création de paquets et de DLL . . . . . . . . . 7-10
Utilisation des paquets et des DLL . . . . . 7-11
Utilisation des DLL dans C++Builder . . . . . 7-12
Création de DLL dans C++Builder. . . . . . . 7-12
Création de DLL contenant
des composants VCL et CLX . . . . . . . . . 7-13
Liaison de DLL . . . . . . . . . . . . . . . . . . 7-16
Ecriture d’applications de bases de données . 7-16
Distribution d’applications
de bases de données . . . . . . . . . . . . 7-17
Création d’applications serveur Web. . . . . . 7-18
Utilisation de WebBroker. . . . . . . . . . . 7-18
Création d’applications WebSnap . . . . . . 7-19
Utilisation d’InternetExpress . . . . . . . . 7-20
Création d’applications services Web. . . . 7-20
Ecriture d’applications en utilisant COM . . . 7-20
Utilisation de COM et de DCOM . . . . . . 7-21
Utilisation de MTS et de COM+ . . . . . . 7-21
Utilisation de modules de données . . . . . . 7-22
Création et modification de modules
de données standard . . . . . . . . . . . . 7-23
Nom d’un module de données
et de son fichier unité . . . . . . . . . . 7-23
Placer et nommer les composants . . . . 7-24
Utilisation des propriétés et événements
des composants dans un module
de données . . . . . . . . . . . . . . . . 7-25
Création de règles de gestion
dans un module de données . . . . . . 7-25
Accès à un module de données
depuis une fiche . . . . . . . . . . . . . . . 7-25
Ajout d’un module de données distant
à un projet serveur d’application . . . . . 7-26
Utilisation du référentiel d’objets. . . . . . . . 7-27
Partage d’éléments dans un projet . . . . . 7-27
Ajout d’éléments au Référentiel d’objets. . 7-27
Partage d’objets par une équipe
de développement. . . . . . . . . . . . . . 7-27
Utilisation d’un élément du référentiel
d’objets dans un projet . . . . . . . . . . . 7-28
Copie d’un élément . . . . . . . . . . . . 7-28
Héritage d’un élément . . . . . . . . . . 7-28
Utilisation d’un élément . . . . . . . . . 7-28
Utilisation de modèles de projet . . . . . . 7-29
Modification d’éléments partagés. . . . . . 7-29
Spécification d’un projet par défaut, d’une
nouvelle fiche et de la fiche principale . . 7-29
Activation de l’aide dans les applications . . . 7-30
Interfaces avec les systèmes d’aide . . . . . 7-30
Chapitre 7
Création d’applications, de composants
et de bibliothèques
7-1
Création d’applications . . . . . . . . . . . . . . 7-1
Applications d’interface utilisateur
graphique . . . . . . . . . . . . . . . . . . . 7-1
Modèles d’interfaces utilisateur . . . . . 7-2
Applications SDI . . . . . . . . . . . . . . 7-2
Applications MDI. . . . . . . . . . . . . . 7-2
Définition des options de l’EDI, du projet
et de la compilation . . . . . . . . . . . 7-3
Modèles de programmation . . . . . . . . . 7-3
Applications console . . . . . . . . . . . . . . 7-4
Utilisation de la VCL et de la CLX
dans les applications console . . . . . . 7-4
Applications service . . . . . . . . . . . . . . 7-4
Threads de service . . . . . . . . . . . . . 7-7
Propriétés de nom d’un service . . . . . 7-9
Débogage d’applications service . . . . . 7-10
iii
Implémentation de ICustomHelpViewer . . 7-31
Communication avec le gestionnaire
d’aide . . . . . . . . . . . . . . . . . . . . . 7-31
Demande d’informations
au gestionnaire d’aide . . . . . . . . . . . . 7-32
Affichage de l’aide basée sur un mot clé . . 7-33
Affichage des sommaires . . . . . . . . . . . 7-34
Implémentation de IExtendedHelpViewer . 7-34
Implémentation de IHelpSelector . . . . . . 7-35
Recensement des objets du système d’aide. 7-36
Recensement des visualiseurs d’aide . . 7-36
Recensement des sélecteurs d’aide . . . . 7-36
Utilisation de l’aide
dans une application VCL . . . . . . . . . . . 7-37
Comment TApplication traite-il l’aide VCL 7-37
Comment les contrôles VCL
traitent-ils l’aide . . . . . . . . . . . . . . . 7-37
Utilisation de l’aide
dans une application CLX . . . . . . . . . . . 7-38
Comment TApplication
traite-il l’aide CLX . . . . . . . . . . . . . . 7-38
Comment les contrôles CLX
traitent-ils l’aide . . . . . . . . . . . . . . . 7-38
Appel direct à un système d’aide . . . . . . . . 7-39
Utilisation de IHelpSystem . . . . . . . . . . . . 7-39
Personnalisation du système d’aide de l’EDI . 7-40
Transfert de paramètres supplémentaires
aux fiches . . . . . . . . . . . . . . . . . . . . 8-8
Récupération des données des fiches. . . . . 8-9
Récupération de données
dans les fiches non modales . . . . . . . 8-9
Récupération de données
dans les fiches modales . . . . . . . . . 8-10
Réutilisation des composants
et des groupes de composants . . . . . . . . 8-13
Création et utilisation
des modèles de composants . . . . . . . . . . 8-13
Manipulation des cadres. . . . . . . . . . . . . 8-14
Création de cadres . . . . . . . . . . . . . . 8-14
Ajout de cadres à la palette
des composants . . . . . . . . . . . . . . . 8-15
Utilisation et modification des cadres . . . 8-15
Partage des cadres . . . . . . . . . . . . . . 8-16
Développement de boîtes de dialogue. . . . . 8-17
Utilisation des boîtes de dialogue
d’ouverture . . . . . . . . . . . . . . . . . . 8-17
Organisation des actions pour les barres d’outils
et les menus . . . . . . . . . . . . . . . . . . . 8-18
Qu’est-ce qu’une action ?. . . . . . . . . . . 8-20
Définition des bandes d’actions . . . . . . . 8-21
Création des barres d’outils et des menus . 8-21
Ajout de couleurs, de motifs ou d’images
aux menus, boutons et barres d’outils 8-23
Ajout d’icônes aux menus
et aux barres d’outils . . . . . . . . . . 8-23
Création de barres d’outils et de menus
personnalisables par l’utilisateur . . . 8-24
Cacher les éléments et les catégories
inutilisés dans les bandes d’actions . . 8-25
Utilisation des listes d’actions. . . . . . . . . . 8-26
Définition des listes d’actions . . . . . . . . 8-26
Que se passe-t-il lors du déclenchement
d’une action ? . . . . . . . . . . . . . . . . 8-27
Réponse par les événements . . . . . . . 8-27
Comment les actions trouvent
leurs cibles . . . . . . . . . . . . . . . . 8-29
Actualisation des actions . . . . . . . . . . . 8-29
Classes d’actions prédéfinies . . . . . . . . 8-30
Conception de composants action . . . . . 8-31
Recensement d’actions . . . . . . . . . . . . 8-32
Création et gestion de menus . . . . . . . . . . 8-32
Ouverture du concepteur de menus . . . . 8-33
Construction des menus . . . . . . . . . . . 8-34
Nom des menus . . . . . . . . . . . . . . 8-34
Nom des éléments de menu . . . . . . . 8-34
Chapitre 8
Conception de l’interface utilisateur
des applications
8-1
Contrôle du comportement de l’application .
Manipulation de l’application . . . . . . .
Gestion de l’écran . . . . . . . . . . . . . .
Paramétrage des fiches . . . . . . . . . . . . .
Utilisation de la fiche principale . . . . . .
Cacher la fiche principale . . . . . . . . . .
Ajout de fiches . . . . . . . . . . . . . . . .
Liaison de fiches . . . . . . . . . . . . .
Gestion de la disposition . . . . . . . . . .
Utilisation des fiches . . . . . . . . . . . . . .
Contrôle du stockage en mémoire
des fiches . . . . . . . . . . . . . . . . . .
Affichage d’une fiche créée
automatiquement . . . . . . . . . . . .
Création dynamique de fiche . . . . . .
Création de fiches non modales
comme fenêtres . . . . . . . . . . . . .
Création d’une instance de fiche
en utilisant une variable locale . . . .
.
.
.
.
.
.
.
.
.
.
8-1
8-2
8-2
8-2
8-3
8-3
8-3
8-3
8-4
8-5
. 8-5
. 8-6
. 8-6
. 8-7
. 8-7
iv
Ajout, insertion et suppression
d’éléments de menu . . . . . . . . . . . 8-35
Ajout de lignes de séparation. . . . . . . 8-37
Spécification de touches accélératrices
et de raccourcis clavier . . . . . . . . . . 8-37
Création de sous-menus. . . . . . . . . . . . 8-37
Création de sous-menus par déplacement
de menus existants . . . . . . . . . . . . 8-38
Déplacement d’éléments de menu . . . . 8-38
Ajout d’images à des éléments de menu 8-39
Affichage du menu . . . . . . . . . . . . . 8-40
Edition des éléments de menu
dans l’inspecteur d’objets . . . . . . . . . . 8-40
Utilisation du menu contextuel
du concepteur de menus . . . . . . . . . . 8-41
Commandes du menu contextuel . . . . 8-41
Déplacement parmi les menus
à la conception . . . . . . . . . . . . . . 8-41
Utilisation des modèles de menu . . . . . . 8-42
Enregistrement d’un menu comme modèle 8-43
Conventions de nom pour les éléments
et les gestionnaires d’événements
des modèles de menu . . . . . . . . . . 8-44
Manipulation d’éléments de menu
à l’exécution. . . . . . . . . . . . . . . . . . 8-45
Fusion de menus . . . . . . . . . . . . . . . . 8-45
Spécification du menu actif :
propriété Menu . . . . . . . . . . . . . . 8-45
Ordre des éléments de menu fusionnés :
propriété GroupIndex . . . . . . . . . . 8-46
Importation de fichiers ressource . . . . . . 8-46
Conception de barres d’outils
et de barres multiples . . . . . . . . . . . . . . 8-47
Ajout d’une barre d’outils en utilisant
un composant volet . . . . . . . . . . . . . 8-48
Ajout d’un turbobouton à un volet . . . 8-48
Spécification du glyphe
d’un turbobouton . . . . . . . . . . . . . 8-49
Définition de l’état initial
d’un turbobouton . . . . . . . . . . . . . 8-49
Création d’un groupe de turboboutons . 8-49
Utilisation de boutons bascule . . . . . . 8-50
Ajout d’une barre d’outils en utilisant
le composant barre d’outils . . . . . . . . . 8-50
Ajout d’un bouton outil . . . . . . . . . . 8-50
Affectation d’images
à des boutons outil . . . . . . . . . . . . 8-51
Définition de l’aspect et des conditions
initiales d’un bouton outil . . . . . . . . 8-51
Création de groupes de boutons outil .
Utilisation de boutons outil bascule . .
Ajout d’un composant barre multiple . . .
Définition de l’aspect
de la barre multiple . . . . . . . . . . .
Réponse aux clics . . . . . . . . . . . . . . .
Affectation d’un menu
à un bouton outil . . . . . . . . . . . .
Ajout de barres d’outils cachées . . . . . .
Masquage et affichage d’une barre d’outils
8-52
8-52
8-52
8-53
8-53
8-53
8-54
8-54
Chapitre 9
Types de contrôles
9-1
Contrôles texte . . . . . . . . . . . . . . . . . . . 9-1
Contrôles de saisie . . . . . . . . . . . . . . . 9-2
Propriétés des contrôles de saisie . . . . . . . 9-3
Contrôles de saisie mémo
et texte formaté . . . . . . . . . . . . . . 9-3
Contrôles de visualisation de texte
(CLX seulement). . . . . . . . . . . . . . . . 9-4
Libellés . . . . . . . . . . . . . . . . . . . . . . 9-4
Contrôles de saisies spécialisées . . . . . . . . . 9-5
Barres de défilement . . . . . . . . . . . . . . 9-5
Barres graduées . . . . . . . . . . . . . . . . . 9-5
Contrôles flèches haut-bas (VCL seulement) 9-6
Contrôles incrémenteur (CLX seulement) . . 9-6
Contrôles touche d’accès rapide
(VCL seulement). . . . . . . . . . . . . . . . 9-7
Contrôles séparateur . . . . . . . . . . . . . . 9-7
Boutons et contrôles similaires . . . . . . . . . . 9-7
Contrôles bouton . . . . . . . . . . . . . . . . 9-8
Boutons bitmap . . . . . . . . . . . . . . . . . 9-8
Turboboutons . . . . . . . . . . . . . . . . . . 9-9
Cases à cocher . . . . . . . . . . . . . . . . . . 9-9
Boutons radio . . . . . . . . . . . . . . . . . . 9-9
Barres d’outils . . . . . . . . . . . . . . . . . 9-10
Barres multiples (VCL seulement) . . . . . 9-10
Contrôles liste . . . . . . . . . . . . . . . . . . . 9-11
Boîtes liste et boîtes liste de cases à cocher 9-11
Boîtes à options . . . . . . . . . . . . . . . . 9-12
Vues arborescentes . . . . . . . . . . . . . . 9-13
Vues liste . . . . . . . . . . . . . . . . . . . . 9-13
Sélecteurs Date/Heure et calendriers
mensuels (VCL seulement). . . . . . . . . 9-14
Regroupement de contrôles . . . . . . . . . . . 9-14
Boîtes groupe et groupes de boutons radio 9-14
Volets . . . . . . . . . . . . . . . . . . . . . . 9-15
Boîtes de défilement . . . . . . . . . . . . . 9-15
Contrôles onglets . . . . . . . . . . . . . . . 9-16
v
Contrôles pages . . . . . . . . . . . . . . .
Contrôles en-têtes . . . . . . . . . . . . . .
Contrôles d’affichage . . . . . . . . . . . . . .
Barres d’état. . . . . . . . . . . . . . . . . .
Barres de progression . . . . . . . . . . . .
Propriétés d’aide ou de conseil d’aide . .
Grilles . . . . . . . . . . . . . . . . . . . . . . .
Grilles de dessin . . . . . . . . . . . . . . .
Grilles de chaînes . . . . . . . . . . . . . .
Editeur de liste de valeurs (VCL seulement)
Contrôles graphiques . . . . . . . . . . . . . .
Images . . . . . . . . . . . . . . . . . . . . .
Formes . . . . . . . . . . . . . . . . . . . . .
Biseaux . . . . . . . . . . . . . . . . . . . .
Boîtes à peindre . . . . . . . . . . . . . . .
Contrôles animation (VCL seulement) . .
. 9-16
. 9-16
. 9-16
. 9-17
. 9-17
. 9-17
. 9-18
. 9-18
. 9-18
. 9-19
. 9-20
. 9-20
. 9-20
. 9-20
. 9-21
. 9-21
Enregistrement d’une image
dans un fichier . . . . . . . . . . . .
Remplacement de l’image . . . . . .
Utilisation du presse-papiers
avec les graphiques . . . . . . . . . . .
Copier des graphiques
dans le presse-papiers. . . . . . . .
Couper des graphiques
dans le presse-papiers. . . . . . . .
Coller des graphiques
depuis le presse-papiers . . . . . .
Techniques de dessin
dans une application . . . . . . . . . .
Répondre à la souris . . . . . . . . .
Ajout d’un champ à un objet fiche .
Amélioration du dessin des lignes .
Utilisation du multimédia . . . . . . . . . .
Ajout de séquences vidéo silencieuses
à une application . . . . . . . . . . . .
Exemple d’ajout de séquences vidéo
silencieuses . . . . . . . . . . . . . .
Ajout de séquences audio et/ou vidéo
à une application . . . . . . . . . . . .
Exemple d’ajout de séquences audio
et/ou vidéo (VCL seulement) . . .
Chapitre 10
Utilisation des graphiques
et du multimédia
Présentation de la programmation relative
aux graphiques . . . . . . . . . . . . . . . .
Rafraîchissement de l’écran . . . . . . . .
Types des objets graphiques . . . . . . .
Propriétés et méthodes communes
du canevas . . . . . . . . . . . . . . . .
Utilisation des propriétés
de l’objet canevas . . . . . . . . . . . .
Utilisation des crayons. . . . . . . . .
Utilisation des pinceaux . . . . . . . .
Lecture et définition de pixels . . . .
Utilisation des méthodes du canevas
pour dessiner des objets graphiques. .
Dessin de lignes et de polylignes . .
Dessin de formes . . . . . . . . . . . .
Gestion de plusieurs objets de dessin
dans votre application . . . . . . . . . .
Faire le suivi de l’outil de dessin
à utiliser . . . . . . . . . . . . . . . .
Changement d’outil en utilisant
un turbobouton . . . . . . . . . . .
Utilisation des outils de dessin . . . .
Dessiner sur un graphique . . . . . . . .
Création de graphiques défilables . .
Ajout d’un contrôle image . . . . . .
Chargement et enregistrement
de fichiers graphiques . . . . . . . . . .
Chargement d’une image
depuis un fichier . . . . . . . . . . .
10-1
. . 10-1
. . 10-2
. . 10-3
. . 10-4
.
.
.
.
. 10-23
. 10-23
. 10-23
. 10-24
.
.
.
.
.
10-25
10-25
10-28
10-29
10-31
. 10-31
. 10-32
. 10-33
. 10-35
Chapitre 11
Ecriture d’applications multithreads 11-1
. 10-5
. 10-6
. 10-8
10-10
Définition d’objets thread . . . . . . . . . . . . 11-2
Initialisation du thread . . . . . . . . . . . . 11-3
Affectation d’une priorité par défaut . . 11-3
Libération des threads . . . . . . . . . . 11-4
Ecriture de la fonction thread . . . . . . . . 11-4
Utilisation du thread principal
VCL/CLX . . . . . . . . . . . . . . . . . 11-4
Utilisation de variables locales
aux threads . . . . . . . . . . . . . . . . 11-6
Vérification de l’arrêt
par d’autres threads . . . . . . . . . . . 11-6
Gestion des exceptions
dans la fonction thread . . . . . . . . . 11-7
Ecriture du code de nettoyage . . . . . . . 11-7
Coordination de threads . . . . . . . . . . . . . 11-8
Eviter les accès simultanés . . . . . . . . . . 11-8
Verrouillage d’objets. . . . . . . . . . . . 11-8
Utilisation de sections critiques . . . . . 11-8
Utilisation du synchronisateur à écriture
exclusive et lecture multiple . . . . . . 11-9
. 10-10
. 10-10
. 10-12
. 10-13
. 10-13
.
.
.
.
.
. 10-21
. 10-22
10-14
10-14
10-18
10-18
10-18
. 10-20
. 10-21
vi
Autres techniques de partage
de la mémoire . . . . . . . . . . . . .
Attente des autres threads . . . . . . . .
Attente de la fin d’exécution
d’un thread . . . . . . . . . . . . . .
Attente de l’achèvement d’une tâche
Exécution d’objets thread . . . . . . . . . . .
Redéfinition de la priorité par défaut . .
Démarrage et arrêt des threads . . . . .
Débogage d’applications multithreads . . .
Nommer un thread . . . . . . . . . . . .
Conversion d’un thread anonyme
en thread nommé . . . . . . . . . . .
Affectation de noms distincts
à des threads similaires . . . . . . .
Classes d’exception VCL et CLX . . . . . 12-18
Considérations de portabilité . . . . . . . 12-20
. 11-10
. 11-10
.
.
.
.
.
.
.
Chapitre 13
Gestion en langage C++
de VCL et CLX
11-10
11-11
11-12
11-12
11-13
11-13
11-14
. 11-14
. 11-15
Chapitre 12
Gestion des exceptions
13-1
Les modèles objet en C++ et en Pascal Objet . 13-1
Héritage et interfaces . . . . . . . . . . . . . 13-2
Utilisation d’interface à la place
de l’héritage multiple . . . . . . . . . . 13-2
Déclaration des classes interfaces . . . . 13-2
IUnknown et IInterface . . . . . . . . . . 13-3
Création de classes gérant IUnknown . 13-4
Classes interfaces et gestion
de la durée de vie . . . . . . . . . . . . 13-5
Identification et instanciation des objets . . 13-6
Différences entre les références C++
et Pascal Objet . . . . . . . . . . . . . . 13-6
Copie d’objets . . . . . . . . . . . . . . . 13-7
Utilisation d’objets comme arguments
de fonction . . . . . . . . . . . . . . . . 13-8
Construction d’objets en C++Builder
pour les classes VCL/CLX . . . . . . . . . 13-8
Construction d’objets en C++ . . . . . . 13-8
Construction d’objets Pascal Objet . . . 13-8
Construction des objets C++Builder . . 13-8
Appels de méthodes virtuelles dans les
constructeurs des classes de base . . . . 13-10
Modèle Pascal Objet. . . . . . . . . . . .13-11
Modèle C++ . . . . . . . . . . . . . . . .13-11
Modèle C++Builder . . . . . . . . . . . .13-11
Exemple : appel de méthodes virtuelles13-11
Initialisation par le constructeur
des données membres
pour les fonctions virtuelles . . . . . 13-12
Destruction d’objets. . . . . . . . . . . . . 13-14
Exceptions déclenchées
dans les constructeurs. . . . . . . . . 13-14
Méthodes virtuelles appelées
dans les destructeurs . . . . . . . . . 13-15
AfterConstruction et BeforeDestruction . 13-15
Fonctions virtuelles de classe . . . . . . . 13-15
Gestion des types de données et des concepts
du langage Pascal Objet . . . . . . . . . . . 13-16
Typedefs . . . . . . . . . . . . . . . . . . . 13-16
Classes gérant le langage Pascal Objet . . 13-16
Equivalents dans le langage C++
du langage Pascal Objet . . . . . . . . . 13-17
Paramètres var . . . . . . . . . . . . . . 13-17
12-1
Gestion des exceptions C++ . . . . . . . . . . . 12-1
Syntaxe de gestion des exceptions . . . . . . 12-2
Le bloc try . . . . . . . . . . . . . . . . . . 12-2
L’instruction throw . . . . . . . . . . . . . 12-3
L’instruction catch . . . . . . . . . . . . . 12-3
Redéclenchement des exceptions . . . . . . 12-4
Spécifications des exceptions . . . . . . . . . 12-4
Déroulement des exceptions . . . . . . . . . 12-5
Pointeurs sécurisés . . . . . . . . . . . . . 12-6
Constructeurs dans la gestion d’exceptions 12-6
Gestion des exceptions non interceptées
et inattendues . . . . . . . . . . . . . . . . . 12-6
Exceptions structurées sous Win32 . . . . . . . 12-7
Syntaxe des exceptions structurées . . . . . 12-8
Gestion des exceptions structurées . . . . . 12-8
Filtres d’exceptions. . . . . . . . . . . . . . . 12-9
Mélange du C++ avec des exceptions
structurées. . . . . . . . . . . . . . . . . . 12-11
Exemple d’un programme C++
ayant des exceptions C. . . . . . . . . 12-12
Définition d’exceptions . . . . . . . . . . . 12-13
Déclenchement d’exceptions . . . . . . . . 12-13
Blocs de terminaison. . . . . . . . . . . . . 12-14
Options de gestion des exceptions
C++Builder . . . . . . . . . . . . . . . . . 12-15
Gestion des exceptions VCL/CLX. . . . . . . 12-16
Différences entre la gestion d’exceptions
C++ et VCL/CLX . . . . . . . . . . . . . 12-16
Gestion des exceptions du système
d’exploitation . . . . . . . . . . . . . . . . 12-17
Gestion des exceptions VCL et CLX. . . . 12-18
vii
Paramètres sans type. . . . . . . . . . .
Tableaux ouverts . . . . . . . . . . . . . . .
Calcul du nombre d’éléments. . . . . .
Temporaires . . . . . . . . . . . . . . . .
Tableau de constantes . . . . . . . . . .
Macro OPENARRAY . . . . . . . . . . .
Macro EXISTINGARRAY . . . . . . . .
Fonctions C++ attendant un argument
tableau ouvert . . . . . . . . . . . . . .
Types définis différemment . . . . . . . . .
Types de données booléens . . . . . . .
Types de données Char . . . . . . . . .
Interfaces Delphi . . . . . . . . . . . . . . .
Chaînes de ressource . . . . . . . . . . . .
Paramètres par défaut . . . . . . . . . . . .
Informations de type à l’exécution . . . .
Types sans correspondants . . . . . . . . .
Type réel sur six octets. . . . . . . . . .
Tableaux renvoyés par des fonctions .
Extensions des mots clés . . . . . . . . . .
__classid . . . . . . . . . . . . . . . . . .
__closure . . . . . . . . . . . . . . . . . .
__property . . . . . . . . . . . . . . . . .
__published . . . . . . . . . . . . . . . .
Extensions du mot clé __declspec . . . . .
__declspec(delphiclass) . . . . . . . . .
__declspec(delphireturn). . . . . . . . .
__declspec(delphirtti) . . . . . . . . . .
__declspec(dynamic) . . . . . . . . . . .
__declspec(hidesbase) . . . . . . . . . .
__declspec(package) . . . . . . . . . . .
__declspec(pascalimplementation) . . .
__declspec(uuid) . . . . . . . . . . . . .
13-17
13-18
13-18
13-19
13-19
13-20
13-20
Registre . . . . . . . . . . . . . . . . . . . 14-7
Autres différences . . . . . . . . . . . . . 14-8
Fonctionnalités manquantes dans la CLX . 14-9
Fonctionnalités non portées directement. . 14-9
Comparaison entre les unités CLX et VCL 14-10
Différences dans les constructeurs
d’objets CLX . . . . . . . . . . . . . . . . 14-13
Gestion des événements widget
et système . . . . . . . . . . . . . . . . . 14-14
Partage des fichiers source
entre Windows et Linux . . . . . . . . . 14-14
Différences d’environnement
entre Windows et Linux . . . . . . . . . 14-14
Structure de répertoires sous Linux . . . 14-17
Ecriture de code portable . . . . . . . . . 14-18
Utilisation des directives
conditionnelles . . . . . . . . . . . . . 14-19
Emission de messages . . . . . . . . . 14-20
Inclusion de code assembleur inline . 14-21
Différences de programmation
sous Linux . . . . . . . . . . . . . . . . . 14-22
Applications de bases de données
multiplates-formes . . . . . . . . . . . . . . 14-22
Différences de dbExpress . . . . . . . . . 14-23
Différences au niveau composant . . . . . 14-24
Différences au niveau
de l’interface utilisateur . . . . . . . . . 14-25
Portage d’applications
de bases de données vers Linux. . . . . 14-26
Mise à jour des données
dans les applications dbExpress. . . . . 14-28
Applications Internet multiplates-formes . . 14-30
Portage d’applications Internet
vers Linux . . . . . . . . . . . . . . . . . 14-31
13-20
13-20
13-21
13-21
13-21
13-22
13-22
13-23
13-24
13-24
13-24
13-25
13-25
13-25
13-27
13-29
13-29
13-29
13-30
13-30
13-30
13-30
13-31
13-31
13-31
Chapitre 14
Chapitre 15
Développement d’applications
multiplates-formes
14-1
Création d’applications multiplates-formes .
Portage d’applications Windows vers Linux.
Techniques de portage. . . . . . . . . . . .
Portages propres à une plate-forme . .
Portages multiplates-formes. . . . . . .
Portages d’émulation Windows . . . .
Portage de votre application . . . . . . . .
CLX et VCL . . . . . . . . . . . . . . . . . .
Différences de CLX . . . . . . . . . . . . .
Présentation visuelle . . . . . . . . . . .
Styles . . . . . . . . . . . . . . . . . . . .
Variants . . . . . . . . . . . . . . . . . .
. 14-1
. 14-2
. 14-2
. 14-3
. 14-3
. 14-3
. 14-3
. 14-5
. 14-6
. 14-6
. 14-7
. 14-7
Utilisation des paquets
et des composants
Pourquoi utiliser des paquets ? . . . . . . .
Les paquets et les DLL standard . . . . .
Paquets d’exécution . . . . . . . . . . . . . .
Utilisation des paquets
dans une application . . . . . . . . . . .
Paquets chargés dynamiquement . . . . .
Choix des paquets d’exécution à utiliser.
Paquets personnalisés . . . . . . . . . . .
Paquets de conception . . . . . . . . . . . . .
Installation de paquets de composants. .
Création et modification de paquets . . . . .
viii
15-1
. 15-2
. 15-2
. 15-3
.
.
.
.
.
.
.
15-3
15-4
15-4
15-5
15-5
15-6
15-7
Création d’un paquet . . . . . . . . . . . .
Modification d’un paquet existant . . . . .
Fichiers source de paquet et fichiers
d’options de projet . . . . . . . . . . . . .
Empaquetage des composants . . . . .
Présentation de la structure d’un paquet .
Nom de paquets . . . . . . . . . . . . .
Liste Requires . . . . . . . . . . . . . . .
Liste Contains . . . . . . . . . . . . . .
Construction des paquets . . . . . . . . . .
Directives de compilation
propres aux paquets . . . . . . . . . .
Utilisation du compilateur et du lieur
en ligne de commande . . . . . . . . .
Fichiers de paquets créés
lors d’une construction. . . . . . . . .
Déploiement de paquets . . . . . . . . . . . .
Déploiement d’applications utilisant
des paquets . . . . . . . . . . . . . . . . .
Distribution de paquets
à d’autres développeurs . . . . . . . . . .
Fichiers de collection de paquets . . . . .
. 15-7
. 15-8
Basculement dynamique de
de ressource . . . . . . . .
Localisation des applications .
Localisation des ressources
. 15-9
. 15-9
15-10
15-10
15-10
15-11
15-11
Chapitre 17
Déploiement des applications
17-1
Déploiement d’applications généralistes . . . 17-1
Utilisation des programmes d’installation . 17-2
Identification des fichiers
de l’application. . . . . . . . . . . . . . 17-3
Fichiers de l’application . . . . . . . . . 17-3
Fichiers paquet. . . . . . . . . . . . . . . 17-3
Modules de fusion. . . . . . . . . . . . . 17-4
Contrôles ActiveX . . . . . . . . . . . . . 17-5
Applications complémentaires . . . . . . 17-6
Emplacement des DLL . . . . . . . . . . 17-6
Déploiement d’applications CLX . . . . . . . . 17-6
Déploiement d’applications
de bases de données . . . . . . . . . . . . . . 17-7
Déploiement d’applications de bases
de données dbExpress . . . . . . . . . . . 17-8
Déploiement d’applications BDE . . . . . . 17-9
Le moteur de bases de données
Borland . . . . . . . . . . . . . . . . . . 17-9
SQL Links . . . . . . . . . . . . . . . . 17-10
Déploiement d’applications de bases de
données multiniveaux (DataSnap) . . . .17-11
Déploiement d’applications Web . . . . . . . .17-11
Déploiement sur serveur Apache . . . . . 17-12
Activation des modules. . . . . . . . . 17-12
Applications CGI . . . . . . . . . . . . 17-12
Programmation pour des environnements
hôtes hétérogènes . . . . . . . . . . . . . . . 17-13
Résolution d’écran et profondeur
de couleurs . . . . . . . . . . . . . . . . . 17-14
Si vous n’utilisez pas de redimensionnement
dynamique . . . . . . . . . . . . . . . 17-14
Si vous redimensionnez dynamiquement
les fiches et les contrôles . . . . . . . 17-14
Adaptation à des profondeurs de couleurs
variables. . . . . . . . . . . . . . . . . 17-16
Fontes . . . . . . . . . . . . . . . . . . . . . 17-16
Versions des systèmes d’exploitation . . . 17-17
Termes du contrat de licence logicielle . . . 17-17
DEPLOY . . . . . . . . . . . . . . . . . . . 17-17
README . . . . . . . . . . . . . . . . . . . 17-18
Contrat de licence . . . . . . . . . . . . . . 17-18
Documentation de produits
vendus par un tiers . . . . . . . . . . . . 17-18
15-12
15-13
15-14
15-14
15-15
15-15
15-15
Chapitre 16
Création d’applications
internationales
DLL
. . . . . . . . 16-13
. . . . . . . . 16-13
. . . . . . . . 16-13
16-1
Internationalisation et localisation. . . . . . . . 16-1
Internationalisation . . . . . . . . . . . . . . 16-1
Localisation . . . . . . . . . . . . . . . . . . . 16-2
Internationalisation des applications . . . . . . 16-2
Codage de l’application . . . . . . . . . . . . 16-2
Jeux de caractères. . . . . . . . . . . . . . 16-2
Jeux de caractères OEM et ANSI . . . . . 16-3
Jeux de caractères sur plusieurs octets . 16-3
Caractères larges . . . . . . . . . . . . . . 16-3
Inclure des fonctionnalités bi-directionnelles
dans les applications . . . . . . . . . . . 16-4
Propriété BiDiMode . . . . . . . . . . . . 16-6
Fonctionnalités spécifiques
aux cibles locales . . . . . . . . . . . . . 16-9
Conception de l’interface utilisateur. . . . . 16-9
Texte . . . . . . . . . . . . . . . . . . . . . 16-9
Images graphiques . . . . . . . . . . . . 16-10
Formats et ordre de tri. . . . . . . . . . 16-10
Correspondances entre claviers. . . . . 16-10
Isolement des ressources . . . . . . . . . . 16-11
Création de DLL de ressource . . . . . . . 16-11
Utilisation des DLL de ressource . . . . . 16-12
ix
Partie II
Edition des données affichées
dans un contrôle . . . . . . . . . . . . . 19-6
Activation et désactivation de l’affichage
des données . . . . . . . . . . . . . . . . . 19-7
Rafraîchissement de l’affichage
des données . . . . . . . . . . . . . . . . . 19-7
Activation des événements souris,
clavier et timer. . . . . . . . . . . . . . . . 19-8
Choix de l’organisation des données. . . . . . 19-8
Affichage d’un seul enregistrement. . . . . 19-8
Affichage de données
en tant que libellés. . . . . . . . . . . . 19-9
Affichage et édition de champs
dans une zone de saisie . . . . . . . . . 19-9
Affichage et édition de texte
dans un contrôle mémo . . . . . . . . 19-10
Affichage et édition dans un contrôle mémo
de texte formaté . . . . . . . . . . . . 19-10
Affichage et édition de champs graphiques
dans un contrôle image . . . . . . . . .19-11
Affichage de données dans des boîtes liste
et des boîtes à options . . . . . . . . .19-11
Manipulation de champs booléens
avec des cases à cocher . . . . . . . . 19-15
Limitation de valeurs de champ
avec des boutons radio . . . . . . . . 19-16
Affichage de plusieurs enregistrements . 19-17
Visualisation et édition des données
avec un contrôle TDBGrid . . . . . . . . . . 19-17
Utilisation d’un contrôle grille
à son état par défaut . . . . . . . . . . . 19-18
Création d’une grille personnalisée. . . . 19-19
Présentation des colonnes persistantes 19-20
Création de colonnes persistantes . . . 19-21
Suppression de colonnes persistantes. 19-22
Modification de l’ordre
des colonnes persistantes . . . . . . . 19-22
Définition des propriétés de colonne
en mode conception . . . . . . . . . . 19-23
Définition d’une colonne
de liste de référence . . . . . . . . . . 19-24
Insertion d’un bouton
dans une colonne . . . . . . . . . . . 19-24
Restauration des valeurs par défaut
d’une colonne . . . . . . . . . . . . . 19-25
Affichage des champs ADT et tableau . . 19-25
Définition des options de la grille . . . . 19-28
Saisie de modifications dans la grille. . . 19-29
Contrôle du dessin de la grille . . . . . . 19-30
Développement d’applications
de bases de données
Chapitre 18
Conception d’applications
de bases de données
18-1
Utilisation des bases de données . . . . . . . . 18-1
Types de bases de données . . . . . . . . . . 18-3
Sécurité des bases de données . . . . . . . . 18-4
Transactions . . . . . . . . . . . . . . . . . . . 18-5
Intégrité référentielle, procédures stockées
et déclencheurs . . . . . . . . . . . . . . . . 18-6
Architecture des bases de données . . . . . . . 18-6
Structure générale . . . . . . . . . . . . . . . 18-7
Fiche interface utilisateur . . . . . . . . . 18-7
Module de données . . . . . . . . . . . . 18-7
Connexion directe à un serveur
de bases de données . . . . . . . . . . . . . 18-9
Utilisation d’un fichier dédié sur disque . 18-10
Connexion à un autre ensemble
de données . . . . . . . . . . . . . . . . . 18-12
Connexion d’un ensemble de données client
à un autre ensemble de données
dans la même application . . . . . . . 18-14
Utilisation d’une architecture
multiniveau . . . . . . . . . . . . . . . 18-15
Combinaison des approches . . . . . . . . 18-16
Conception de l’interface utilisateur . . . . . 18-17
Analyse des données . . . . . . . . . . . . 18-18
Ecriture de rapports . . . . . . . . . . . . . 18-18
Chapitre 19
Utilisation de contrôles de données 19-1
Fonctionnalités communes
des contrôles de données . . . . . . . . . . . . 19-2
Association d’un contrôle de données
à un ensemble de données . . . . . . . . . 19-3
Modification de l’ensemble de données
associé à l’exécution . . . . . . . . . . . 19-4
Activation et désactivation
de la source de données . . . . . . . . . 19-4
Réponse aux modifications effectuées
par le biais de la source de données . . 19-4
Edition et mise à jour des données . . . . . 19-5
Activation de l’édition des contrôles
lors d’une saisie utilisateur . . . . . . . 19-5
x
Comment répondre aux actions de l’utilisateur
à l’exécution. . . . . . . . . . . . . . . . . 19-30
Création d’une grille qui contient d’autres
contrôles orientés données . . . . . . . . . . 19-31
Navigation et manipulation
d’enregistrements . . . . . . . . . . . . . . . 19-33
Choix des boutons visibles . . . . . . . . . 19-34
Affichage et dissimulation des boutons
en mode conception . . . . . . . . . . 19-34
Affichage et dissimulation des boutons
à l’exécution . . . . . . . . . . . . . . . 19-34
Affichage de panneaux d’information. . . 19-35
Utilisation d’un navigateur pour plusieurs
ensembles de données . . . . . . . . . . . 19-35
Ouverture et fermeture des champs
d’une grille de décision . . . . . . . . 20-12
Réorganisation des lignes et des colonnes
d’une grille de décision . . . . . . . . 20-12
Perforation pour voir les détails
dans les grilles de décision . . . . . . 20-13
Limite des dimensions à sélectionner
dans les grilles de décision . . . . . . 20-13
Propriétés des grilles de décision . . . . . 20-13
Création et utilisation de graphes
de décision . . . . . . . . . . . . . . . . . . . 20-14
Création de graphes de décision . . . . . 20-14
Utilisation de graphes de décision . . . . 20-15
Affichage du graphe de décision . . . . . 20-16
Personnalisation du graphe de décision . 20-17
Définition des modèles de graphe
de décision par défaut . . . . . . . . 20-18
Personnalisation des séries
d’un graphe de décision . . . . . . . 20-19
Utilisation des composants d’aide
à la décision à l’exécution . . . . . . . . . . 20-20
Pivots de décision à l’exécution . . . . . . 20-20
Grilles de décision à l’exécution . . . . . 20-21
Graphes de décision à l’exécution . . . . 20-21
Considérations relatives au contrôle
de la mémoire . . . . . . . . . . . . . . . . . 20-21
Définition du maximum de dimensions,
de champs récapitulatifs, et de cellules 20-22
Définition de l’état des dimensions. . . . 20-22
Utilisation de dimensions paginées. . . . 20-23
Chapitre 20
Utilisation de composants
d’aide à la décision
20-1
Présentation . . . . . . . . . . . . . . . . . . . . 20-1
Présentation des références croisées. . . . . . . 20-2
Références croisées à une dimension . . . . 20-3
Références croisées à plusieurs
dimensions . . . . . . . . . . . . . . . . . . 20-3
Instructions relatives à l’utilisation de composants
d’aide à la décision . . . . . . . . . . . . . . . 20-3
Utilisation d’ensembles de données
avec les composants d’aide à la décision . . . 20-5
Création d’ensembles de données de décision
avec TQuery ou TTable . . . . . . . . . . . 20-6
Création d’ensembles de données de décision
avec l’éditeur de requête de décision . . . 20-6
Utilisation des cubes de décision . . . . . . . . 20-7
Propriétés et événements
des cubes de décision . . . . . . . . . . . . 20-8
Utilisation de l’éditeur de cube de décision 20-8
Visualisation et modification des paramètres
de dimensions . . . . . . . . . . . . . . . 20-8
Définition du maximum de dimensions
et de récapitulations . . . . . . . . . . . 20-9
Visualisation et modification des options
de conception . . . . . . . . . . . . . . . 20-9
Utilisation de sources de décision . . . . . . . 20-10
Propriétés et événements . . . . . . . . . . 20-10
Utilisation de pivots de décision . . . . . . . 20-10
Propriétés des pivots de décision . . . . . 20-11
Création et utilisation de grilles de décision . 20-11
Création de grilles de décision . . . . . . . 20-12
Utilisation de grilles de décision . . . . . . 20-12
Chapitre 21
Connexion aux bases de données 21-1
Utilisation de connexions implicites . . . . .
Contrôles des connexions . . . . . . . . . . .
Connexion à un serveur
de bases de données . . . . . . . . . . .
Déconnexion d’un serveur
de base de données . . . . . . . . . . . .
Contrôle de la connexion au serveur . . . .
Gestion des transactions . . . . . . . . . . . .
Démarrage d’une transaction . . . . . . .
Achèvement d’une transaction . . . . . .
Achèvement d’une transaction réussie
Achèvement d’une transaction
non réussie . . . . . . . . . . . . . . .
Spécification du niveau d’isolation
des transactions . . . . . . . . . . . . . .
Envoi de commandes au serveur. . . . . . .
xi
. 21-2
. 21-3
. 21-3
.
.
.
.
.
.
21-4
21-4
21-6
21-7
21-9
21-9
. 21-9
21-10
.21-11
Utilisation d’ensembles de données associés
Fermeture d’ensembles de données
sans déconnexion du serveur . . . . . . .
Déplacement parmi les ensembles
de données associés . . . . . . . . . . . .
Obtention de métadonnées . . . . . . . . . . .
Enumération des tables disponibles . . . .
Enumération des champs d’une table . . .
Enumération des procédures stockées
disponibles . . . . . . . . . . . . . . . . .
Enumération des index disponibles . . . .
Enumération des paramètres
de procédure stockée . . . . . . . . . . .
21-13
Définition d’options de filtre . . . . . . . 22-18
Navigation parmi les enregistrements
d’un ensemble de données filtré . . . . 22-19
Modification des données . . . . . . . . . . . 22-20
Modification d’enregistrements . . . . . . 22-20
Ajout de nouveaux enregistrements . . . 22-21
Insertion d’enregistrements . . . . . . 22-22
Ajout d’enregistrements à la fin . . . . 22-23
Suppression d’enregistrements . . . . . . 22-23
Validation des données . . . . . . . . . . . 22-24
Annulation des modifications . . . . . . . 22-24
Modification d’enregistrements entiers. . 22-25
Champs calculés . . . . . . . . . . . . . . . . 22-26
Types d’ensembles de données . . . . . . . . 22-27
Utilisation d’ensembles de données
de type table . . . . . . . . . . . . . . . . . . 22-29
Avantages de l’utilisation des ensembles
de données de type table. . . . . . . . . 22-30
Tri des enregistrements avec des index . 22-30
Obtention d’informations sur les index 22-31
Spécification d’un index
avec IndexName . . . . . . . . . . . . 22-31
Création d’un index
avec IndexFieldNames . . . . . . . . 22-32
Utilisation d’index pour chercher
des enregistrements . . . . . . . . . . . . 22-32
Exécution d’une recherche
avec les méthodes Goto . . . . . . . . 22-33
Exécution d’une recherche
avec les méthodes Find . . . . . . . . 22-34
Spécification de l’enregistrement en cours
après une recherche réussie . . . . . 22-34
Recherche sur des clés partielles . . . 22-34
Réitération ou extension
d’une recherche . . . . . . . . . . . . 22-35
Limitation des enregistrements
avec des portées . . . . . . . . . . . . . . 22-35
Présentation des différences
entre les portées et les filtres . . . . . 22-35
Spécification de portées. . . . . . . . . 22-36
Modification d’une portée . . . . . . . 22-39
Application ou annulation
d’une portée . . . . . . . . . . . . . . 22-40
Création de relations maître/détail . . . . 22-40
Comment faire de la table la partie détail
d’un autre ensemble de données . . 22-41
Utilisation de tables détail imbriquées 22-43
Contrôle des accès en lecture/écriture
aux tables . . . . . . . . . . . . . . . . . . 22-44
21-13
21-14
21-14
21-15
21-15
21-15
21-16
21-16
Chapitre 22
Présentation
des ensembles de données
22-1
Utilisation des descendants de TDataSet . . . . 22-2
Détermination des états d’un ensemble
de données . . . . . . . . . . . . . . . . . . . . 22-3
Ouverture et fermeture des ensembles
de données . . . . . . . . . . . . . . . . . . . . 22-5
Navigation dans les ensembles de données . . 22-6
Utilisation des méthodes First et Last . . . . 22-7
Utilisation des méthodes Next et Prior . . . 22-7
Utilisation de la méthode MoveBy . . . . . 22-8
Utilisation des propriétés Eof et Bof. . . . . 22-9
Eof . . . . . . . . . . . . . . . . . . . . . . 22-9
Bof . . . . . . . . . . . . . . . . . . . . . 22-10
Marquage d’enregistrements . . . . . . . . 22-11
La propriété Bookmark . . . . . . . . . 22-11
La méthode GetBookmark . . . . . . . 22-11
Les méthodes GotoBookmark
et BookmarkValid . . . . . . . . . . . . 22-11
La méthode CompareBookmarks. . . . 22-11
La méthode FreeBookmark . . . . . . . 22-11
Un exemple d’utilisation de signets . . 22-12
Recherche dans les ensembles de données . . 22-12
Utilisation de la méthode Locate. . . . . . 22-12
Utilisation de la méthode Lookup . . . . . 22-13
Affichage et édition d’ensembles de données
en utilisant des filtres . . . . . . . . . . . . . 22-15
Activation et désactivation des filtres . . . 22-15
Création de filtres . . . . . . . . . . . . . . 22-16
Définition de la propriété Filter . . . . 22-16
Ecriture d’un gestionnaire d’événement
OnFilterRecord . . . . . . . . . . . . . 22-18
Permutation entre les gestionnaires
d’événements filtre à l’exécution . . . 22-18
xii
Création et suppression des tables. . . . . 22-44
Création de tables . . . . . . . . . . . . 22-44
Suppression de tables . . . . . . . . . . 22-47
Vidage des tables. . . . . . . . . . . . . . . 22-47
Synchronisation des tables . . . . . . . . . 22-48
Utilisation d’ensembles de données
de type requête. . . . . . . . . . . . . . . . . 22-48
Spécification de la requête . . . . . . . . . 22-49
Spécification d’une requête en utilisant
la propriété SQL . . . . . . . . . . . . 22-50
Spécification d’une requête en utilisant
la propriété CommandText . . . . . . 22-51
Utilisation de paramètres
dans les requêtes . . . . . . . . . . . . . . 22-51
Fourniture des paramètres
pendant la conception . . . . . . . . . 22-52
Fourniture des paramètres
pendant l’exécution. . . . . . . . . . . 22-53
Etablissement de relations maître/détail
en utilisant des paramètres . . . . . . . . 22-54
Préparation des requêtes . . . . . . . . . . 22-55
Exécution de requêtes qui ne renvoient pas
d’ensemble de résultats . . . . . . . . . . 22-56
Utilisation d’ensembles de résultats
unidirectionnels. . . . . . . . . . . . . . . 22-57
Utilisation d’ensembles de données de type
procédure stockée . . . . . . . . . . . . . . . 22-57
Utilisation de paramètres
avec les procédures stockées . . . . . . . 22-59
Définition des paramètres
pendant la conception . . . . . . . . . 22-60
Utilisation des paramètres
pendant l’exécution. . . . . . . . . . . 22-61
Préparation des procédures stockées . . . 22-62
Exécution de procédures stockées qui ne
renvoient pas d’ensemble de résultats. . 22-62
Lecture de plusieurs ensembles
de résultats . . . . . . . . . . . . . . . . . 22-63
Définition d’un champ de données . . . 23-7
Définition d’un champ calculé . . . . . . 23-8
Programmation d’un champ calculé . . 23-9
Définition d’un champ de référence . 23-10
Définition d’un champ agrégat . . . . 23-12
Suppression de champs persistants. . . . 23-12
Définition des événements et des propriétés
des champs persistants . . . . . . . . . . 23-13
Définition des propriétés d’affichage
et d’édition en mode conception . . 23-13
Définition des propriétés des composants
champ à l’exécution . . . . . . . . . . 23-15
Création des ensembles d’attributs
pour les composants champ . . . . . 23-15
Association des ensembles d’attributs
aux composants champ . . . . . . . . 23-16
Suppression des associations
d’ensembles d’attributs . . . . . . . . 23-16
Contrôle ou dissimulation de la saisie
utilisateur . . . . . . . . . . . . . . . . 23-17
Utilisation des formats par défaut pour les
champs numériques, date et heure . 23-17
Gestion des événements . . . . . . . . 23-18
Manipulation des méthodes de champ
lors de l’exécution . . . . . . . . . . . . . . 23-19
Affichage, conversion et accès aux valeurs
des champs . . . . . . . . . . . . . . . . . . 23-20
Affichage de valeurs dans les contrôles
standard . . . . . . . . . . . . . . . . . . 23-20
Conversion des valeurs de champs. . . . 23-21
Accès à des valeurs par la propriété par défaut
d’un ensemble de données . . . . . . . . 23-22
Accès à des valeurs par la propriété Fields
d’un ensemble de données . . . . . . . . 23-23
Accès à des valeurs par la méthode
FieldByName d’un ensemble
de données . . . . . . . . . . . . . . . . . 23-23
Définition de la valeur par défaut
d’un champ . . . . . . . . . . . . . . . . . . 23-24
Utilisation de contraintes . . . . . . . . . . . 23-24
Création de contrainte personnalisée . . . 23-24
Utilisation des contraintes du serveur . . 23-25
Utilisation des champs objet . . . . . . . . . 23-26
Affichage des champs ADT et tableau . . 23-27
Utilisation des champs ADT. . . . . . . . 23-27
Utilisation de composants champ
persistant . . . . . . . . . . . . . . . . 23-27
Utilisation de la méthode FieldByName
d’un ensemble de données . . . . . . 23-28
Chapitre 23
Manipulation
des composants champ
Composants champ dynamique . . . . .
Champs persistants . . . . . . . . . . . .
Création de champs persistants . . .
Modification de l’ordre des champs
persistants . . . . . . . . . . . . . . .
Définition de nouveaux champs
persistants . . . . . . . . . . . . . . .
23-1
. . . . 23-2
. . . . 23-3
. . . . 23-4
. . . . 23-6
. . . . 23-6
xiii
Utilisation de la propriété FieldValues
d’un ensemble de données . . . . . .
Utilisation de la propriété FieldValues
d’un champ ADT . . . . . . . . . . . .
Utilisation de la propriété Fields
d’un champ ADT . . . . . . . . . . . .
Utilisation des champs tableau. . . . . . .
Utilisation de champs persistants . . .
Utilisation de la propriété FieldValues
d’un champ tableau . . . . . . . . . .
Utilisation de la propriété Fields
d’un champ tableau . . . . . . . . . .
Utilisation des champs ensemble
de données . . . . . . . . . . . . . . . . .
Affichage des champs ensemble
de données. . . . . . . . . . . . . . . .
Accès aux données d’un ensemble
de données imbriqué . . . . . . . . . .
Utilisation de champs de référence . . . .
Affichage des champs de référence . .
Accès aux données d’un champ
de référence . . . . . . . . . . . . . . .
Liaison des paramètres . . . . . . . . . 24-13
Manipulation des procédures stockées
redéfinies d’Oracle. . . . . . . . . . . 24-13
Connexion aux bases de données
avec TDatabase . . . . . . . . . . . . . . 24-14
Association d’un composant
base de données à une session. . . . 24-14
Interactions entre les composants
base de données et session . . . . . . 24-15
Identification de la base de données . 24-15
Ouverture d’une connexion
avec TDatabase. . . . . . . . . . . . . 24-17
Utilisation des composants base de données
dans les modules de données . . . . 24-18
Gestion des sessions de bases de données 24-18
Activation d’une session . . . . . . . . 24-20
Spécification du comportement
de la connexion de base de données
par défaut . . . . . . . . . . . . . . . . 24-21
Gestion des connexions de bases
de données . . . . . . . . . . . . . . . 24-22
Manipulation des tables Paradox et dBASE
protégées par mot de passe . . . . . 24-24
Spécification des répertoires Paradox. 24-27
Manipulation des alias BDE . . . . . . 24-28
Récupération des informations
d’une session . . . . . . . . . . . . . . 24-29
Création de sessions supplémentaires 24-30
Affectation d’un nom à une session . 24-31
Gestion de sessions multiples . . . . . 24-32
Utilisation des transactions avec le BDE . . 24-34
Utilisation du SQL transparent . . . . . . 24-35
Utilisation de transactions locales . . . . 24-35
Utilisation du BDE pour placer
en mémoire cache les mises à jour . . . . . 24-36
Activation des mises à jour BDE
en mémoire cache . . . . . . . . . . . . . 24-38
Application des mises à jour BDE
en mémoire cache . . . . . . . . . . . . . 24-38
Application des mises à jour en mémoire
cache avec une base de données. . . 24-40
Application des mises à jour en mémoire
cache avec les méthodes de composant
base de données . . . . . . . . . . . . 24-41
Création d’un gestionnaire d’événement
OnUpdateRecord. . . . . . . . . . . . 24-41
Gestion des erreurs de mise à jour
en mémoire cache . . . . . . . . . . . 24-43
23-28
23-28
23-28
23-29
23-29
23-29
23-29
23-30
23-30
23-30
23-31
23-31
23-31
Chapitre 24
Utilisation du moteur de bases
de données Borland
24-1
Architecture BDE . . . . . . . . . . . . . . . . . 24-1
Utilisation d’ensembles de données BDE . . 24-2
Association d’un ensemble de données
avec les connexions de bases de données
et de session . . . . . . . . . . . . . . . . 24-3
Mise en cache des BLOBS . . . . . . . . . 24-4
Obtention d’un handle BDE. . . . . . . . 24-5
Utilisation de TTable. . . . . . . . . . . . . . 24-5
Spécification du type d’une table locale . 24-6
Contrôle d’accès en lecture/écriture
aux tables locales . . . . . . . . . . . . . 24-6
Spécification d’un fichier d’index
dBASE . . . . . . . . . . . . . . . . . . . 24-7
Renommer une table locale . . . . . . . . 24-8
Importation des données
d’une autre table . . . . . . . . . . . . . 24-8
Utilisation de TQuery . . . . . . . . . . . . . 24-9
Création de requêtes hétérogènes . . . 24-10
Obtention d’un ensemble de résultats
modifiable . . . . . . . . . . . . . . . . 24-11
Mise à jour des ensembles de résultats
en lecture seule . . . . . . . . . . . . . 24-12
Utilisation de TStoredProc . . . . . . . . . 24-13
xiv
Utilisation d’objets mise à jour pour mettre
à jour un ensemble de données . . . . . 24-45
Création d’instructions SQL
pour les composants mise à jour . . . 24-46
Utilisation de plusieurs objets
mise à jour . . . . . . . . . . . . . . . . 24-50
Exécution des instructions SQL. . . . . 24-52
Utilisation de TBatchMove . . . . . . . . . . . 24-55
Création d’un composant action groupée. 24-55
Spécification d’un mode d’action
groupée . . . . . . . . . . . . . . . . . . . 24-56
Ajout d’enregistrements . . . . . . . . . 24-57
Mise à jour d’enregistrements . . . . . 24-57
Ajout et mise à jour d’enregistrements 24-57
Copie d’ensembles de données . . . . . 24-57
Suppression d’enregistrements . . . . . 24-58
Mappage des types de données . . . . . . 24-58
Exécution d’une action groupée . . . . . . 24-59
Gestion des erreurs relatives
aux actions groupées. . . . . . . . . . . . 24-59
Dictionnaire de données . . . . . . . . . . . . 24-60
Outils de manipulation du BDE . . . . . . . . 24-62
Utilisation des ensembles
d’enregistrements . . . . . . . . . . . .25-11
Filtrage d’enregistrements
à partir de signets . . . . . . . . . . . 25-12
Lecture d’enregistrements
de façon asynchrone. . . . . . . . . . 25-13
Utilisation des mises à jour groupées. 25-13
Lecture et enregistrement des données
dans des fichiers . . . . . . . . . . . . 25-16
Utilisation de TADODataSet. . . . . . . . 25-17
Utilisation d’objets commande . . . . . . . . 25-19
Spécification de la commande . . . . . . . 25-19
Utilisation de la méthode Execute . . . . 25-20
Annulation des commandes . . . . . . . . 25-20
Récupération d’ensembles de résultats
à l’aide de commandes . . . . . . . . . . 25-21
Gestion des paramètres de commande. . 25-21
Chapitre 26
Utilisation d’ensembles de données
unidirectionnels
26-1
Types d’ensembles de données
unidirectionnels . . . . . . . . . . . . . . . . . 26-2
Connexion au serveur de bases de données . 26-3
Configuration de TSQLConnection . . . . . 26-4
Identification du pilote . . . . . . . . . . 26-4
Spécification des paramètres
de connexion . . . . . . . . . . . . . . . 26-4
Dénomination d’une description
de connexion . . . . . . . . . . . . . . . 26-5
Utilisation de l’éditeur de connexion . . 26-5
Spécification des données à afficher . . . . . . 26-6
Représentation des résultats d’une requête 26-7
Représentation des enregistrements
d’une table . . . . . . . . . . . . . . . . . . 26-7
Représentation d’une table en utilisant
TSQLDataSet . . . . . . . . . . . . . . . . 26-8
Représentation d’une table
en utilisant TSQLTable . . . . . . . . . 26-8
Représentation des résultats
d’une procédure stockée . . . . . . . . . . 26-8
Récupération des données. . . . . . . . . . . . 26-9
Préparation de l’ensemble de données . . . 26-9
Récupération de plusieurs ensembles
de données . . . . . . . . . . . . . . . . . 26-10
Exécution des commandes ne renvoyant pas
d’enregistrement . . . . . . . . . . . . . . . 26-10
Spécification de la commande à exécuter .26-11
Exécution de la commande . . . . . . . . .26-11
Chapitre 25
Utilisation des composants ADO
25-1
Présentation générale des composants ADO. . 25-2
Connexion à des stockages de données ADO . 25-3
Connexion à un stockage de données
avec TADOConnection . . . . . . . . . . . 25-3
Accès à l’objet connexion . . . . . . . . . 25-5
Optimisation d’une connexion . . . . . . . . 25-5
Connexions asynchrones . . . . . . . . . 25-5
Contrôle des dépassements de délais . . 25-6
Indication des types d’opérations
pris en charge par la connexion . . . . 25-6
Spécification de l’exécution automatique
des transactions par la connexion . . . 25-7
Accès aux commandes d’une connexion . . 25-8
Evénements connexion ADO . . . . . . . . . 25-8
Evénements se produisant pendant
l’établissement d’une connexion . . . . 25-8
Evénements se produisant pendant
la déconnexion . . . . . . . . . . . . . . 25-9
Evénements se produisant pendant
la gestion des transactions. . . . . . . . 25-9
Autres événements . . . . . . . . . . . . . 25-9
Utilisation des ensembles de données ADO . . 25-9
Connexion d’un ensemble de données ADO
à un stockage de données . . . . . . 25-10
xv
Création et modification des métadonnées
du serveur . . . . . . . . . . . . . . . . . . 26-12
Définition de curseurs liés maître/détail . . . 26-13
Accès aux informations de schéma . . . . . . 26-13
Récupération de métadonnées dans un
ensemble de données unidirectionnel . . 26-14
Lecture des données après l’utilisation
de l’ensemble de données
pour des métadonnées . . . . . . . . . 26-15
Structure des ensembles
de métadonnées. . . . . . . . . . . . . 26-15
Débogage d’applications dbExpress. . . . . . 26-19
Utilisation de TSQLMonitor pour contrôler
les commandes SQL . . . . . . . . . . . . 26-19
Utilisation d’un callback pour contrôler
les commandes SQL . . . . . . . . . . . . 26-20
Clonage d’un curseur d’ensemble
de données client . . . . . . . . . . . 27-17
Ajout d’informations d’application
aux données . . . . . . . . . . . . . . . . 27-18
Utilisation d’un ensemble de données client
pour mettre en cache les mises à jour . . . 27-18
Présentation de l’utilisation d’un cache
pour les mises à jour . . . . . . . . . . . 27-19
Choix du type d’ensemble de données
pour les mises à jour en cache. . . . . . 27-21
Indication des enregistrements modifiés . 27-22
Mise à jour des enregistrements . . . . . 27-24
Application des mises à jour . . . . . . 27-24
Intervention pendant l’application
des mises à jour . . . . . . . . . . . . 27-25
Conciliation des erreurs
de mise à jour . . . . . . . . . . . . . 27-27
Utilisation d’un ensemble de données client
avec un fournisseur. . . . . . . . . . . . . . 27-29
Spécification d’un fournisseur. . . . . . . 27-29
Extraction des données dans l’ensemble
de données ou le document source . . . 27-31
Extractions incrémentales. . . . . . . . 27-31
Extraction à la demande . . . . . . . . 27-32
Obtention de paramètres de l’ensemble
de données source. . . . . . . . . . . . . 27-32
Transmission de paramètres à l’ensemble
de données source. . . . . . . . . . . . . 27-33
Envoi de paramètres de requête
ou de procédure stockée . . . . . . . 27-34
Limitation des enregistrements
avec des paramètres . . . . . . . . . . 27-34
Gestion des contraintes liées au serveur . 27-35
Rafraîchissement des enregistrements . . 27-36
Communication avec des fournisseurs à l’aide
d’événements personnalisés . . . . . . . 27-37
Redéfinition de l’ensemble de données
source . . . . . . . . . . . . . . . . . . . . 27-38
Utilisation d’un ensemble de données client
avec des données basées sur des fichiers . 27-39
Création d’un nouvel ensemble
de données . . . . . . . . . . . . . . . . . 27-39
Chargement des données depuis un fichier
ou un flux . . . . . . . . . . . . . . . . . 27-40
Fusion des modifications
dans les données . . . . . . . . . . . . . 27-41
Sauvegarde des données dans un fichier
ou un flux . . . . . . . . . . . . . . . . . 27-41
Chapitre 27
Utilisation
d’ensembles de données client
27-1
Manipulation des données avec un ensemble
de données client . . . . . . . . . . . . . . . . 27-2
Navigation parmi les données des ensembles
de données client . . . . . . . . . . . . . . . 27-2
Limitation des enregistrements affichés. . . 27-3
Edition des données . . . . . . . . . . . . . . 27-6
Annulation des modifications. . . . . . . 27-6
Enregistrement des modifications . . . . 27-7
Définition de contraintes pour les valeurs
des données . . . . . . . . . . . . . . . . . . 27-8
Spécification de contraintes
personnalisées . . . . . . . . . . . . . . . 27-8
Tri et indexation . . . . . . . . . . . . . . . . 27-9
Ajout d’un nouvel index . . . . . . . . 27-10
Suppression et permutation d’index . . 27-11
Utilisation des index pour regrouper
les données . . . . . . . . . . . . . . . 27-11
Représentation des valeurs calculées . . . 27-12
Utilisation de champs calculés de façon
interne dans les ensembles de données
client . . . . . . . . . . . . . . . . . . . 27-13
Utilisation des agrégats maintenus . . . . 27-13
Spécification d’agrégats . . . . . . . . . 27-14
Agrégats de groupes
d’enregistrements . . . . . . . . . . . . 27-15
Obtention de valeurs d’agrégat. . . . . 27-16
Copie de données d’un autre ensemble
de données . . . . . . . . . . . . . . . . . 27-16
Affectation directe des données . . . . 27-16
xvi
Chapitre 28
Utilisation des composants
fournisseur
Structure de l’application client . . . . . . . 29-5
Structure du serveur d’applications . . . . 29-5
Contenu du module de données
distant . . . . . . . . . . . . . . . . . . . 29-6
Utilisation des modules de données
transactionnels . . . . . . . . . . . . . . 29-7
Regroupement des modules de données
distants . . . . . . . . . . . . . . . . . . 29-9
Sélection d’un protocole de connexion . . 29-10
Utilisation de connexions DCOM . . . 29-10
Utilisation de connexions Socket . . . .29-11
Utilisation de connexions Web . . . . .29-11
Utilisation de connexions SOAP. . . . 29-12
Construction d’une application multiniveau 29-13
Création du serveur d’applications . . . . . 29-13
Configuration du module de données
distant. . . . . . . . . . . . . . . . . . . . 29-15
Configuration d’un module de données
distant non transactionnel . . . . . . 29-15
Configuration d’un module de données
distant transactionnel . . . . . . . . . 29-16
Configuration de TSoapDataModule . 29-18
Extension de l’interface du serveur
d’applications . . . . . . . . . . . . . . . 29-18
Ajout de rappels à l’interface du serveur
d’applications. . . . . . . . . . . . . . 29-19
Extension de l’interface d’un serveur
d’applications transactionnel . . . . . 29-19
Gestion des transactions
dans les applications multiniveaux . . . 29-19
Gestion des relations maître / détail . . . 29-20
Gestion des informations d’état
dans les modules de données distants . 29-21
Utilisation de plusieurs modules
de données distants . . . . . . . . . . . . 29-23
Recensement du serveur d’applications . . . 29-24
Création de l’application client . . . . . . . . 29-24
Connexion au serveur d’applications. . . 29-25
Spécification d’une connexion
à l’aide de DCOM . . . . . . . . . . . 29-26
Spécification d’une connexion
à l’aide de sockets . . . . . . . . . . . 29-27
Spécification d’une connexion
à l’aide de HTTP . . . . . . . . . . . . 29-28
Spécification d’une connexion
à l’aide de SOAP. . . . . . . . . . . . 29-28
Courtage de connexions . . . . . . . . 29-29
Gestion des connexions serveur. . . . . . 29-29
Connexion au serveur. . . . . . . . . . 29-30
28-1
Spécification de la source de données . . . . . 28-2
Utilisation d’un ensemble de données comme
source des données. . . . . . . . . . . . . . 28-2
Utilisation d’un document XML comme source
des données . . . . . . . . . . . . . . . . . . 28-3
Communication avec l’ensemble de données
client . . . . . . . . . . . . . . . . . . . . . . . . 28-3
Détermination du mode d’application
des mises à jour à l’aide d’un fournisseur
d’ensemble de données . . . . . . . . . . . . . 28-4
Contrôle des informations placées
dans les paquets de données . . . . . . . . . . 28-5
Spécification des champs apparaissant
dans les paquets de données . . . . . . . . 28-5
Initialisation des options contrôlant
les paquets de données . . . . . . . . . . . 28-6
Ajout d’informations personnalisées
aux paquets de données . . . . . . . . . . . 28-7
Comment répondre aux demandes
de données des clients . . . . . . . . . . . . . 28-8
Comment répondre aux demandes
de mise à jour des clients . . . . . . . . . . . . 28-9
Modification des paquets delta avant
la mise à jour de la base de données . . 28-10
Comment contrôler l’application
des mises à jour . . . . . . . . . . . . . . 28-11
Filtrage des mises à jour . . . . . . . . . . 28-13
Résolution des erreurs de mise à jour
par le fournisseur . . . . . . . . . . . . . 28-13
Application des mises à jour à des ensembles
de données représentant plusieurs
tables . . . . . . . . . . . . . . . . . . . . . 28-14
Comment répondre aux événements
générés par le client . . . . . . . . . . . . . . 28-14
Gestion des contraintes du serveur . . . . . . 28-15
Chapitre 29
Création d’applications
multiniveaux
29-1
Avantages du modèle de base de données
multiniveau . . . . . . . . . . . . . . . . . . . . 29-2
Présentation des applications multiniveaux
basées sur les fournisseurs . . . . . . . . . . . 29-3
Présentation d’une application
à niveau triple . . . . . . . . . . . . . . . . 29-4
xvii
Fermeture ou changement
de connexion serveur. . . . . . . . . . 29-30
Appel des interfaces serveur . . . . . . . . 29-31
Connexion à un serveur d’applications qui
utilise plusieurs modules de données . . 29-32
Ecriture des applications client Web . . . . . 29-33
Distribution d’une application client en tant
que contrôle ActiveX. . . . . . . . . . . . 29-34
Création d’une fiche active
pour l’application client . . . . . . . . 29-34
Construction des applications Web
avec InternetExpress . . . . . . . . . . . . 29-35
Construction d’une application
InternetExpress . . . . . . . . . . . . . . . 29-36
Utilisation des bibliothèques javascript 29-37
Droits d’accès au serveur d’applications
et à son lancement . . . . . . . . . . . 29-38
Utilisation d’un courtier XML . . . . . . . 29-38
Lecture des paquets de données XML 29-39
Application des mises à jour
à partir des paquets delta XML. . . . 29-40
Création des pages Web avec un générateur
de page InternetExpress . . . . . . . . . . 29-41
Utilisation de l’éditeur de pages Web . 29-42
Définition des propriétés
des éléments Web . . . . . . . . . . . . 29-43
Personnalisation du modèle d’un générateur
de page InternetExpress . . . . . . . . 29-44
Chapitre 30
Utilisation de XML dans les applications
de bases de données
30-1
Définition des transformations. . . . . . . . .
Correspondance entre les nœuds XML
et les champs du paquet de données . .
Utilisation de XMLMapper . . . . . . . . .
Chargement d’un schéma XML
ou d’un paquet de données . . . . . .
Définition des mappages . . . . . . . .
Génération de fichiers
de transformation . . . . . . . . . . . .
Conversion de documents XML en paquets
de données . . . . . . . . . . . . . . . . . . .
Spécification du document XML source .
Spécification de la transformation . . . . .
. 30-1
. 30-2
. 30-4
. 30-5
. 30-5
. 30-6
. 30-7
. 30-7
. 30-7
Obtention du paquet de données résultant 30-8
Conversion de nœuds définis
par l’utilisateur . . . . . . . . . . . . . . . 30-8
Utilisation d’un document XML comme source
pour un fournisseur . . . . . . . . . . . . . . 30-9
Utilisation d’un document XML comme client
d’un fournisseur . . . . . . . . . . . . . . . 30-10
Lecture d’un document XML
à partir d’un fournisseur . . . . . . . . . 30-10
Application de mises à jour d’un document
XML à un fournisseur . . . . . . . . . . .30-11
Partie III
Ecriture d’applications Internet
Chapitre 31
Ecriture d’applications CORBA
31-1
Vue générale d’une application CORBA. . . . 31-2
Stubs et squelettes. . . . . . . . . . . . . . . 31-2
Utilisation de Smart Agent . . . . . . . . . 31-3
Activation d’applications serveur . . . . . . 31-4
Liaison dynamique d’appels d’interfaces . 31-4
Ecriture de serveurs CORBA . . . . . . . . . . 31-5
Définition d’interfaces d’objets . . . . . . . 31-5
Utilisation de l’expert serveur CORBA . . 31-6
Génération de stubs et de squelettes
à partir d’un fichier IDL . . . . . . . . . . 31-6
Utilisation de l’expert d’implémentation
d’objet CORBA . . . . . . . . . . . . . . . 31-7
Instanciation des objets CORBA . . . . . 31-8
Utilisation du modèle de délégation . . 31-9
Visualisation et changement
des modifications . . . . . . . . . . . 31-10
Implémentation des objets CORBA. . . . 31-10
Protection contre les conflits de thread 31-12
Modification des interfaces CORBA . . . 31-13
Recensement d’interfaces serveur. . . . . 31-13
Ecriture de clients CORBA . . . . . . . . . . 31-14
Utilisation des stubs . . . . . . . . . . . . 31-15
Utilisation de l’interface d’appel
dynamique . . . . . . . . . . . . . . . . . 31-16
Test des serveurs CORBA . . . . . . . . . . . 31-18
Configuration de l’outil de test . . . . . . 31-18
Enregistrement et exécution
de scripts de test. . . . . . . . . . . . . . 31-19
xviii
Chapitre 32
Création d’applications serveur
Internet
Type de méthode de requête . . . . . . . 33-7
Activation et désactivation
des éléments d’action . . . . . . . . . . 33-7
Choix d’un élément d’action par défaut 33-7
Réponse aux messages de requête
avec des éléments d’action . . . . . . . . . 33-8
Envoi de la réponse . . . . . . . . . . . . 33-9
Utilisation de plusieurs éléments
d’action . . . . . . . . . . . . . . . . . . 33-9
Accès aux informations de requêtes client . . 33-9
Propriétés contenant des informations
d’en-tête de requête . . . . . . . . . . . . . 33-9
Propriétés identifiant la destination. . 33-10
Propriétés décrivant le client Web. . . 33-10
Propriétés identifiant le but
de la requête . . . . . . . . . . . . . . 33-10
Propriétés décrivant la réponse
attendue . . . . . . . . . . . . . . . . . .33-11
Propriétés décrivant le contenu . . . . .33-11
Contenu d’un message de requête HTTP .33-11
Création de messages de réponse HTTP . . .33-11
Informations d’en-tête de réponse . . . . 33-12
Indication du statut de la réponse . . 33-12
Indication d’attente d’une action
du client. . . . . . . . . . . . . . . . . 33-12
Description de l’application serveur . 33-12
Description du contenu . . . . . . . . . 33-13
Définition du contenu de la réponse . . . 33-13
Envoi de la réponse. . . . . . . . . . . . . 33-13
Génération du contenu des messages
de réponse . . . . . . . . . . . . . . . . . . . 33-14
Utilisation du composant
générateur de page . . . . . . . . . . . . 33-14
Modèles HTML . . . . . . . . . . . . . 33-14
Choix du modèle HTML . . . . . . . . 33-15
Conversion des balises HTML
transparentes . . . . . . . . . . . . . . 33-16
Utilisation du générateur de page
depuis un élément d’action. . . . . . 33-16
Chaînage de générateurs de page . . . 33-17
Utilisation des bases de données
dans les réponses . . . . . . . . . . . . . . . 33-18
Ajout d’une session au module Web . . . 33-19
Représentation HTML
d’une base de données . . . . . . . . . . 33-19
Utilisation des générateurs
de page ensemble de données . . . . 33-19
Utilisation des générateurs de tableau 33-20
Choix des attributs de tableau . . . . . 33-20
32-1
A propos de WebBroker et de WebSnap . . . . 32-1
Terminologie et normes . . . . . . . . . . . . . . 32-3
Composition d’une URL
(Uniform Resource Locator) . . . . . . . . 32-4
URI et URL . . . . . . . . . . . . . . . . . 32-4
En-tête de message de requête HTTP . . . . 32-4
Activité d’un serveur HTTP . . . . . . . . . . . 32-5
Composition des requêtes client . . . . . . . 32-5
Traitement des requêtes client
par le serveur . . . . . . . . . . . . . . . . . 32-6
Réponses aux requêtes client . . . . . . . . . 32-6
Types d’applications serveur Web . . . . . . . . 32-7
ISAPI et NSAPI . . . . . . . . . . . . . . . 32-7
CGI autonome . . . . . . . . . . . . . . . 32-7
WinCGI autonome . . . . . . . . . . . . . 32-7
Apache . . . . . . . . . . . . . . . . . . . . 32-7
Débogueur d’application Web . . . . . . 32-8
Conversion des types cibles
d’applications serveur Web . . . . . . . . . 32-9
Débogage d’applications serveur . . . . . . . 32-10
Utilisation du débogueur
d’application Web . . . . . . . . . . . . . 32-10
Démarrage de l’application
avec le débogueur d’application Web 32-10
Conversion de votre application en un autre
type d’application serveur Web. . . . 32-11
Débogage d’applications Web
sous forme de DLL. . . . . . . . . . . . . 32-11
Droits des utilisateurs nécessaires au
débogage des DLL . . . . . . . . . . . 32-12
Chapitre 33
Utilisation de l’agent Web
Création d’applications serveur Web
avec l’agent Web . . . . . . . . . . . . .
Module Web . . . . . . . . . . . . . .
Objet application Web . . . . . . . . .
Structure d’une application agent Web .
Répartiteur Web . . . . . . . . . . . . . .
Ajout d’actions au répartiteur . . . .
Répartition des messages de requête
Eléments d’action . . . . . . . . . . . . .
Choix du déclenchement
des éléments d’action . . . . . . . .
URL de destination. . . . . . . . .
33-1
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
. 33-1
. 33-2
. 33-3
. 33-3
. 33-4
. 33-5
. 33-5
. 33-6
. . . . 33-6
. . . . 33-6
xix
Choix des attributs de lignes . . . .
Choix des attributs de colonnes . .
Incorporation de tableaux
dans un document HTML . . . . .
Configuration d’un générateur
de tableau ensemble de données .
Configuration d’un générateur
de tableau requête . . . . . . . . .
. . 33-21
. . 33-21
Etape 3. Ajout d’un composant
adaptateur . . . . . . . . . . . . . . . 34-16
Création d’une grille pour afficher
les données . . . . . . . . . . . . . . . . . 34-17
Etape 1. Ajout d’une grille . . . . . . . 34-17
Etape 2. Ajout de commandes
de modification à la grille . . . . . . 34-19
Ajout d’une fiche de modification . . . . 34-20
Etape 1. Ajout d’un nouveau module
de page Web . . . . . . . . . . . . . . 34-20
Etape 2. Enregistrement
du nouveau module . . . . . . . . . . 34-20
Etape 3. Mise à disposition
de CountryTableU pour le nouveau
module . . . . . . . . . . . . . . . . . 34-20
Etape 4. Ajout des champs de saisie . 34-20
Etape 5. Ajout de boutons . . . . . . . 34-21
Etape 6. Liaison entre les actions
de la fiche et la page de la grille. . . 34-22
Etape 7. Liaison des actions de grille
avec la page de la fiche . . . . . . . . 34-23
Exécution de l’application complète . . . 34-23
Ajout du compte-rendu des erreurs . . . 34-24
Etape 1. Ajout de la gestion des erreurs
à la grille . . . . . . . . . . . . . . . . 34-24
Etape 2. Ajout de la gestion des erreurs
à la fiche . . . . . . . . . . . . . . . . 34-24
Etape 3. Test du mécanisme de gestion
des erreurs . . . . . . . . . . . . . . . 34-24
Conception HTML avancée . . . . . . . . . . 34-25
Manipulation de script côté serveur
dans des fichiers HTML . . . . . . . . . 34-26
Prise en charge des ouvertures de session . 34-27
Ajout d’une prise en charge
des ouvertures de session . . . . . . . . 34-28
Utilisation du service de sessions . . . . . 34-29
Pages de connexion . . . . . . . . . . . . . 34-30
Configuration des pages nécessitant
des connexions. . . . . . . . . . . . . . . 34-32
Droits d’accès utilisateur . . . . . . . . . . 34-32
Affichage dynamique des champs sous la
forme de zones de saisie ou de texte 34-33
Masquage des champs
et de leur contenu . . . . . . . . . . . 34-34
Interdiction d’accès à la page . . . . . 34-34
Utilisation de scripts côté serveur
avec WebSnap . . . . . . . . . . . . . . . . . 34-34
Scripts actifs . . . . . . . . . . . . . . . . . 34-35
Moteur de script. . . . . . . . . . . . . . . 34-36
. . 33-21
. . 33-21
. . 33-22
Chapitre 34
Création d’applications serveur Web
avec WebSnap
34-1
Composants WebSnap de base. . . . . . . . .
Modules Web . . . . . . . . . . . . . . . . .
Types de module d’application Web . .
Modules de page Web . . . . . . . . . .
Modules de données Web . . . . . . . .
Adaptateurs . . . . . . . . . . . . . . . . . .
Champs . . . . . . . . . . . . . . . . . .
Actions . . . . . . . . . . . . . . . . . . .
Erreurs . . . . . . . . . . . . . . . . . . .
Enregistrements . . . . . . . . . . . . . .
Générateurs de page . . . . . . . . . . . . .
Création d’applications serveur Web
avec WebSnap . . . . . . . . . . . . . . . . .
Sélection d’un type de serveur . . . . . . .
Spécification des composants du module
d’application . . . . . . . . . . . . . . . .
Sélection des options du module
d’application Web . . . . . . . . . . . . .
Didacticiel WebSnap. . . . . . . . . . . . . . .
Création d’une nouvelle application . . .
Etape 1. Démarrage de l’expert
d’application WebSnap. . . . . . . . .
Etape 2. Enregistrement
des fichiers générés et du projet . . .
Etape 3. Spécification du titre
de l’application . . . . . . . . . . . . .
Création d’une page CountryTable . . . .
Etape 1. Ajout d’un nouveau module
de page Web. . . . . . . . . . . . . . .
Etape 2. Enregistrement du nouveau
module de page Web . . . . . . . . . .
Ajout des composants de données
au module CountryTable . . . . . . . . .
Etape 1. Ajout des composants
orientés données . . . . . . . . . . . .
Etape 2. Spécification d’un champ clé .
. 34-2
. 34-2
. 34-3
. 34-4
. 34-5
. 34-6
. 34-6
. 34-6
. 34-7
. 34-7
. 34-7
. 34-8
. 34-9
. 34-9
34-11
34-12
34-12
34-12
34-13
34-13
34-13
34-14
34-14
34-15
34-15
34-16
xx
Blocs de script . . . . . . . . . . . . . . . . 34-36
Création de scripts . . . . . . . . . . . . . . 34-36
Experts modèles . . . . . . . . . . . . . 34-36
TAdapterPageProducer . . . . . . . . . 34-36
Modification et visualisation des scripts . 34-37
Comment inclure un script
dans une page . . . . . . . . . . . . . . . 34-37
Objets de script . . . . . . . . . . . . . . . . 34-37
Répartition des requêtes et des réponses . . . 34-38
Composants répartiteur . . . . . . . . . . . 34-38
Fonctions d’un répartiteur d’adaptateur . 34-39
Utilisation de composants d’adaptateur
pour générer du contenu . . . . . . . 34-39
Réception de requêtes de l’adaptateur
et génération des réponses . . . . . . 34-40
Requête d’image . . . . . . . . . . . . . 34-42
Réponse d’image . . . . . . . . . . . . . 34-42
Répartition des éléments d’action . . . . . 34-43
Fonctions du répartiteur de page . . . . . 34-44
Utilisation de l’expert
d’application SOAP . . . . . . . . . . . . 36-12
Ajout de nouveaux services Web . . . . . 36-13
Modification du code généré. . . . . . 36-13
Utilisation d’une classe de base
différente . . . . . . . . . . . . . . . . 36-14
Utilisation de l’importateur
de services Web . . . . . . . . . . . . . . 36-15
Création de classes d’exception personnalisées
pour les services Web . . . . . . . . . . . 36-16
Génération de documents WSDL
pour une application de service Web . . 36-17
Conception de clients pour les services Web 36-18
Importation de documents WSDL . . . . 36-18
Appel des interfaces invocables . . . . . . 36-18
Chapitre 37
Utilisation des sockets
Chapitre 35
Utilisation de documents XML
35-1
Utilisation du modèle DOM . . . . . . . . . .
Utilisation des composants XML . . . . . . .
Utilisation de TXMLDocument. . . . . . .
Utilisation des nœuds XML. . . . . . . . .
Utilisation de la valeur d’un nœud . .
Utilisation des attributs d’un nœud . .
Ajout et suppression de nœuds enfant
Abstraction de documents XML
avec l’expert Liaison de données . . . . . .
Utilisation de l’expert
Liaison de données XML . . . . . . . . .
Utilisation du code généré par l’expert
Liaison de données XML . . . . . . . . .
. 35-2
. 35-3
. 35-3
. 35-4
. 35-5
. 35-5
. 35-5
. 35-6
. 35-8
. 35-9
Chapitre 36
Utilisation de services Web
36-1
Présentation des interfaces invocables . . . .
Utilisation de types non scalaires
dans des interfaces invocables . . . . . .
Recensement des types non scalaires .
Recensement des types typedef
et des types énumérés . . . . . . . . .
Emploi d’objets distants . . . . . . . . .
Exemple d’objet distant . . . . . . . . .
Conception de serveurs
gérant les services Web . . . . . . . . . . . .
Conception d’un serveur de service Web .
. 36-2
37-1
Implémentation des services . . . . . . . . . .
Description des protocoles de services . . .
Communication avec les applications .
Services et ports . . . . . . . . . . . . . . . .
Types de connexions par socket . . . . . . . .
Connexions client . . . . . . . . . . . . . . .
Connexions d’écoute . . . . . . . . . . . . .
Connexions serveur . . . . . . . . . . . . . .
Description des sockets . . . . . . . . . . . . .
Description des hôtes . . . . . . . . . . . . .
Choix entre le nom de l’hôte
et son adresse IP . . . . . . . . . . . . .
Utilisation des ports . . . . . . . . . . . . .
Utilisation des composants socket . . . . . . .
Obtenir des informations sur la connexion
Utilisation de sockets client . . . . . . . . .
Désignation du serveur souhaité . . . .
Formation de la connexion . . . . . . . .
Obtention d’informations
sur la connexion . . . . . . . . . . . . .
Fermeture de la connexion . . . . . . . .
Utilisation de sockets serveur . . . . . . . .
Désignation du port . . . . . . . . . . . .
Ecoute des requêtes client . . . . . . . .
Connexion aux clients. . . . . . . . . . .
Fermeture des connexions serveur . . .
Réponse aux événements socket . . . . . . . .
Evénements d’erreurs. . . . . . . . . . . . .
Evénements client . . . . . . . . . . . . . . .
Evénements serveur . . . . . . . . . . . . .
Evénements d’écoute . . . . . . . . . . .
. 36-4
. 36-5
. 36-6
. 36-8
. 36-9
36-11
36-11
xxi
37-1
37-2
37-2
37-2
37-2
37-3
37-3
37-3
37-3
37-4
37-5
37-5
37-5
37-6
37-6
37-6
37-7
37-7
37-7
37-7
37-7
37-8
37-8
37-8
37-8
37-9
37-9
37-9
37-9
Evénements de connexions client
Lectures et écritures sur des connexions
socket . . . . . . . . . . . . . . . . . . .
Connexions non bloquantes . . . . .
Lecture et écriture d’événements .
Connexions bloquantes . . . . . . . .
Chapitre 39
. . . 37-10
.
.
.
.
.
.
.
.
.
.
.
.
Utilisation
des bibliothèques de types
37-10
37-10
37-11
37-11
Partie IV
Développement d’applications COM
Chapitre 38
Présentation
des technologies COM
39-1
L’éditeur de bibliothèques de types . . . . . . 39-2
Composants de l’éditeur
de bibliothèques de types . . . . . . . . . 39-3
Barre d’outils . . . . . . . . . . . . . . . 39-4
Volet liste des objets. . . . . . . . . . . . 39-5
Barre d’état . . . . . . . . . . . . . . . . . 39-6
Les pages d’informations de type . . . . 39-6
Eléments d’une bibliothèque de types . . . 39-9
Interfaces . . . . . . . . . . . . . . . . . . 39-9
Dispinterfaces . . . . . . . . . . . . . . 39-10
CoClasses . . . . . . . . . . . . . . . . . 39-10
Définitions de types . . . . . . . . . . . 39-10
Modules. . . . . . . . . . . . . . . . . . .39-11
Utilisation de l’éditeur de bibliothèques
de types. . . . . . . . . . . . . . . . . . . 39-12
Types autorisés. . . . . . . . . . . . . . 39-13
Création d’une nouvelle bibliothèque
de types . . . . . . . . . . . . . . . . . 39-14
Ouverture d’une bibliothèque de types
existante . . . . . . . . . . . . . . . . . 39-15
Ajout d’une interface à une bibliothèque
de types . . . . . . . . . . . . . . . . . 39-15
Modification d’une interface en utilisant
la bibliothèque de types . . . . . . . 39-15
Ajout de propriétés et méthodes
à une interface ou dispinterface . . . 39-16
Ajout d’une CoClasse à une bibliothèque
de types . . . . . . . . . . . . . . . . . 39-17
Ajout d’une interface à une CoClasse 39-18
Ajout d’une énumération à une bibliothèque
de types . . . . . . . . . . . . . . . . . 39-18
Ajout d’un alias à une bibliothèque
de types . . . . . . . . . . . . . . . . . 39-18
Ajout d’un enregistrement ou d’une union
à une bibliothèque de types . . . . . 39-19
Ajout d’un module à une bibliothèque
de types . . . . . . . . . . . . . . . . . 39-19
Enregistrement et recensement
des informations d’une bibliothèque
de types . . . . . . . . . . . . . . . . . 39-19
Enregistrement d’une bibliothèque
de types . . . . . . . . . . . . . . . . . 39-20
38-1
COM, spécification et implémentation . 38-2
Extensions de COM . . . . . . . . . . . . 38-2
Composantes d’une application COM . . . . . 38-3
Interfaces COM. . . . . . . . . . . . . . . . . 38-3
L’interface COM de base, IUnknown . . 38-4
Pointeurs d’interface COM . . . . . . . . 38-5
Serveurs COM . . . . . . . . . . . . . . . . . 38-5
CoClasses et fabricants de classes . . . . 38-6
Serveurs en processus, hors processus
et distants . . . . . . . . . . . . . . . . . 38-7
Le mécanisme du marshaling. . . . . . . 38-8
Agrégation. . . . . . . . . . . . . . . . . . 38-9
Clients COM . . . . . . . . . . . . . . . . . 38-10
Extensions de COM . . . . . . . . . . . . . . . 38-10
Serveurs Automation . . . . . . . . . . . . 38-13
Pages Active Server . . . . . . . . . . . . . 38-14
Contrôles ActiveX . . . . . . . . . . . . . . 38-14
Documents Active . . . . . . . . . . . . . . 38-15
Objets transactionnels . . . . . . . . . . . . 38-15
Objets événement COM+ et souscripteurs
d’événement . . . . . . . . . . . . . . . . 38-16
Bibliothèques de types . . . . . . . . . . . 38-17
Contenu d’une bibliothèque de types . 38-17
Création de bibliothèques de types . . 38-18
Quand utiliser les bibliothèques
de types . . . . . . . . . . . . . . . . . 38-18
Accès aux bibliothèques de types . . . 38-19
Avantages des bibliothèques de types . 38-19
Utilisation des outils de bibliothèques
de types . . . . . . . . . . . . . . . . . 38-20
Implémentation des objets COM
à l’aide d’experts . . . . . . . . . . . . . . . . 38-20
Code généré par les experts . . . . . . . . 38-23
xxii
Rafraîchissement de la bibliothèque
de types . . . . . . . . . . . . . . .
Recensement d’une bibliothèque
de types . . . . . . . . . . . . . . .
Exportation d’un fichier IDL . . . .
Déploiement des bibliothèques de types .
. . 39-20
. . 39-21
. . 39-21
. . 39-21
Chapitre 40
Création de clients COM
40-1
Importation des informations
d’une bibliothèque de types . . . . . . . . . . 40-2
Utilisation de la boîte de dialogue Importation
de bibliothèque de types . . . . . . . . . . 40-3
Utilisation de la boîte de dialogue
Importation d’ActiveX . . . . . . . . . . . . 40-4
Code généré par l’importation des
informations d’une bibliothèque de types 40-5
Contrôle d’un objet importé . . . . . . . . . . . 40-7
Utilisation des composants enveloppe . . . 40-7
Enveloppes ActiveX . . . . . . . . . . . . 40-8
Enveloppes des serveurs Automation . . 40-8
Utilisation de contrôles ActiveX
orientés données . . . . . . . . . . . . . . . 40-9
Exemple : impression d’un document
avec Microsoft Word . . . . . . . . . . . . 40-11
Etape 1 : préparation de C++Builder
pour cet exemple . . . . . . . . . . . . 40-11
Etape 2 : importation de la bibliothèque
de types Word. . . . . . . . . . . . . . 40-12
Etape 3 : utilisation d’un objet interface
VTable ou de répartition
pour contrôler Microsoft Word . . . . 40-12
Etape 4 : nettoyage de l’exemple . . . . 40-13
Ecriture de code client basé sur les définitions
de la bibliothèque de types . . . . . . . . 40-14
Connexion à un serveur . . . . . . . . . 40-14
Contrôle d’un serveur Automation
en utilisant une interface double . . . 40-14
Contrôle d’un serveur Automation en
utilisant une interface de répartition . 40-15
Gestion des événements
dans un contrôleur Automation . . . 40-16
Création de clients pour les serveurs
n’ayant pas une bibliothèque de types . . . 40-18
Chapitre 41
Création de serveurs COM simples 41-1
Présentation de la création d’un objet COM . . 41-2
Conception d’un objet COM . . . . . . . . . . . 41-2
Utilisation de l’expert objet COM . . . . . .
Utilisation de l’expert objet Automation . .
Choix d’un modèle de thread . . . . . . .
Ecriture d’un objet supportant
le modèle de thread libre . . . . . . .
Ecriture d’un objet supportant
le modèle de thread apartment . . .
Ecriture d’un objet supportant
le modèle de thread neutre . . . . . .
Spécification des options ATL . . . . . . . .
Définition de l’interface d’un objet COM . .
Ajout d’une propriété
à l’interface de l’objet . . . . . . . . . . .
Ajout d’une méthode
à l’interface de l’objet . . . . . . . . . . .
Exposition d’événements aux clients . . .
Gestion des événements
dans un objet Automation . . . . . .
Interfaces d’Automation . . . . . . . . . . . .
Interfaces doubles . . . . . . . . . . . . . .
Interfaces de répartition . . . . . . . . . .
Interfaces personnalisées . . . . . . . . . .
Marshaling des données . . . . . . . . . . . .
Types compatibles avec l’Automation . .
Restrictions de type
pour le marshaling automatique . . . .
Marshaling personnalisé . . . . . . . . . .
Recensement d’un objet COM . . . . . . . .
Recensement d’un serveur en processus .
Recensement d’un serveur
hors processus . . . . . . . . . . . . . . .
Test et débogage de l’application . . . . . . .
. 41-3
. 41-4
. 41-5
. 41-7
. 41-8
. 41-8
. 41-9
41-10
41-10
.41-11
.41-11
41-12
41-13
41-13
41-14
41-15
41-15
41-16
41-16
41-17
41-17
41-17
41-18
41-18
Chapitre 42
Création d’une page Active Server 42-1
Création d’un objet Active Server . . . . . . .
Utilisation des éléments intrinsèques ASP.
Application . . . . . . . . . . . . . . . . .
Request . . . . . . . . . . . . . . . . . . .
Response . . . . . . . . . . . . . . . . . .
Session . . . . . . . . . . . . . . . . . . .
Server . . . . . . . . . . . . . . . . . . . .
Création d’ASP pour des serveurs
en et hors processus. . . . . . . . . . . . .
Recensement d’un objet Active Server. . . . .
Recensement d’un serveur en processus . .
Recensement d’un serveur hors processus
Test et débogage d’une application ASP . . .
xxiii
42-2
42-3
42-4
42-4
42-5
42-6
42-7
42-7
42-8
42-8
42-9
42-9
Chapitre 43
Création d’un contrôle ActiveX
43-1
Présentation de la création
d’un contrôle ActiveX . . . . . . . . . . . . . . 43-2
Eléments d’un contrôle ActiveX . . . . . . . 43-3
Contrôle VCL . . . . . . . . . . . . . . . . 43-3
Enveloppe ActiveX . . . . . . . . . . . . . 43-3
Bibliothèque de types . . . . . . . . . . . 43-3
Page de propriétés . . . . . . . . . . . . . 43-4
Conception d’un contrôle ActiveX . . . . . . . 43-4
Génération d’un contrôle ActiveX
à partir d’un contrôle VCL . . . . . . . . . . . 43-4
Génération d’un contrôle ActiveX
basé sur une fiche VCL . . . . . . . . . . . . . 43-6
Licences des contrôles ActiveX . . . . . . . . . 43-8
Personnalisation de l’interface
du contrôle ActiveX . . . . . . . . . . . . . . . 43-9
Ajout de propriétés, méthodes
et événements supplémentaires . . . . . 43-10
Ajout de propriétés et de méthodes . . 43-10
Ajout d’événement . . . . . . . . . . . . 43-12
Activation de la liaison de données simple
avec la bibliothèque de types . . . . . . . 43-12
Création d’une page de propriétés
pour un contrôle ActiveX . . . . . . . . . . . 43-14
Création d’une nouvelle page
de propriétés . . . . . . . . . . . . . . . . 43-15
Ajout de contrôles à une page
de propriétés . . . . . . . . . . . . . . . . 43-15
Association des contrôles
de la page de propriétés
aux propriétés du contrôle ActiveX . . . 43-15
Actualisation de la page de propriétés 43-16
Actualisation de l’objet . . . . . . . . . 43-16
Connexion d’une page de propriétés
à un contrôle ActiveX . . . . . . . . . . . 43-16
Recensement d’un contrôle ActiveX . . . . . 43-17
Test d’un contrôle ActiveX . . . . . . . . . . . 43-17
Déploiement d’un contrôle ActiveX
sur le Web. . . . . . . . . . . . . . . . . . . . 43-18
Paramétrage des options . . . . . . . . . . 43-19
Chapitre 44
Création d’objets MTS ou COM+
Principe des objets transactionnels . . . .
Contraintes d’un objet transactionnel .
Gestion des ressources . . . . . . . . . . .
Accès au contexte d’un objet . . . . . .
Activation juste-à-temps . . . . . . . .
44-1
.
.
.
.
.
.
.
.
.
.
. 44-2
. 44-3
. 44-4
. 44-4
. 44-5
Regroupement des ressources . . . . . . . . 44-6
Fournisseurs de ressources
base de données . . . . . . . . . . . . . 44-6
Gestionnaire de propriétés partagées . . 44-7
Libération des ressources . . . . . . . . 44-10
Regroupement d’objets . . . . . . . . . . . 44-10
Support transactionnel MTS et COM+. . . . .44-11
Attributs transactionnels . . . . . . . . . . 44-12
Initialisation de l’attribut
transactionnel. . . . . . . . . . . . . . 44-13
Objets avec état et sans état . . . . . . . . 44-13
Contrôle de l’arrêt des transactions. . . . 44-14
Démarrage des transactions . . . . . . . . 44-14
Définition d’un objet transaction
côté client . . . . . . . . . . . . . . . . 44-15
Définition d’un objet transaction
côté serveur. . . . . . . . . . . . . . . 44-16
Délais des transactions . . . . . . . . . . . 44-17
Sécurité en fonction des rôles . . . . . . . . . 44-17
Présentation de la création
des objets transactionnels . . . . . . . . . . 44-18
Utilisation de l’expert objet transactionnel . 44-19
Choix d’un modèle de thread
pour un objet transactionnel . . . . . . . 44-20
Activités. . . . . . . . . . . . . . . . . . 44-21
Génération d’événements dans COM+ . . . 44-22
Utilisation de l’expert objet événement . 44-24
Utilisation de l’expert Objet Abonnement
d’événement COM+. . . . . . . . . . . . 44-25
Déclenchement d’événement en utilisant
un objet événement COM+ . . . . . . . 44-26
Transfert de références d’objets . . . . . . . . 44-27
Utilisation de la méthode SafeRef . . . 44-27
Callbacks . . . . . . . . . . . . . . . . . 44-28
Débogage et test des objets transactionnels . 44-28
Installation d’objets transactionnels . . . . . 44-29
Administration d’objets transactionnels . . . 44-30
Partie V
Création de composants
personnalisés
Chapitre 45
Présentation générale
de la création d’un composant
45-1
Bibliothèques de classes . . . . . . . . . . . . . 45-1
Composants et classes . . . . . . . . . . . . . . 45-2
Comment créer un composant ? . . . . . . . . 45-3
xxiv
Modification de contrôles existants . . . . . 45-3
Création de contrôles fenêtrés . . . . . . . . 45-4
Création de contrôles graphiques . . . . . . 45-4
Sous-classement de contrôles Windows. . . 45-5
Création de composants non visuels . . . . 45-5
Contenu d’un composant ? . . . . . . . . . . . . 45-5
Suppression des dépendances . . . . . . . . 45-6
Définition des propriétés, méthodes
et événements. . . . . . . . . . . . . . . . . 45-6
Propriétés . . . . . . . . . . . . . . . . . . 45-6
Evénements . . . . . . . . . . . . . . . . . 45-7
Méthodes . . . . . . . . . . . . . . . . . . 45-7
Encapsulation des graphiques . . . . . . . . 45-8
Recensement des composants . . . . . . . . 45-9
Création d’un nouveau composant . . . . . . . 45-9
Création d’un composant
avec l’expert composant . . . . . . . . . . 45-10
Création manuelle d’un composant . . . . 45-12
Création d’un fichier unité . . . . . . . 45-13
Dérivation du composant . . . . . . . . 45-13
Déclaration d’un nouveau
constructeur . . . . . . . . . . . . . . . 45-14
Recensement du composant. . . . . . . 45-14
Création de bitmaps pour les composants 45-16
Test des composants non installés. . . . . . . 45-17
Test des composants installés . . . . . . . . . 45-19
Installation d’un composant
dans la palette de composants . . . . . . . . 45-20
Emplacement des fichiers du composant . 45-20
Ajout du composant . . . . . . . . . . . . . 45-21
Définition de l’interface d’exécution . .
Définition de l’interface de conception.
Répartition des méthodes . . . . . . . . . .
Méthodes standard . . . . . . . . . . . .
Méthodes virtuelles . . . . . . . . . . . .
Surcharge des méthodes . . . . . . .
Membres abstraits d’une classe . . . . . . .
Classes et pointeurs . . . . . . . . . . . . .
. 46-7
. 46-8
. 46-9
. 46-9
46-10
46-10
.46-11
.46-11
Chapitre 47
Création de propriétés
47-1
Pourquoi créer des propriétés ? . . . . . . . . . 47-1
Types de propriétés. . . . . . . . . . . . . . . . 47-2
Publication des propriétés héritées . . . . . . . 47-3
Définition des propriétés . . . . . . . . . . . . 47-4
Déclaration des propriétés . . . . . . . . . . 47-4
Stockage interne des données . . . . . . . . 47-4
Accès direct . . . . . . . . . . . . . . . . . . 47-5
Méthodes d’accès . . . . . . . . . . . . . . . 47-5
Méthode read . . . . . . . . . . . . . . . 47-7
Méthode write . . . . . . . . . . . . . . . 47-7
Valeurs par défaut d’une propriété . . . . . 47-8
Spécification d’aucune
valeur par défaut . . . . . . . . . . . . 47-8
Création de propriétés tableau . . . . . . . . . 47-9
Création de propriétés
pour les sous-composants . . . . . . . . . . 47-10
Stockage et chargement des propriétés . . . .47-11
Utilisation du mécanisme de stockage
et de chargement . . . . . . . . . . . . . 47-12
Spécification des valeurs par défaut . . . 47-12
Détermination du stockage . . . . . . . . 47-13
Initialisation après chargement . . . . . . 47-14
Stockage et chargement des propriétés
non publiées . . . . . . . . . . . . . . . . 47-14
Création de méthodes pour le stockage et le
chargement de valeurs de propriétés 47-15
Redéfinition de la méthode
DefineProperties . . . . . . . . . . . . 47-15
Chapitre 46
Programmation orientée objet
et écriture des composants
.
.
.
.
.
.
.
.
46-1
Définition de nouvelles classes . . . . . . . . . 46-1
Dérivation de nouvelles classes . . . . . . . 46-2
Modifier les valeurs par défaut
d’une classe pour éviter
les répétitions . . . . . . . . . . . . . . . 46-2
Ajout de nouvelles capacités
à une classe . . . . . . . . . . . . . . . . 46-3
Déclaration d’une nouvelle classe
de composant . . . . . . . . . . . . . . . . . 46-3
Ancêtres, descendants et hiérarchies
des classes. . . . . . . . . . . . . . . . . . . . . 46-4
Contrôle des accès . . . . . . . . . . . . . . . . . 46-4
Masquer les détails d’implémentation . . . 46-5
Définition de l’interface avec le concepteur
des composants . . . . . . . . . . . . . . . . 46-7
Chapitre 48
Création d’événements
Qu’est-ce qu’un événement ? . . . . . . . .
Les événements sont des closures . . .
Les événements sont des propriétés . .
Les types d’événements
sont des types de closures . . . . . . .
Le type de renvoi des gestionnaires
d’événements est void . . . . . . .
xxv
48-1
. . 48-1
. . 48-2
. . 48-2
. . 48-3
. . 48-3
Les gestionnaires d’événements
sont facultatifs . . . . . . . . . . . . . . . . 48-4
Implémentation des événements standard . . . 48-4
Identification des événements standard. . . 48-5
Evénements standard
pour tous les contrôles . . . . . . . . . . 48-5
Evénements standard
pour les contrôles standard . . . . . . . 48-5
Rendre visibles des événements . . . . . . . 48-5
Changement de la gestion
des événements standard . . . . . . . . . . 48-6
Définition de vos propres événements . . . . . 48-7
Déclenchement de l’événement . . . . . . . 48-7
Deux sortes d’événements. . . . . . . . . 48-7
Définition du type de gestionnaire . . . . . 48-8
Notifications simples . . . . . . . . . . . . 48-8
Gestionnaires d’événements spécifiques. 48-8
Renvoi d’informations
à partir du gestionnaire . . . . . . . . . 48-8
Déclaration de l’événement . . . . . . . . . . 48-9
Les noms d’événement débutent
par “On” . . . . . . . . . . . . . . . . . . 48-9
Appel de l’événement . . . . . . . . . . . . . 48-9
Les gestionnaires vides
doivent être valides. . . . . . . . . . . . 48-9
Les utilisateurs peuvent surcharger
la gestion par défaut . . . . . . . . . . 48-10
Chapitre 49
Création de méthodes
Eviter les interdépendances . . . . . . . .
Noms des méthodes. . . . . . . . . . . . .
Protection des méthodes . . . . . . . . . .
Méthodes qui doivent être publiques .
Méthodes qui doivent être protégées .
Rendre virtuelles des méthodes . . . . . .
Déclaration des méthodes . . . . . . . . .
49-1
.
.
.
.
.
.
.
.
.
.
.
.
.
.
. 49-1
. 49-2
. 49-3
. 49-3
. 49-3
. 49-4
. 49-4
Chapitre 50
Graphiques et composants
Présentation des graphiques . . . . . . . . .
Utilisation du canevas . . . . . . . . . . . .
Travail sur les images . . . . . . . . . . . . .
Utilisation d’une image, d’un graphique
ou d’un canevas . . . . . . . . . . . . .
Chargement et stockage des graphiques
Gestion des palettes . . . . . . . . . . . .
50-1
. . 50-1
. . 50-3
. . 50-3
. . 50-4
. . 50-4
. . 50-5
Spécification d’une palette
pour un contrôle . . . . . . . . . . . . .
Réponse aux changements de palette. .
Bitmaps hors écran . . . . . . . . . . . . . . . .
Création et gestion des bitmaps hors écran
Copie des images bitmap . . . . . . . . . .
Réponse aux changements . . . . . . . . . . .
50-6
50-6
50-6
50-7
50-7
50-7
Chapitre 51
Gestion des messages
et des notifications système
51-1
Compréhension du système
de gestion des messages . . . . . . . . . . . . 51-1
Que contient un message Windows ? . . . 51-2
Répartition des messages . . . . . . . . . . 51-3
Suivi du flux des messages . . . . . . . 51-3
Modification de la gestion des messages . . . 51-4
Surcharge de la méthode du gestionnaire . 51-4
Utilisation des paramètres d’un message . 51-5
Interception des messages . . . . . . . . . . 51-5
Création de nouveaux gestionnaires
de messages . . . . . . . . . . . . . . . . . . . 51-6
Définition de vos propres messages . . . . 51-6
Déclaration d’un identificateur
de message . . . . . . . . . . . . . . . . 51-6
Déclaration d’un type
structure de message . . . . . . . . . . 51-7
Déclaration d’une nouvelle méthode
de gestion d’un message . . . . . . . . . . 51-8
Envoi des messages . . . . . . . . . . . . . . 51-8
Diffusion d’un message
à tous les contrôles d’une fiche . . . . 51-9
Appel direct du gestionnaire
de message d’un contrôle. . . . . . . . 51-9
Envoi d’un message à l’aide de la file
d’attente des messages Windows . . 51-10
Envoi d’un message qui ne s’exécute pas
immédiatement. . . . . . . . . . . . . 51-10
Réponse aux notifications du système
à l’aide de CLX . . . . . . . . . . . . . . . . .51-11
Réponse aux signaux . . . . . . . . . . . . .51-11
Affectation de gestionnaires
de signaux personnalisés . . . . . . . 51-12
Réponse aux événements système . . . . 51-13
Evénements couramment utilisés . . . 51-14
Surcharge de la méthode EventFilter . 51-15
Génération des événements Qt . . . . 51-16
xxvi
Chapitre 52
Accessibilité des composants
au moment de la conception
Chapitre 53
52-1
Recensement des composants . . . . . . . . . . 52-1
Déclaration de la fonction Register . . . . . 52-2
Ecriture de la fonctionRegister . . . . . . . . 52-2
Spécification des composants . . . . . . . 52-2
Spécification de la page de palette . . . . 52-3
Utilisation de la fonction
RegisterComponents . . . . . . . . . . . 52-3
Ajout de bitmaps à la palette . . . . . . . . . . 52-4
Fournir l’aide pour vos composants . . . . . . 52-5
Création du fichier d’aide. . . . . . . . . . . 52-5
Création des entrées . . . . . . . . . . . . 52-5
Aide contextuelle des composants . . . . 52-7
Ajout des fichiers d’aide
des composants . . . . . . . . . . . . . . 52-7
Ajout d’éditeurs de propriétés . . . . . . . . . . 52-8
Dérivation d’une classe
éditeur de propriétés. . . . . . . . . . . . . 52-8
Modification de la propriété
sous une forme textuelle . . . . . . . . . . 52-9
Affichage de la valeur de la propriété . 52-10
Définition de la valeur de la propriété 52-10
Modification globale de la propriété . . . 52-10
Spécification des attributs de l’éditeur . . 52-11
Recensement de l’éditeur de propriétés. . 52-12
Catégories de propriété . . . . . . . . . . . . . 52-13
Recensement d’une propriété à la fois . . 52-14
Recensement de plusieurs propriétés
en une seule fois . . . . . . . . . . . . . . 52-14
Spécification de catégories de propriétés . 52-15
Utilisation de la fonction
IsPropertyInCategory . . . . . . . . . . . 52-16
Ajout d’éditeurs de composants . . . . . . . . 52-17
Ajout d’éléments au menu contextuel. . . 52-17
Spécification d’éléments de menu . . . 52-17
Implémentation des commandes . . . . 52-18
Modification du comportement suite
à un double-clic. . . . . . . . . . . . . . . 52-18
Ajout de formats de Presse-papiers . . . . 52-19
Recensement d’un éditeur de composants 52-20
Compilation des composants en paquets. . . 52-20
Problèmes d’installation
de composants personnalisés. . . . . . . . . 52-21
Modification
d’un composant existant
Création et recensement du composant
Modification de la classe composant. .
Surcharge du constructeur . . . . . .
Spécification de la nouvelle valeur
par défaut de la propriété . . . . .
53-1
. . . . 53-1
. . . . 53-3
. . . . 53-3
. . . . 53-4
Chapitre 54
Création d’un contrôle graphique
54-1
Création et recensement du composant . . . . 54-1
Publication des propriétés héritées . . . . . . . 54-3
Ajout de fonctionnalités graphiques . . . . . . 54-3
Détermination de ce qui doit être dessiné . 54-4
Déclaration du type de la propriété. . . 54-4
Déclaration de la propriété . . . . . . . . 54-4
Ecriture de la méthode
d’implémentation . . . . . . . . . . . . 54-5
Surcharge du constructeur
et du destructeur . . . . . . . . . . . . . . 54-5
Modification des valeurs par défaut
des propriétés . . . . . . . . . . . . . . 54-5
Publication du crayon et du pinceau . . . . 54-6
Déclaration des données membres . . . 54-6
Déclaration des propriétés d’accès . . . 54-7
Initialisation des classes
ayant un propriétaire . . . . . . . . . . 54-8
Définition des propriétés des classes
ayant un propriétaire . . . . . . . . . . 54-8
Dessin de l’image du composant . . . . . . 54-9
Adaptation du dessin de la forme . . . . 54-10
Chapitre 55
Personnalisation d’une grille
Création et recensement du composant .
Publication des propriétés héritées . . . .
Modification des valeurs initiales . . . .
Redimensionnement des cellules . . . . .
Remplissage des cellules. . . . . . . . . .
Suivi de la date . . . . . . . . . . . . .
Stockage interne de la date. . . . .
Accès au jour, au mois et à l’année
Génération des numéros de jours .
Sélection du jour en cours . . . . .
Navigation de mois en mois
et d’année en année . . . . . . . . . . .
xxvii
55-1
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
. 55-1
. 55-3
. 55-4
. 55-5
. 55-6
. 55-6
. 55-7
. 55-8
. 55-9
.55-11
. . 55-12
Navigation de jour en jour . . . . . . . . . .
Déplacement de la sélection . . . . . . .
Fourniture d’un événement OnChange .
Exclusion des cellules vides . . . . . . .
.
.
.
.
55-13
55-13
55-13
55-14
Chapitre 56
Contrôles orientés données
Création d’un contrôle
pour scruter les données . . . . . . . . . .
Création et recensement du composant .
Fonctionnement du contrôle
en lecture seulement . . . . . . . . . . .
Ajout de la propriété ReadOnly . . .
Autorisation des mises à jour
nécessaires . . . . . . . . . . . . . . .
Ajout du lien aux données . . . . . . . .
Déclaration de la donnée membre . .
Déclaration des propriétés d’accès . .
Exemple de déclaration
des propriétés d’accès . . . . . . . .
Initialisation du lien de données . . .
Réponse aux changements de données .
Création d’un contrôle de modification
de données . . . . . . . . . . . . . . . . . .
Modification de la valeur par défaut
de FReadOnly. . . . . . . . . . . . . . .
Gestion des messages liés à la souris
ou au clavier . . . . . . . . . . . . . . .
Réponse aux messages indiquant
la manipulation de la souris. . . . .
Réponse aux messages indiquant
la manipulation du clavier . . . . .
Mise à jour de la classe lien de données
sur un champ . . . . . . . . . . . . . . .
Modification de la méthode Change . .
Mise à jour de l’ensemble de données .
56-1
. . 56-2
. . 56-2
. . 56-3
. . 56-3
.
.
.
.
. 56-4
. 56-5
. 56-6
. 56-6
. . 56-6
. . 56-7
. . 56-8
. . 56-9
. . 56-9
. . 56-9
. 56-10
. 56-11
. 56-12
. 56-12
. 56-13
Chapitre 57
Transformation d’une boîte
de dialogue en composant
57-1
Définition de l’interface du composant . . . . . 57-2
Création et recensement du composant . . . . 57-2
Création de l’interface du composant. . . . . . 57-3
Inclusion des fichiers de l’unité de la fiche. 57-4
Ajout des propriétés de l’interface . . . . . . 57-4
Ajout de la méthode Execute . . . . . . . . . 57-5
Test du composant. . . . . . . . . . . . . . . . . 57-7
Chapitre 58
Extensions de l’EDI
58-1
Présentation de l’API Tools . . . . . . . . . . . 58-2
Conception d’une classe expert . . . . . . . . . 58-3
Implémentation des interfaces de l’expert . 58-4
Simplification de l’implémentation
d’interfaces . . . . . . . . . . . . . . . . . . 58-6
Installation du paquet de l’expert. . . . . . 58-7
Accès aux services de l’API Tools . . . . . . . 58-8
Utilisation d’objets natifs de l’EDI . . . . . 58-9
Utilisation de l’interface INTAServices . 58-9
Ajout d’une image à la liste d’images . 58-9
Ajout d’une action à la liste d’actions 58-10
Suppression de boutons
de barres d’outils . . . . . . . . . . . .58-11
Débogage d’un expert . . . . . . . . . . . 58-12
Numéros de version de l’interface . . . . 58-12
Utilisation des fichiers et des éditeurs . . . . 58-13
Utilisation des interfaces de module . . . 58-14
Utilisation des interfaces d’éditeur . . . . 58-14
Création de fiches et de projets . . . . . . . . 58-15
Création de modules . . . . . . . . . . . . 58-15
Notification d’un expert
des événements de l’EDI. . . . . . . . . . . 58-19
Installation d’une DLL expert. . . . . . . . . 58-23
Utilisation d’une DLL
sans paquets d’exécution . . . . . . . . . 58-25
Annexe A
Implémentations spécifiques
de la norme ANSI
A-1
Annexe B
Référence de scripts
côté serveur WebSnap
Types d’objets . . . . . . . . .
Type Adapter . . . . . . .
Propriétés . . . . . . . .
Type AdapterAction . . .
Propriétés . . . . . . . .
Méthodes . . . . . . . .
Type AdapterErrors . . . .
Propriétés . . . . . . . .
Type AdapterField . . . .
Propriétés . . . . . . . .
Méthodes . . . . . . . .
Type AdapterFieldValues
Propriétés . . . . . . . .
xxviii
B-1
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
. B-2
. B-2
. B-3
. B-4
. B-5
. B-6
. B-6
. B-6
. B-7
. B-7
B-10
B-10
B-10
Méthodes . . . . . . . . . .
Type AdapterFieldValuesList .
Propriétés . . . . . . . . . .
Méthodes . . . . . . . . . .
Type AdapterHiddenFields . .
Propriétés . . . . . . . . . .
Méthodes . . . . . . . . . .
Type AdapterImage . . . . . .
Propriétés . . . . . . . . . .
Type Module . . . . . . . . . .
Propriétés . . . . . . . . . .
Type Page . . . . . . . . . . . .
Propriétés . . . . . . . . . .
Objets globaux . . . . . . . . . . .
Objet Application . . . . . . .
Propriétés . . . . . . . . . .
Méthodes . . . . . . . . . .
Objet EndUser . . . . . . . . .
Propriétés . . . . . . . . . .
Objet Modules . . . . . . . . .
Objet page. . . . . . . . . . . .
Objet pages . . . . . . . . . . .
Objet Producer . . . . . . . . .
Propriétés . . . . . . . . . .
Méthodes . . . . . . . . . .
Objet Request . . . . . . . . . .
Propriétés . . . . . . . . . .
Objet Response . . . . . . . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.B-11
.B-11
.B-11
.B-12
.B-12
.B-12
.B-12
.B-12
.B-12
.B-13
.B-13
.B-13
.B-13
.B-14
.B-15
.B-15
.B-16
.B-16
.B-16
.B-17
.B-17
.B-17
.B-17
.B-18
.B-18
.B-18
.B-18
.B-19
Propriétés .
Méthodes .
Objet Session.
Propriétés .
Exemples JScript
Exemple 1 . .
Exemple 2 . .
Exemple 3 . .
Exemple 4 . .
Exemple 5 . .
Exemple 6 . .
Exemple 7 . .
Exemple 8 . .
Exemple 9 . .
Exemple 10 . .
Exemple 11 . .
Exemple 12 . .
Exemple 13 . .
Exemple 14 . .
Exemple 15 . .
Exemple 16 . .
Exemple 17 . .
Exemple 18 . .
Exemple 19 . .
Exemple 20 . .
Exemple 21 . .
Exemple 22 . .
xxix
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
B-19
B-19
B-19
B-19
B-20
B-21
B-21
B-22
B-22
B-22
B-23
B-23
B-24
B-24
B-25
B-27
B-28
B-29
B-30
B-32
B-33
B-34
B-35
B-36
B-36
B-37
B-38
xxx
Chapitre
1
Introduction
Chapitre 1
Ce manuel aborde des notions de développement intermédiaires et avancées.
Il traite notamment de la création d’applications de bases de données client/
serveur, de l’écriture de composants personnalisés, de la création d’applications
serveurs Web Internet et du support des spécifications de nombreux standards
comme SOAP, TCP/IP, COM+ et ActiveX. Nombre de fonctionnalités avancées
concernant le développement web, les technologies XML de pointe et le
développement de bases de données nécessitent des composants ou des experts
qui ne sont pas disponibles dans toutes les versions de C++Builder.
Ce guide suppose que l’utilisation et les techniques fondamentales de
programmation C++Builder ont été assimilées. Pour une présentation de la
programmation C++Builder et de l’environnement de développement intégré
(EDI), voir le manuel de Prise en main et l’aide en ligne.
Contenu de ce manuel
Ce manuel est composé des cinq parties suivantes :
• La partie I, “Programmation C++Builder”, décrit la manière de concevoir des
applications C++Builder généralistes. Cette partie donne des détails sur les
techniques de programmation utilisables dans toute application C++Builder.
Elle décrit, par exemple, la manière d’utiliser les objets courants de la
bibliothèque de composants visuels (VCL) ou de la bibliothèque de
composants Borland multiplate-forme (CLX) qui simplifient le développement
de l’interface utilisateur : gestion des chaînes, manipulation du texte,
implémentation des dialogues communs, manipulation des graphiques, gestion
des erreurs et des exceptions, utilisation des DLL, automation OLE et écriture
d’applications internationales.
En général, il est rarement important que la VCL de C++Builder soit écrite en
Pascal Objet. Mais, dans un petit nombre de cas, cela peut affecter vos
programmes C++Builder. Un chapitre concernant le langage C++ et la VCL
Introduction
1-1
Contenu de ce manuel
traite de certaines questions relatives au langage, comme la façon dont
l’instanciation de classe en C++ diffère lorsque vous utilisez des classes de la
VCL et les extensions du langage C++ permettant de prendre en charge le
modèle de programmation “composant-propriété-événement” de C++Builder.
Un chapitre décrit comment utiliser les objets de la bibliothèque de
composants Borland multiplate-forme (CLX) pour développer des applications
pouvant être compilées et exécutées sous Windows ou sous Linux.
Le chapitre sur le déploiement aborde les opérations nécessaires pour
distribuer votre application auprès de ses utilisateurs. Ce chapitre donne des
informations sur les options de compilation, l’utilisation de InstallShield
Express, les problèmes de droits de distribution et sur le choix des paquets,
DLL et autres bibliothèques qu’il faut utiliser pour générer la version
distribuée d’une application.
• La partie II, “Développement d’applications de bases de données”, décrit
comment construire des applications de bases de données en utilisant les
outils et les composants base de données. C++Builder vous permet d’accéder à
de nombreux types de bases de données, notamment des bases de données
locales, comme Paradox et dBASE, et des bases de données serveur SQL en
réseau, comme InterBase, Oracle et Sybase. Vous pouvez choisir parmi divers
mécanismes d’accès aux données, dont dbExpress, BDE (moteur de bases de
données Borland), InterBaseExpress et ADO (ActiveX Data Objects). Pour
implémenter les applications de bases de données les plus évoluées, vous avez
besoin de fonctionnalités qui ne sont pas disponibles dans toutes les éditions
de C++Builder.
• La partie III, “Ecriture d’applications Internet”, décrit comment créer des
applications distribuées sur internet. C++Builder comprend une large gamme
d’outils permettant d’écrire des applications serveur web : Web Broker
(agent Web), une architecture pour la création d’applications serveur
multiplates-formes ; WebSnap, qui vous permet de concevoir des pages web
dans un environnement GUI ; le support de l’utilisation de documents XML ;
BizSnap, une architecture pour utiliser les services web basés sur SOAP.
Cette partie contient également un chapitre sur les composants socket de
C++Builder qui vous permettent de créer des applications pouvant
communiquer avec d’autres systèmes utilisant TCP/IP et des protocoles
voisins. Les sockets fournissent des connexions basées sur le protocole TCP/
IP, mais sont suffisamment généraux pour fonctionner avec des protocoles
apparentés comme Xerox Network System (XNS), DECnet de Digital ou la
famille IPX/SPX de Novell.
• La partie IV, “Développement d’applications COM”, décrit comment
construire des applications qui peuvent interagir avec d’autres objets API
basés sur COM. C++Builder supporte les applications COM basées sur les
experts ATL (Active Template Library) et un éditeur de bibliothèque de types
pour faciliter le développement de serveurs COM. Un outil d’importation
vous permet de créer rapidement des applications client. Le support des
clients COM est disponible dans toutes les éditions de C++Builder. Le support
des serveurs COM n’est pas disponible dans toutes les éditions de C++Builder.
1-2
Guide du développeur
Conventions typographiques
• La partie V, “Création de composants personnalisés”, décrit la manière de
concevoir et d’implémenter vos propres composants et de les intégrer à la
palette des composants de l’EDI. Un composant peut quasiment constituer
tout élément de programme manipulable à la conception. L’implémentation de
composants personnalisés nécessite la dérivation d’une nouvelle classe à partir
d’une classe existante de la bibliothèque de classes VCL ou CLX.
Conventions typographiques
Ce manuel utilise les polices et les symboles décrits dans le tableau suivant pour
mettre en évidence des parties particulières du texte :
Tableau 1.1 Polices et symboles utilisés dans ce manuel
Police ou symbole
Police à pas fixe
[]
Signification
Le texte apparaissant avec une police à pas fixe sert à représenter le
texte tel qu’il apparaît à l’écran ou dans du code C++. Il indique aussi
les valeurs à saisir au clavier.
Les crochets dans le texte ou dans une syntaxe représentent des
éléments facultatifs. Ces crochets sont à omettre lors de la saisie.
Gras
Les mots en gras dans le texte ou dans le code servent à représenter les
mots réservés du C++ et les options du compilateur.
Italique
Les mots en caractères italiques représentent des identificateurs du C++
comme les variables et les noms de types. L’italique sert aussi à faire
ressortir certains mots comme les nouveaux termes.
Touches
Cette police sert à indiquer une touche du clavier. Par exemple,
“Appuyez sur Echap pour quitter un menu”.
Support technique
Borland propose diverses options de support, notamment des services gratuits
sur Internet vous permettant de faire des recherches dans notre base
documentaire et de contacter d’autres utilisateurs de produits Borland.
Vous disposez aussi de différents services de support technique et d’un support
payant de type Consulting.
Pour plus d’informations sur les services de support développeur proposés
par Borland, visitez notre site Web à l’adresse http://www.borland.com/
devsupport/bcppbuilder. Si vous résidez en France, consultez www.borland.fr
ou http://www.borland.com/bww/intlcust.html si vous êtes situé dans un
autre pays.
Quand vous contactez le support, soyez prêt à fournir des informations
complètes sur l’environnement, la version et l’édition du produit que vous
utilisez, ainsi qu’une description détaillée du problème.
Introduction
1-3
1-4
Guide du développeur
Partie
I
Programmation C++Builder
Partie I
Les chapitres de cette partie présentent les concepts et connaissances nécessaires
pour créer des applications C++Builder avec n’importe quelle édition du produit.
Programmation C++Builder
Chapitre
2
Développement d’applications
avec C++Builder
Chapitre 2
Borland C++Builder est un environnement de programmation visuelle orienté
objet permettant le développement d’applications 32 bits en vue de leur
déploiement sous Windows et sous Linux. En utilisant C++Builder, vous pouvez
créer de puissantes applications avec un minimum de programmation.
C++Builder propose un ensemble d’outils de conception pour le développement
rapide d’applications (RAD), dont des experts programmateur et des modèles
d’applications ou de fiches, et gère la programmation orientée objet avec deux
bibliothèques étendues de classes :
• La bibliothèque de classes VCL comprend des objets qui encapsulent l’API
Windows ainsi que d’autres techniques de programmation utiles (Windows).
• La bibliothèque de composants Borland multiplate-forme (CLX), qui contient des
objets encapsulant la bibliothèque Qt (Windows ou Linux).
Ce chapitre décrit brièvement l’environnement de développement C++Builder et
comment il s’inscrit dans le cycle de développement. Le reste de ce manuel
donne des détails techniques sur le développement d’applications, la gestion des
bases de données et les applications Internet ou Intranet, la création de contrôles
ActiveX ou COM, et sur l’écriture de composants personnalisés.
L’environnement de développement intégré
Au démarrage de C++Builder, vous êtes immédiatement placé dans
l’environnement de développement intégré, appelé également EDI. Cet
environnement propose tous les outils nécessaires à la conception, au test, au
débogage et au déploiement d’applications, permettant un prototypage rapide et
un cycle de développement plus court.
Développement d’applications avec C++Builder
2-1
Conception d’applications
L’EDI dispose de tous les outils nécessaires pour commencer à concevoir une
application :
• Le concepteur de fiche, une fenêtre vide, appelée une fiche, dans laquelle
concevoir l’interface utilisateur, de l’application.
• La palette des composants qui affiche des composants visuels ou non visuels
que vous pouvez utiliser pour concevoir votre interface utilisateur.
• L’inspecteur d’objets pour connaître ou modifier les propriétés et événements
d’un objet.
• L’arborescence d’objets pour afficher ou modifier les relations logiques d’un
objet.
• L’éditeur de code pour écrire ou modifier la logique sous-jacente d’un
programme.
• Le gestionnaire de projet qui permet de gérer les fichiers constituant un ou
plusieurs projets.
• Le débogueur intégré pour rechercher et corriger les erreurs dans votre code.
• De nombreux outils, comme les éditeurs de propriété qui permettent de
modifier la valeur des propriétés d’un objet.
• Des outils en ligne de commande, y compris des compilateurs, des éditeurs de
liens.
• Des bibliothèques de classes contenant de nombreux objets réutilisables. De
nombreux objets fournis dans la bibliothèque des classes sont accessibles dans
la palette des composants de l’EDI. Par convention, les noms des objets de la
bibliothèque des classes commencent par un T, comme TStatusBar.
Certains d’entre eux ne font pas partie de toutes les éditions du produit.
Une présentation plus complète de l’environnement de développement est
proposée dans le manuel Prise en main, livré avec le produit. En outre, le système
d’aide en ligne offre de l’aide sur tous les menus, boîtes de dialogues et fenêtres.
Conception d’applications
Vous pouvez utiliser C++Builder pour concevoir tout type d’application 32 bits,
que ce soit un utilitaire de portée générale ou un programme complexe de
gestion de données ou des applications distribuées.
Alors même que vous concevez visuellement l’interface utilisateur d’une
application, C++Builder génère le code C++ sous-jacent pour gérer l’application.
Dès que vous sélectionnez et modifiez les propriétés des composants et des
fiches, le résultat de ces modifications apparaît automatiquement dans le code
source, et vice-versa. Vous pouvez modifier directement les fichiers source avec
tout éditeur de texte, y compris l’éditeur de code intégré. Les modifications
effectuées dans le code sont immédiatement reflétées dans l’environnement
visuel.
Dans C++Builder vous pouvez créer vos propres composants. La plupart des
composants fournis sont écrits en Pascal Objet. Vous pouvez ajouter à la palette
les composants que vous avez écrits et la personnaliser à votre convenance en
insérant de nouveaux onglets.
2-2
Guide du développeur
Création des projets
Vous pouvez également utiliser C++Builder pour concevoir des applications
s’exécutant sous Linux et sous Windows en utilisant CLX. CLX contient un
ensemble de classes qui, lorsque vous les utilisez à la place de la VCL,
permettent à votre programme de passer indifféremment de Windows à Linux.
Reportez-vous au chapitre 14, “Développement d’applications multiplatesformes”, pour avoir des détails sur le développement multiplate-forme et
connaître les différences entre les environnements Windows et Linux.
Le chapitre 7, “Création d’applications, de composants et de bibliothèques”, décrit
comment C++Builder gère les différents types d’applications.
Création des projets
Tout le développement d’applications avec C++Builder s’effectue par le biais des
projets. Quand vous créez une application dans C++Builder, vous créez un
projet. Un projet est une collection de fichiers qui constituent une application.
Certains de ces fichiers sont créés au cours de la conception. D’autres sont
générés automatiquement lorsque vous compilez le code source du projet.
Vous pouvez voir le contenu d’un projet à l’aide d’un outil de gestion de projet
nommé le Gestionnaire de projet. Le gestionnaire de projet présente la liste, sous
forme d’une vue hiérarchisée, des noms d’unités et des fiches contenues
éventuellement dans chaque unité, ainsi que les chemins d’accès aux fichiers du
projet. Vous pouvez modifier directement la plupart de ces fichiers, mais il est
plus simple et plus sûr d’utiliser les outils visuels de C++Builder.
En haut de la hiérarchie, se trouve un fichier groupe. Vous pouvez combiner
plusieurs projets dans un groupe de projets. Cela vous permet d’ouvrir plusieurs
projets à la fois dans le gestionnaire de projet. Les groupes de projets permettent
de rassembler des projets liés et de travailler sur eux, par exemple des
applications qui fonctionnent ensemble ou font partie d’une application
multiniveau. Si vous ne travaillez que sur un seul projet, vous n’avez pas besoin
de fichier groupe de projets pour créer une application.
Les fichiers projet, qui décrivent des projets individuels, des fichiers et des
options associées, portent l’extension .bpr. Les fichiers projet contiennent des
directives pour la construction d’une application ou d’un objet partagé. Quand
vous ajoutez et supprimez des fichiers en utilisant le gestionnaire de projet, le
fichier projet est mis à jour. Vous spécifiez les options du projet dans la boîte de
dialogue Options de projet, qui contient des onglets pour les divers aspects de
votre projet, comme les fiches, l’application, le compilateur. Ces options de projet
sont stockées avec le projet dans le fichier projet.
Les unités et les fiches sont les blocs de base de la construction d’une application
C++Builder. Un projet peut partager n’importe quel fichier fiche et unité existant,
y compris ceux qui se trouvent hors de l’arborescence des répertoires du projet.
Cela inclut des procédures et des fonctions personnalisées, écrites sous forme de
routines indépendantes.
Si vous ajoutez à un projet un fichier partagé, celui-ci n’est pas copié dans le
répertoire du projet en cours ; il reste à sa place initiale. L’ajout du fichier
Développement d’applications avec C++Builder
2-3
Modification du code
partagé au projet en cours inscrit le nom et le chemin du fichier dans le fichier
projet. C++Builder s’en charge automatiquement lorsque vous ajoutez des unités
à un projet
Quand vous compilez un projet, l’emplacement des fichiers qui constituent le
projet n’a aucune importance. Le compilateur traite les fichiers partagés comme
ceux créés par le projet lui-même.
Modification du code
L’éditeur de code C++Builder est un éditeur ASCII complet. Si vous utilisez
l’environnement de programmation visuel, une fiche est automatiquement
affichée dans un nouveau projet. Vous pouvez commencer la conception de
l’interface de votre application en plaçant des objets sur la fiche et en modifiant
leur fonctionnement dans l’inspecteur d’objets. Mais d’autres tâches de
programmation, comme l’écriture des gestionnaires d’événements pour les objets,
doivent se faire en tapant directement le code.
Le contenu d’une fiche et toutes ses propriétés ainsi que ses composants et leurs
propriétés peuvent être modifiés sous forme de texte dans l’éditeur de code.
Vous pouvez ajuster le code généré dans l’éditeur de code et ajouter d’autres
composants en tapant du code dans l’éditeur. Au fur et à mesure que vous tapez
du code dans l’éditeur, le compilateur l’analyse constamment afin de changer la
disposition de la fiche. Vous pouvez revenir à la fiche, voir et tester les
changements apportés dans l’éditeur, puis continuer à modifier la fiche ellemême.
La génération de code C++Builder et le système de flux des propriétés est
entièrement ouvert à l’examen. Le code source de tout ce qui se trouve dans le
fichier exécutable final (tous les objets VCL, les objets CLX, les sources RTL et
les fichiers projet) peut être visualisé et modifié dans l’éditeur de code.
Compilation des applications
Quand vous avez fini de concevoir l’interface de votre application sur la fiche,
après avoir écrit le code supplémentaire souhaité, vous pouvez compiler le projet
depuis l’EDI ou depuis la ligne de commande.
Tous les projets ont comme cible un fichier exécutable distribuable unique. Vous
pouvez voir ou tester votre application à divers stades du développement en la
compilant, la construisant ou l’exécutant :
• Quand vous la compilez, seules les unités qui ont changé depuis la dernière
compilation sont recompilées.
• Quand vous la construisez, toutes les unités du projet sont compilées, qu’elles
aient ou non changé depuis la dernière compilation. Cette technique est utile
quand vous n’êtes pas certain des fichiers qui ont été modifiés ou quand vous
voulez simplement garantir que tous les fichiers en cours soient synchronisés.
2-4
Guide du développeur
Débogage des applications
Il est également important de construire l’application quand vous avez changé
les directives globales du compilateur, afin d’assurer que tout le code se
compile de façon correcte. Vous pouvez tester ainsi la validité de votre code
source, sans compiler le projet.
• Quand vous l’exécutez, vous compilez l’application, puis l’exécutez. Si vous
avez modifié le code source depuis la dernière compilation, le compilateur
recompile les modules qui ont été changés et lie à nouveau votre application.
Si vous avez regroupé plusieurs projets, vous pouvez les compiler ou les
construire tous à la fois dans un même groupe de projet. Choisissez Projet|
Compiler tous les projets ou Projet|Construire tous les projets, le groupe de
projets étant sélectionné dans le gestionnaire de projet.
CLX
Pour compiler une application CLX sous Linux, une application Borland C++
n’est pas encore disponible, mais vous pouvez déjà développer l’application dans
C++Builder.
Débogage des applications
C++Builder dispose d’un débogueur intégré qui permet de localiser et de
corriger les erreurs d’une application. Le débogueur intégré permet de contrôler
l’exécution du programme, de surveiller la valeur de variables et d’éléments de
structures de données ou de modifier la valeur de données lors de l’exécution.
Le débogueur intégré peut suivre à la fois les erreurs d’exécution et les erreurs
de logique. En exécutant le programme jusqu’à un emplacement spécifique et en
visualisant la valeur des variables, les fonctions de la pile des appels et les
sorties du programme, vous pouvez surveiller son comportement et trouver les
endroits où il ne se comporte pas comme prévu. Le débogueur est décrit plus en
détail dans l’aide en ligne.
Vous pouvez également utiliser la gestion des exceptions pour connaître,
localiser et traiter les erreurs. Les exceptions dans C++Builder sont des classes,
comme les autres classes de C++Builder, sauf que, par convention, leur nom
commence par E au lieu de T. Voir chapitre 12, “Gestion des exceptions”, pour
davantage d’informations sur la gestion des exceptions.
Déploiement des applications
C++Builder dispose d’outils facilitant le déploiement d’une application. Par
exemple, InstallShield Express (non disponible dans toutes les éditions) vous aide
à créer un programme d’installation pour votre application qui contient tous les
fichiers nécessaires à l’exécution de l’application distribuée. Le logiciel
TeamSource (non disponible dans toutes les éditions) est également disponible
pour suivre les mises à jour des applications.
Remarque
Les versions de C++Builder n’ont pas toutes des capacités de déploiement.
Développement d’applications avec C++Builder
2-5
Déploiement des applications
CLX
Pour déployer une application CLX sous Linux, une application Borland C++
n’est pas encore disponible, mais vous pouvez déjà développer l’application dans
C++Builder.
Pour davantage d’informations sur le déploiement, voir chapitre 17,
“Déploiement des applications”.
2-6
Guide du développeur
Chapitre
3
Utilisation des bibliothèques
de composants
Chapitre 3
Ce chapitre présente les bibliothèques de classes et décrit certains composants
que vous pouvez utiliser au cours du développement de vos applications.
C++Builder comprend à la fois la bibliothèque de composants visuels (VCL)
et la bibliothèque de composants Borland multiplate-forme (CLX). La VCL est
réservée au développement Windows tandis que CLX permet le développement
multiplate-forme Windows et Linux. Ces deux bibliothèques de classes sont
différentes mais présentent de nombreuses similarités.
Présentation des bibliothèques de classes
VCL et CLX sont des bibliothèques de classes constituées d’objets que vous
utilisez pour développer des applications. Les deux bibliothèques se ressemblent
et contiennent de nombreux objets identiques. Certains objets de la VCL
implémentent des fonctionnalités disponibles uniquement sous Windows, comme
les objets qui apparaissent dans les onglets ADO, BDE, QReport, COM+ et
Serveurs de la palette des composants. Pratiquement tous les objets CLX sont
disponibles à la fois pour Windows et pour Linux.
Tous les objets VCL et CLX dérivent de TObject qui est une classe abstraite dont
les méthodes encapsulent des comportements essentiels comme la construction,
la destruction et la gestion des messages. Quand vous écrivez vos propres
classes, elles doivent dériver de TObject dans la bibliothèque de classes que vous
prévoyez utiliser.
Les composants sont un sous-ensemble de la VCL ou de CLX et dérivent de la
classe abstraite TComponent. Vous pouvez placer les composants dans une fiche
ou un module de données et les manipuler pendant la conception. La plupart
des composants sont visuels ou non, selon qu’ils apparaissent ou pas à
l’exécution. Certains des composants apparaissent sur la palette des composants.
Utilisation des bibliothèques de composants
3-1
Présentation des bibliothèques de classes
Les composants visuels, comme TForm ou TSpeedButton sont appelés des contrôles
et dérivent de TControl. TControl propose des propriétés qui spécifient les
attributs visuels des contrôles comme la hauteur ou la largeur.
Les composants non visuels sont utilisés pour diverses tâches. Si, par exemple,
vous écrivez une application qui se connecte à une base de données, vous
pouvez placer un composant TDataSource dans une fiche pour connecter un
contrôle et l’ensemble de données utilisé par le contrôle. Cette connection est
invisible pour l’utilisateur, TDataSource est donc non visuel. A la conception,
les composants non visuels sont représentés par une icône. Cela vous permet
de manipuler leurs propriétés et événements comme pour un contrôle visuel.
Vous pouvez accéder à des informations détaillées sur tous les objets VCL ou
CLX, en utilisant l’aide en ligne pendant que vous programmez. Dans l’éditeur
de code, placez le curseur en un endroit quelconque de l’objet et appuyez sur F1
pour afficher de l’aide. Les objets, propriétés, méthodes et événements qui font
partie de la VCL sont marqués “Référence VCL” et ceux faisant partie de CLX
sont marqués “Référence CLX”.
Propriétés, méthodes et événements
VCL et CLX sont toutes deux une hiérarchie d’objets, intégrée à l’EDI qui vous
permet de développer rapidement des applications. Les objets des deux
bibliothèques sont basés sur des propriétés, des méthodes et des événements.
Chaque objet contient des données membres (propriétés), des fonctions qui
opèrent sur les données (méthodes) et un moyen d’interagir avec les utilisateurs
des classes (événements). La VCL et CLX sont écrits en Pascal Objet, même si la
VCL est basée sur l’API Windows et CLX sur bibliothèque de widgets Qt.
Propriétés
Les Propriétés sont les caractéristiques d’un objet, relatives à son comportement
visible ou aux opérations qu’il effectue. Par exemple, la propriété Visible
détermine si un objet doit être vu ou non dans l’interface d’une application.
Des propriétés bien conçues simplifient l’utilisation de vos composants par
d’autres programmeurs et en facilite la maintenance.
Voici quelques fonctionnalités utiles des propriétés :
• Alors que les méthodes ne sont disponibles qu’à l’exécution, vous pouvez
accéder aux propriétés et les modifier au cours de la conception et obtenir une
réponse immédiate des composants dans l’EDI.
• Les propriétés peuvent être accédées via l’inspecteur d’objets dans lequel vous
pouvez changer les valeurs visuellement. Définir les propriétés au moment de
la conception est plus simple qu’écrire directement le code, et ce dernier est
plus facile à maintenir.
• Comme les données sont encapsulées, elles sont protégées et privées pour
l’objet réel.
3-2
Guide du développeur
Présentation des bibliothèques de classes
• Les appels effectués pour obtenir ou définir des valeurs sont des méthodes, et
donc le traitement reste invisible pour l’utilisateur de l’objet. Par exemple, les
données peuvent résider dans une table, mais apparaître au programmeur
comme des données membres normales.
• Vous pouvez implémenter la logique qui déclenche des événements ou
modifier d’autres données pendant l’accès à la propriété. Par exemple, changer
la valeur d’une propriété peut nécessiter la modification d’une autre. Vous
pouvez modifier les méthodes créées pour la propriété.
• Les propriétés peuvent être virtuelles.
• Une propriété n’est pas limitée à un seul objet. Changer une propriété d’un
objet peut affecter plusieurs autres objets. Par exemple, définir la propriété
Checked d’un bouton radio affecte tous les autres boutons radio du groupe.
Méthodes
Une méthode est une fonction qui est un membre d’une classe. Les méthodes
définissent le comportement d’un objet. Les méthodes de classe peuvent accéder
à toutes les propriétés publiques, protégées et privées et aux données membres de la
classe, on les désigne fréquemment par le terme fonctions membres.
Voir “Contrôle des accès” à la page 46-4.
Evénements
Un événement est une action ou une occurrence détectée par un programme.
La plupart des applications modernes sont dites pilotées par événements, car
elles sont conçues pour répondre à des événements. Dans un programme, le
programmeur n’a aucun moyen de prévoir la séquence exacte des actions que
va entreprendre l’utilisateur. Il peut, par exemple, choisir un élément de menu,
cliquer sur un bouton ou sélectionner du texte. Vous allez donc écrire le code
qui gère chacun des événements qui vous intéressent au lieu d’écrire du code
s’exécutant toujours selon le même ordre.
Quelle que soit la façon dont un événement a été appelé, C++Builder recherche
si du code a été écrit pour gérer cet événement. Si c’est le cas, ce code est
exécuté, sinon, le comportement par défaut se produit.
Les types d’événements qui peuvent survenir se divisent en deux grandes
catégories :
• Evénements utilisateur
• Evénements système
Evénements utilisateur
Les événements utilisateur sont des actions initiées par l’utilisateur.
Les événements utilisateur sont, par exemple, OnClick (l’utilisateur a cliqué avec
la souris), OnKeyPress (l’utilisateur a appuyé sur une touche du clavier)
et OnDblClick (l’utilisateur a double-cliqué sur un bouton de la souris).
Utilisation des bibliothèques de composants
3-3
Objets, composants et contrôles
Evénements système
Ce sont des événements que le système d’exploitation déclenche pour vous.
Par exemple, l’événement OnTimer (le composant Timer déclenche l’un de ces
événements lorsqu’un intervalle prédéfini s’est écoulé), l’événement OnCreate (le
composant est en train d’être créé), l’événement OnPaint (un composant ou une
fenêtre a besoin d’être redessiné), etc. En règle générale, ces événements ne sont
pas directement déclenchés par des actions de l’utilisateur.
Objets, composants et contrôles
La figure suivante est une vue très simplifiée de la hiérarchie des héritages qui
illustre les relations entre objets, composants et contrôles.
Figure 3.1
Objets, composants et contrôles
TObject
TComponent
TControl
TForm
TButton
TCheckBox
TListBox
Chaque objet hérite de TObject et beaucoup d’objets héritent de TComponent.
Les contrôles héritent de TControl et ont la capacité à s’afficher eux-mêmes à
l’exécution. Un contrôle comme TCheckBox hérite de toutes les fonctionnalités de
TObject, TComponent et de TControl, et ajoute ses spécificités propres.
Le diagramme suivant est un résumé de la bibliothèque de composants visuels
(VCL) qui montre les principales branches de l’arbre d’héritage. La bibliothèque
de composants Borland multiplate-forme (CLX) lui ressemble beaucoup à ce
niveau, mais TWinControl est remplacé par TWidgetControl.
3-4
Guide du développeur
Objets, composants et contrôles
Figure 3.2
Diagramme simplifié de la hiérarchie
TObject
TPersistent
[Objets]
[Objets]
TComponent
TControl
TWinControl
[Objets]
TGraphicControl
[Objets]
[Objets]
Exception
[Objets]
Plusieurs importantes classes de base montrées dans la figure sont décrites dans
le tableau suivant :
Tableau 3.1 Importantes classes de base
Classe
Description
TObject
C’est la classe de base et l’ancêtre ultime de tout ce qui se trouve dans la
VCL ou dans CLX. TObject encapsule le comportement fondamental commun
à tous les objets VCL/ CLX, en introduisant les méthodes qui effectuent les
fonctions de base, comme la création, la maintenance et la destruction d’une
instance d’objet.
Exception
Spécifie la classe de base de toutes les classes relatives aux exceptions.
Exception fournit une interface cohérente pour les conditions d’erreur et
permet aux applications de les gérer harmonieusement.
TPersistent
Spécifie la classe de base de tous les objets qui implémentent des propriétés.
Les classes issues de TPersistent prennent en charge l’envoi de données aux
flux et permettent l’affectation des classes.
TComponent
Spécifie la classe de base de tous les composants non visuels, comme
TApplication. TComponent est l’ancêtre commun de tous les composants. Cette
classe permet à un composant de figurer sur la palette des composants, de
posséder d’autres composants et d’être manipulé directement sur une fiche.
TControl
Représente la classe de base de tous les contrôles visibles à l’exécution.
TControl est l’ancêtre commun de tous les composants visuels et fournit les
contrôles visuels standard comme la position et le curseur. Cette classe
fournit également des événements qui répondent aux actions de la souris.
TWinControl
Spécifie la classe de base de tous les objets d’interface utilisateur. Les
contrôles issus de TWinControl sont des contrôles fenêtrés qui peuvent
capturer la saisie au clavier. Dans CLX, on les appelle des widgets, et
TWidgetControl remplace TWinControl.
Les quelques sections suivantes présentent une description générale des types de
classes que contient chaque branche. Pour avoir une présentation complète de la
hiérarchie des objets VCL et CLX, reportez-vous aux posters VCL et CLX fournis
avec ce produit.
Utilisation des bibliothèques de composants
3-5
Objets, composants et contrôles
Branche TObject
La branche TObject comprend tous les objets VCL et CLX qui dérivent de TObject
mais non de TPersistent. L’essentiel des capacités des objets VCL et CLX est basé
sur les méthodes définies dans TObject. TObject encapsule le comportement
fondamental commun à tous les objets VCL ou CLX, en introduisant des
méthodes qui permettent :
• De répondre à la création ou à la destruction d’objets.
• De donner des informations sur le type de classe et d’instance d’un objet et
des informations de type à l’exécution (RTTI) sur ses propriétés publiées.
• De gérer le traitement des messages (VCL) ou les événements système (CLX).
TObject est l’ancêtre immédiat de nombreuses classes simples. Les classes
contenues dans la branche de TObject ont une caractéristique commune
importante : elles sont transitoires. Cela signifie que ces classes ne disposent pas
d’une méthode pour enregistrer leur état avant leur destruction ; elles ne sont
pas persistantes.
Le groupe de classes le plus important de cette branche est constitué par la
classe Exception. Cette classe propose un grand nombre de classes d’exceptions
prédéfinies pour gérer automatiquement de nombreuses conditions d’exception
comme les erreurs de division par zéro, les erreurs d’entrées/sorties ou les
transtypages incorrects.
La branche TObject contient également un autre groupe de classes qui
encapsulent des structures de données, comme :
• TBits, une classe qui stocke un “tableau” de valeur booléennes.
• TList, une classe liste liée.
• TStack, une classe qui gère un tableau de pointeurs du type dernier entré,
premier sorti.
• TQueue, une classe qui gère un tableau de pointeurs du type premier entré,
premier sorti.
Dans la VCL, vous trouverez aussi des enveloppes pour les objets externes
comme TPrinter, qui encapsulent l’interface imprimante Windows, et TRegistry,
une enveloppe de bas niveau pour le registre du système et les fonctions qui
opèrent sur le registre. Elles sont spécifiques à l’environnement Windows.
TStream est un bon exemple du type de classes contenues dans cette branche.
TStream est la classe de base des objets flux qui permettent de lire ou d’écrire sur
divers types de support de données, comme les fichiers disque ou la mémoire
vive.
Globalement, cette branche contient un grand nombre de différents types de
classes particulièrement utiles pour le développeur.
3-6
Guide du développeur
Objets, composants et contrôles
Branche TPersistent
La branche TPersistent comprend tous les objets VCL et CLX qui dérivent de
TPersistent mais pas de TComponent. La persistance détermine ce qui est
enregistré dans un fichier fiche ou un module de données et ce qui est chargé
dans la fiche ou le module de données lorsqu’il est extrait de la mémoire.
Les objets de cette branche implémentent des propriétés pour les composants.
Les propriétés sont uniquement chargés et enregistrés avec une fiche si elles ont
un propriétaire. Le propriétaire doit être un composant. Cette branche introduit
la fonction GetOwner qui vous permet de déterminer le propriétaire de la
propriété.
Les objets de cette branche sont également les premiers à inclure une section
publiée dans laquelle les propriétés peuvent être automatiquement chargées et
enregistrées. Un méthode DefineProperties vous permet aussi d’indiquer la façon
de charger et d’enregistrer les propriétés.
Voici quelques autres classes de la branche TPersistent de la hiérarchie :
• TGraphicsObject, une classe de base abstraite pour les objets graphiques, par
exemple : TBrush, TFont et TPen.
• TGraphic, une classe de base abstraite pour les objets comme TBitmap et TIcon,
qui peuvent stocker et afficher des images visuelles.
• TStrings, une classe de base pour les objets qui représentent une liste de
chaînes.
• TClipboard, une classe contenant du texte ou des graphiques qui ont été
coupés ou copiés d’une application.
• TCollection, TOwnedCollection et TCollectionItem, classes qui maintiennent les
collections indexées d’éléments spécialement définis.
Branche TComponent
La branche TComponent contient des objets qui dérivent de TComponent mais pas
de TControl. Les objets de cette branche sont des composants que vous pouvez
manipuler sur des fiches au cours de la conception. Ce sont des objets persistants
aux capacités suivantes :
• Ils apparaissent dans la palette des composants et peuvent être modifiés dans
le concepteur de fiche.
• Ils peuvent posséder et gérer d’autres composants.
• Ils se chargent et s’enregistrent eux-mêmes.
Plusieurs méthodes de TComponent dictent la façon dont agissent les composants
durant la conception et les informations qui sont enregistrées avec le composant.
La gestion des flux est introduite dans cette branche de la VCL et de CLX.
C++Builder gère automatiquement la plupart des opérations principales relatives
aux flux. Les propriétés sont persistantes si elles sont publiées et les propriétés
publiées sont automatiquement mises en flux.
Utilisation des bibliothèques de composants
3-7
Objets, composants et contrôles
La classe TComponent introduit également le concept de possession (propriété)
qui se propage dans la VCL et dans CLX. Deux propriétés supportent la
possession : Owner et Components. Chaque composant dispose d’une propriété
Owner qui référence un autre composant comme son propriétaire. Un composant
peut posséder d’autres composants. Dans ce cas, tous les composants possédés
sont référencés dans la propriété Array du composant.
Le constructeur d’un composant prend un seul paramètre qui est utilisé pour
spécifier le propriétaire du nouveau composant. Si le propriétaire transmis existe,
le nouveau composant est ajouté à la liste des composants du propriétaire. Outre
la liste des composants, pour référencer les composants possédés, cette propriété
fournit la destruction automatique des composants possédés. Si le composant a
un propriétaire, il est détruit lorsque le propriétaire est détruit. Par exemple,
comme TForm est un descendant de TComponent, tous les composants possédés
par la fiche sont détruits, et leur emplacement en mémoire libéré, lorsque la fiche
est détruite. Cela garantit que tous les composants de la fiche se nettoient euxmêmes correctement lorsque leurs destructeurs sont appelés.
Si un type de propriété est TComponent ou un de ses descendants, le système de
flux crée une instance de ce type lorsqu’il le lit. Si un type de propriété est
TPersistent mais pas TComponent, le système de flux utilise l’instance existante,
accessible via la propriété, et lit les valeurs des propriétés de cette instance.
Quand il crée un fichier fiche (fichier utilisé pour stocker les informations
relatives aux composants de la fiche), le concepteur de fiche parcourt le tableau
de ses composants et enregistre tous les composants dans la fiche. Chaque
composant “sait” comment écrire dans un flux (dans ce cas, un fichier texte) ses
propriétés modifiées. Parallèlement, lorsqu’il charge les propriétés des
composants du fichier fiche, le concepteur de fiche parcourt le tableau des
composants et charge chacun d’eux.
Cette branche contient, entre autres, les types de classes suivants :
• TActionList, une classe qui gère une liste d’actions utilisées par des
composants et des contrôles, comme les éléments de menu et les boutons.
• TMainMenu, une classe qui définit une barre de menus et les menus
déroulants associés dans une fiche.
• Les classes TOpenDialog, TSaveDialog, TFontDialog, TFindDialog, TColorDialog,
etc., fournissent les boîtes de dialogue communes.
• TScreen, une classe qui mémorise les fiches et les modules de données
instanciés par l’application, la fiche active et le contrôle actif dans cette fiche,
la taille et la résolution de l’écran, ainsi que les curseurs et les fontes
utilisables par l’application.
Les composants qui n’ont pas besoin d’interface visuelle peuvent être
directement dérivés de TComponent. Pour faire un outil tel qu’un périphérique
TTimer, vous devez le dériver de TComponent. Ce type de composant se trouve
sur la palette des composants, mais exécute des fonctions internes accessibles par
le code et qui n’apparaissent pas, à l’exécution, dans l’interface utilisateur.
3-8
Guide du développeur
Objets, composants et contrôles
Dans CLX, la branche TComponent contient aussi THandleComponent. C’est la
classe de base des composants non visuels qui nécessitent un handle sur un objet
Qt sous-jacent, comme les boîtes de dialogue et les menus.
Pour davantage d’informations sur l’initialisation des propriétés, l’appel des
méthodes ou l’utilisation des événements d’un composant, voir chapitre 5,
“Utilisation des composants”.
Branche TControl
La branche TControl est constituée de composants qui dérivent de TControl mais
pas de TWinControl (TWidgetControl dans CLX). Les objets de cette branche sont
des contrôles, c’est-à-dire des objets visuels que l’utilisateur de l’application peut
voir et manipuler à l’exécution. Tous les contrôles ont des propriétés, méthodes
et événements communs qui sont propres à l’aspect visuel des contrôles, comme
la position du contrôle, le curseur associé à la fenêtre (au widget dans CLX) du
contrôle, des méthodes pour dessiner ou déplacer le contrôle et des événements
permettant de répondre aux actions de la souris. Ils ne peuvent jamais recevoir
la saisie du clavier.
Si TComponent définit des comportements communs à tous les composants,
TControl définit ceux communs à tous les contrôles visuels. Il s’agit de routines
de dessin, des événements standard et de la notion de conteneur.
Tous les contrôles visuels partagent certaines propriétés. Ces propriétés sont
héritées de TControl mais elles ne sont publiées (elles apparaissent dans
l’inspecteur d’objets) que pour les composants où elles sont applicables. Ainsi,
TImage ne publie pas la propriété Color car sa couleur est déterminée par l’image
qu’il affiche.
Il y a deux types de contrôles :
• Ceux qui ont leur propre fenêtre (ou widget).
• Ceux qui utilisent la fenêtre (ou le widget) de leur parent.
Les contrôles qui ont leur propre fenêtre sont appelés contrôles “fenêtrés” (VCL)
ou “basés sur des widgets” (CLX) et dérivent de TWinControl (TWidgetControl
dans CLX). Les boutons et les cases à cocher en font partie.
Les contrôles qui utilisent une fenêtre (ou un widget) parent sont appelés
contrôles graphiques et dérivent de TGraphicControl. Les contrôles image et forme
en font partie. Les contrôles graphique ne possèdent pas de handle et ne peuvent
pas recevoir la focalisation de saisie. Comme un contrôle graphique n’a pas
besoin d’un handle, il utilise moins de ressources système. Les contrôles
graphique doivent se dessiner eux-mêmes et ne peuvent pas servir de parent
à d’autres contrôles.
Voir “Contrôles graphiques” à la page 9-20 pour davantage d’informations sur
les autres contrôles graphiques et le chapitre 9, “Types de contrôles”, pour
davantage d’informations sur les différents types de contrôles. Voir le chapitre 6,
“Manipulation des contrôles”, pour davantage d’informations sur la manière
d’interagir sur les contrôles à l’exécution.
Utilisation des bibliothèques de composants
3-9
Objets, composants et contrôles
Branche TWinControl/TWidgetControl
Dans la VCL, la branche TWinControl contient tous les contrôles qui dérivent de
TWinControl. TWinControl est la classe de base de tous les contrôles fenêtrés, qui
sont des éléments que vous utilisez dans l’interface utilisateur d’une application,
comme les boutons, les étiquettes ou les barres de défilement. Les contrôles
fenêtrés encapsulent un contrôle de Windows.
Dans CLX, TWidgetControl, qui remplace TWinControl, est la classe de base de
tous les contrôles widget qui encapsulent des widgets.
Les contrôles fenêtrés et widgets :
• Peuvent recevoir la focalisation lors de l’exécution de l’application, ce qui
signifie qu’ils peuvent recevoir les saisies clavier effectuées par l’utilisateur de
l’application. Par comparaison, d’autres contrôles peuvent seulement afficher
des données.
• Peuvent être le parent d’un ou de plusieurs contrôles enfant.
• Ont un handle ou un identificateur unique.
La branche TWinControl/TWidgetControl contient des contrôles dessinés
automatiquement (comme TEdit, TListBox, TComboBox, TPageControl, etc) et des
contrôles personnalisés que C++Builder doit dessiner, comme TDBNavigator,
TMediaPlayer (VCL uniquement) ou TGauge (VCL uniquement). Les descendants
directs de TWinControl /TWidgetControl implémentent typiquement des contrôles
standard, comme un champ d’édition, une boîte à option ou un contrôle page, et
savent donc déjà comment se dessiner eux-mêmes.
La classe TCustomControl est fournie pour les composants qui nécessitent un
handle mais n’encapsulent pas de contrôle standard ayant la capacité de se
redessiner lui-même. Vous n’avez jamais à vous soucier de la façon dont les
contrôles s’affichent ou répondent aux événements — C++Builder encapsule
complètement ce comportement pour vous.
3-10
Guide du développeur
Chapitre
4
Utilisation de BaseCLX
Chapitre 4
De nombreuses unités communes à la VCL et à CLX fournissent la prise en
charge sous-jacente des deux bibliothèques de composants. Ces unités sont
collectivement dénommées BaseCLX. BaseCLX n’inclut aucun des composants
qui apparaissent sur la palette des composants. Elle inclut à la place un certain
nombre de classes et de routines globales utilisées par les composants qui
apparaissent sur la palette des composants. Ces classes et routines sont
également disponibles pour une utilisation dans du code d’application ou pour
l’écriture de vos propres classes.
Remarque
Les routines globales constituant BaseCLX sont souvent appelées la bibliothèque
d’exécution. Il ne faut pas confondre ces routines avec la bibliothèque
d’exécution C++. Un grand nombre de ces routines ont des fonctions similaires
à celles de la bibliothèque d’exécution C++, mais elles peuvent se distinguer à
cause de leurs noms de fonctions qui commencent par une lettre capitale, et elles
sont déclarées dans l’en-tête d’une unité.
Les rubriques suivantes traitent de bon nombre des classes et routines qui
constituent BaseCLX, et elles montrent la manière de les utiliser. Ces utilisations
incluent :
•
•
•
•
•
•
•
•
Remarque
Utilisation des flux
Utilisation des fichiers
Utilisation des fichiers ini et du registre
Utilisation des listes
Utilisation des listes de chaînes
Utilisation des chaînes
Conversion de mesures
Création d’espaces de dessin
Cette liste de tâches n’est pas exhaustive. La bibliothèque d’exécution de
BaseCLX contient de nombreuses routines pour l’exécution de tâches qui ne sont
pas mentionnées ici. Ces routines incluent ainsi un ensemble de fonctions
mathématiques (définies dans l’unité Math), des routines pour la manipulation
des valeurs date/heure (définies dans les unités SysUtils et DateUtils) et des
Utilisation de BaseCLX
4-1
Utilisation des flux
routines pour la manipulation des Variants Pascal Objet (définies dans l’unité
Variants).
Utilisation des flux
Les flux sont des classes qui vous permettent de lire et d’écrire des données.
Ils offrent une interface commune pour la lecture et l’écriture sur différents
supports, tels que la mémoire, les chaînes, les sockets et les champs BLOB dans
les bases de données. Il existe plusieurs classes de flux, qui dérivent toutes de
TStream. Chaque classe de flux est propre à un type de support. Par exemple,
TMemoryStream lit ou écrit dans une image mémoire, alors que TFileStream lit
ou écrit dans un fichier.
Utilisation des flux pour lire ou écrire des données
Les classes de flux partagent toutes plusieurs méthodes pour lire et écrire des
données. Ces méthodes se différencient selon qu’elles effectuent les opérations
suivantes :
• Renvoi du nombre d’octets lus ou écrits.
• Nécessité de connaître le nombre d’octets.
• Déclenchement d’une exception en cas d’erreur.
Méthodes de flux pour la lecture et l’écriture
La méthode Read lit un nombre d’octets spécifié à partir du flux en commençant
par sa Position en cours, dans un tampon. Read déplace ensuite la position en
cours du nombre d’octets effectivement lus. Read a le prototype suivant :
virtual int __fastcall Read(void *Buffer, int Count);
Read est utile quand le nombre d’octets du fichier est inconnu. Read renvoie le
nombre d’octets effectivement transférés, qui peut être inférieur à Count si le flux
ne contient pas Count octets de données après la position courante.
La méthode Write écrit Count octets d’un tampon vers le flux, en commençant
à la Position courante. Write a le prototype suivant :
virtual int __fastcall Write(const void *Buffer, int Count);
Après avoir écrit dans le fichier, Write avance la position en cours du nombre
d’octets écrits et renvoie le nombre d’octets effectivement écrits, qui peut être
inférieur à Count si la fin du tampon a été atteinte ou si le flux ne peut pas
accepter d’autres octets.
Les procédures correspondantes sont ReadBuffer et WriteBuffer qui, à la différence
de Read et Write, ne renvoient pas le nombre d’octets lus ou écrits. Ces
procédures sont utiles dans les situations où le nombre d’octets est connu et
obligatoire, par exemple pour la lecture de structures. ReadBuffer et WriteBuffer
déclenchent une exception (EReadError et EWriteError), si le nombre d’octets ne
correspond pas exactement. Les méthodes Read et Write, au contraire, peuvent
4-2
Guide du développeur
Utilisation des flux
renvoyer un nombre d’octets différent de la valeur demandée. Les prototypes de
ReadBuffer et WriteBuffer sont :
virtual int __fastcall ReadBuffer(void *Buffer, int Count);
virtual int __fastcall WriteBuffer(const void *Buffer, int Count);
Ces méthodes appellent les méthodes Read et Write pour réaliser la lecture et
l’écriture effectives.
Lecture et écriture de composants
TStream définit des méthodes spécialisées, ReadComponent et WriteComponent,
pour la lecture et l’écriture de composants. Vous pouvez les utiliser dans vos
applications pour enregistrer des composants et leurs propriétés lorsque vous les
créez ou que vous les modifiez lors de l’exécution.
ReadComponent et WriteComponent sont les méthodes utilisées par l’EDI pour lire
ou écrire des composants dans des fichiers de fiches. Lors de la gestion de flux
de composants à destination ou en provenance d’un fichier de fiche, les classes
de flux fonctionnent avec les classes TFiler, TReader et TWriter, pour lire des
objets à partir du fichier de fiche ou les écrire sur disque. Pour davantage
d’informations sur l’utilisation du système de flux de composant, voir l’aide en
ligne sur les classes TStream, TFiler, TReader, TWriter et TComponent.
Copie de données d’un flux vers un autre
Lors de la copie de données d’un flux vers un autre, il n’est pas nécessaire de
lire puis d’écrire explicitement les données. Vous pouvez utiliser à la place la
méthode CopyFrom, telle qu’elle est illustrée dans l’exemple suivant.
L’application comprend deux contrôles d’édition (From et To) et un bouton pour
copier le fichier.
void __fastcall TForm1::CopyFileClick(TObject *Sender)
{
TStream* stream1= new TFileStream(From->Text,fmOpenRead | fmShareDenyWrite);
try
{
TStream* stream2 = new TFileStream(To->Text, fmOpenWrite | fmShareDenyRead);
try
{
stream2 -> CopyFrom(stream1, stream1->Size);
}
__finally
{
delete stream2;
}
}
__finally
{
delete stream1;
}
}
Utilisation de BaseCLX
4-3
Utilisation des flux
Spécification de la position et de la taille du flux
Outre les méthodes de lecture et d’écriture, les flux permettent aux applications
de se positionner de manière arbitraire dans le flux ou de changer sa taille.
Après un déplacement sur une position spécifiée, l’opération de lecture ou
d’écriture suivante démarre la lecture ou l’écriture du flux à cette position.
Déplacement sur une position particulière
La méthode Seek est le mécanisme le plus général pour se déplacer vers une
position particulière dans le flux. Il existe deux surcharges pour la méthode Seek :
virtual int __fastcall Seek(int Offset, Word Origin);
virtual __int64 __fastcall Seek(const __int64 Offset, TSeekOrigin Origin);
Les deux surcharges fonctionnent de la même manière. La différence réside dans
le fait qu’une version utilise un entier sur 32 bits pour représenter les positions et
les décalages, alors que l’autre utilise un entier sur 64 bits.
Le paramètre Origin indique la manière d’interpréter le paramètre Offset. Origin
peut prendre l’une des valeurs suivantes :
Valeur
Signification
soFromBeginning
Offset part du début de la ressource. Seek se déplace vers la position
Offset. Offset doit être >= 0.
soFromCurrent
Offset part de la position en cours dans la ressource. Seek se déplace
vers Position + Offset.
soFromEnd
Offset part de la fin de la ressource. Offset doit être <= 0 afin d’indiquer
le nombre d’octets avant la fin du fichier.
Seek réinitialise la position en cours dans le flux en la déplaçant du décalage
spécifié. Seek renvoie la nouvelle position en cours dans le flux.
Utilisation des propriétés de position et de taille
Tous les flux disposent de propriétés qui contiennent la position en cours et la
taille du flux. Ces propriétés sont utilisées par la méthode Seek, ainsi que par
toutes les méthodes qui lisent ou écrivent dans le flux.
La propriété Position indique le décalage en cours dans le flux exprimé en octets
(à partir du début des données du flux).
La propriété Size indique la taille en octets du flux. Elle peut être utilisée pour
déterminer le nombre d’octets disponibles pour une lecture, ou pour tronquer les
données dans le flux.
Size est utilisée de manière interne par les routines qui lisent et écrivent dans
le flux.
L’initialisation de la propriété Size modifie la taille des données dans le flux.
Par exemple, sur un flux de fichier, l’initialisation de Size insère une marque de
fin de fichier pour tronquer le fichier. Si la taille du flux ne peut pas être
4-4
Guide du développeur
Utilisation des fichiers
modifiée, une exception est déclenchée. Ainsi, tenter de modifier la taille d’un
flux de fichier ouvert en lecture seule déclenche une exception.
Utilisation des fichiers
BaseCLX prend en charge plusieurs méthodes de manipulation des fichiers.
En plus de l’utilisation des flux de fichiers, il existe plusieurs routines
de la bibliothèque d’exécution pour les E/S fichier. Les flux de fichiers
et les routines globales pour lire et écrire dans des fichiers sont décrits
dans “Approches des E/S fichier” à la page 4-5.
En plus des opérations d’entrée/sortie, vous pouvez souhaiter manipuler des
fichiers sur disque. La prise en charge des opérations sur les fichiers eux-mêmes
plutôt que sur leur contenu est décrite dans “Manipulation de fichiers” à la
page 4-7.
Remarque
Lors de l’utilisation de CLX dans des applications multi-plates-formes, n’oubliez
pas qu’à la différence du langage Pascal Objet, le système d’exploitation Linux
distingue majuscules et minuscules. Lors de l’utilisation d’objets et de routines
fonctionnant avec des fichiers, soyez attentif à la casse des noms de fichiers.
Approches des E/S fichier
Il existe trois approches que vous pouvez adopter pour la lecture et l’écriture de
fichiers :
• L’approche recommandée pour la manipulation de fichiers consiste à utiliser
des flux de fichier. Les flux de fichier sont des instances d’objet de la classe
TFileStream utilisées pour accéder aux informations de fichiers disque. Les flux
de fichier sont portables et proposent une approche de haut niveau des
opérations d’E/S de fichier. Comme les flux de fichier mettent à disposition le
handle de fichier, cette approche peut être combinée avec la suivante. La
section suivante “Utilisation de flux de fichier” décrit en détail TFileStream.
• Vous pouvez travailler avec des fichiers en utilisant une approche basée sur
un handle. Les handles de fichier sont fournis par le système d’exploitation
lorsque vous créez ou ouvrez un fichier pour manipuler son contenu. L’unité
SysUtils définit un certain nombre de routines de gestion de fichiers qui
manipulent des fichiers en utilisant des handles. Sous Windows, il s’agit
généralement d’enveloppes de fonctions de l’API Windows. Comme les
fonctions Delphi utilisent la syntaxe du Pascal Objet et fournissent
occasionnellement les valeurs par défaut des paramètres, elles peuvent
facilement servir d’interface à l’API Windows. De plus, il existe des versions
correspondantes sous Linux, de sorte que vous pouvez utiliser ces routines
dans des applications multi-plates-formes. Pour utiliser une approche basée
sur un handle, ouvrez tout d’abord un fichier en utilisant la fonction FileOpen
ou créez un nouveau fichier en utilisant la fonction FileCreate. Lorsque vous
disposez du handle, utilisez les routines basées sur un handle pour manipuler
son contenu (écrire une ligne, lire le texte, etc.).
Utilisation de BaseCLX
4-5
Utilisation des fichiers
• La bibliothèque d’exécution C et la bibliothèque C++ standard contiennent un
certain nombre de fonctions et de classes pour manipuler des fichiers. Celles-ci
présentent l’avantage de pouvoir être utilisées dans des applications n’utilisant
pas la VCL ou CLX. Pour des informations sur ces fonctions, consultez la
documentation en ligne de la bibliothèque d’exécution C ou de la bibliothèque
C++ standard.
Utilisation de flux de fichier
TFileStream est une classe qui permet aux applications de lire et d’écrire dans un
fichier résidant sur le disque. Elle est utilisée pour une représentation objet de haut
niveau des flux de fichiers. Comme TFileStream est un objet flux, il partage les
méthodes de flux courantes. Vous pouvez utiliser ces méthodes pour lire ou
écrire dans le fichier, copier des données à destination ou à partir d’autres
classes de flux, et lire ou écrire des valeurs de composants. Consultez
“Utilisation des flux” à la page 4-2 pour des détails sur les capacités héritées par
les flux de fichiers du fait qu’ils sont des classes de flux.
Les flux de fichier vous permettent en outre d’accéder au handle du fichier, pour
que vous puissiez les utiliser avec des routines de gestion de fichiers globales
ayant besoin de ce handle.
Création et ouverture de fichiers en utilisant des flux de fichier
Pour créer ou ouvrir un fichier et accéder à son handle, il suffit d’instancier un
TFileStream. Cela crée ou ouvre le fichier spécifié et propose des méthodes qui
permettent de lire ou d’écrire dans le fichier. Si le fichier ne peut pas être ouvert,
le constructeur TFileStream déclenche une exception.
__fastcall TFileStream(const AnsiString FileName, Word Mode);
Le paramètre Mode spécifie comment le fichier doit être ouvert à la création du
flux de fichier. Le paramètre Mode est constitué d’un mode d’ouverture et d’un
mode de partage reliés par un OU logique. Le mode d’ouverture doit prendre
l’une des valeurs suivantes :
Tableau 4.1 Modes d’ouverture
4-6
Valeur
Signification
fmCreate
TFileStream crée un fichier portant le nom spécifié. S’il existe déjà un
fichier portant ce nom, il est ouvert en mode écriture.
fmOpenRead
Ouvre le fichier en lecture seulement.
fmOpenWrite
Ouvre le fichier en écriture seulement. L’écriture dans le fichier
remplace son contenu actuel.
fmOpenReadWrite
Ouvre le fichier pour en modifier le contenu et non pour le
remplacer.
Guide du développeur
Utilisation des fichiers
Le mode de partage peut prendre l’une des valeurs suivantes, avec les
restrictions énumérées ci-dessous :
Tableau 4.2 Modes de partage
Valeur
Signification
fmShareCompat
Le partage est compatible avec la manière dont les FCB sont ouverts.
fmShareExclusive
En aucun cas une autre application ne peut ouvrir le fichier.
fmShareDenyWrite
Les autres applications peuvent ouvrir le fichier en lecture, mais pas
en écriture.
fmShareDenyRead
Les autres applications peuvent ouvrir le fichier en écriture, mais pas
en lecture.
fmShareDenyNone
Rien n’empêche les autres applications de lire ou d’écrire dans le
fichier.
Notez que le mode de partage utilisable dépend du mode d’ouverture utilisé.
Le tableau suivant montre les modes de partage associés à chaque mode
d’ouverture.
Tableau 4.3
Modes de partage disponibles pour chaque mode d’ouverture
Mode d’ouverture
fmShareCompat fmShareExclusive fmShareDenyWrite fmShareDenyRead fmShareDenyNone
fmOpenRead Non utilisable
Non utilisable
Disponible
Non utilisable
Disponible
fmOpenWrite
Disponible
Disponible
Non utilisable
Disponible
Disponible
fmOpenReadWrite
Disponible
Disponible
Disponible
Disponible
Disponible
Les constantes d’ouverture et de partage de fichiers sont définies dans l’unité
SysUtils.
Utilisation du handle de fichier
En instanciant TFileStream, vous avez accès au handle de fichier. Le handle de
fichier est contenu dans la propriété Handle. Sous Windows, Handle est un handle
de fichier Windows. Sous les versions Linux de CLX, il s’agit d’un handle de
fichier Linux. Handle est en lecture seule et reflète le mode d’ouverture du
fichier. Si vous voulez modifier les attributs du handle de fichier, vous devez
créer un nouvel objet flux de fichier.
Certaines routines de manipulation de fichiers attendent comme paramètre un
handle de fichier. Quand vous disposez d’un flux de fichier, vous pouvez utiliser
la propriété Handle dans toutes les situations où vous utiliseriez un handle de
fichier. Attention car à la différence des handles de flux, les flux de fichiers
ferment les handles de fichiers quand l’objet est détruit.
Manipulation de fichiers
Plusieurs opérations courantes portant sur les fichiers sont prédéfinies dans la
bibliothèque d’exécution BaseCLX. Les procédures et fonctions manipulant les
fichiers agissent à un niveau élevé. Pour la plupart des routines, il vous suffit de
Utilisation de BaseCLX
4-7
Utilisation des fichiers
spécifier le nom du fichier, et la routine effectue alors pour vous les appels
nécessaires au système d’exploitation. Dans certains cas, vous utiliserez à la place
des handles de fichiers.
Attention
Au contraire du langage Pascal Objet, le système d’exploitation Linux distingue
majuscules et minuscules. Faites attention à la casse des caractères lorsque vous
travaillez avec des fichiers dans des applications multi-plates-formes.
Suppression d’un fichier
La suppression efface un fichier du disque et retire son entrée du répertoire du
disque. Il n’y a pas d’opération inverse pour restaurer un fichier supprimé : les
applications doivent donc généralement demander à l’utilisateur de confirmer
avant de supprimer des fichiers. Pour supprimer un fichier, transmettez le nom
du fichier à la fonction DeleteFile :
DeleteFile(FileName);
DeleteFile renvoie true si le fichier a été supprimé, et false s’il ne l’a pas été (par
exemple, si le fichier n’existait pas ou s’il était en lecture seule). DeleteFile
supprime sur disque le fichier nommé NomFichier.
Recherche d’un fichier
Les trois routines suivantes permettent de chercher un fichier : FindFirst, FindNext
et FindClose. FindFirst recherche la première instance d’un nom de fichier ayant
un ensemble spécifié d’attributs dans un répertoire spécifié. FindNext renvoie
l’entrée suivante correspondant au nom et aux attributs spécifiés dans un appel
précédent de FindFirst. FindClose libère la mémoire allouée par FindFirst. Vous
devez toujours utiliser FindClose pour clore une séquence FindFirst/FindNext. Si
vous voulez simplement savoir si un fichier existe, une fonction FileExists renvoie
true si le fichier existe, false sinon.
Les trois routines de recherche de fichier attendent dans leurs paramètres un
TSearchRec. TSearchRec définit les informations du fichier recherché par FindFirst
ou FindNext. TSearchRec a la déclaration suivante :
struct TSearchRec
{
int Time; // marqueur date/heure du fichier
int Size; // taille du fichier en octets
int Attr; // indicateurs d’attributs du fichier
AnsiString Name; // nom et extension du fichier
int ExcludeAttr; // indicateurs d’attributs de fichier pour les fichiers à ignorer
unsigned FindHandle;
_WIN32_FIND_DATAA FindData; // structure avec informations supplémentaires
} ;
Si un fichier est trouvé, les champs du paramètre de type TSearchRec sont
modifiés pour décrire le fichier trouvé. Vous pouvez comparer Attr aux
4-8
Guide du développeur
Utilisation des fichiers
constantes ou valeurs d’attributs suivantes pour déterminer si un fichier possède
un attribut donné :
Tableau 4.4 Constantes et valeurs d’attributs
Constante
Valeur
Description
faReadOnly
0x00000001
Fichiers en lecture seule
faHidden
0x00000002
Fichiers cachés
faSysFile
0x00000004
Fichiers système
faVolumeID
0x00000008
Fichiers d’identification de volume
faDirectory
0x00000010
Fichiers répertoire
faArchive
0x00000020
Fichiers archive
faAnyFile
0x0000003F
Tous les fichiers
Pour tester un attribut, combinez la valeur du champ Attr et la constante
d’attribut en utilisant l’opérateur &. Si le fichier possède cet attribut, le résultat
sera supérieur à 0. Par exemple, si le fichier trouvé est caché, l’évaluation de
l’expression suivante donnera true : (SearchRec.Attr & faHidden > 0). Il est possible
de combiner les attributs en associant avec l’opérateur OR leurs constantes ou
valeurs. Par exemple, pour rechercher les fichiers en lecture seule et les fichiers
cachés en plus des fichiers normaux, transmettez (faReadOnly | faHidden) comme
paramètre Attr.
Exemple :
Cet exemple utilise une fiche contenant un libellé, un bouton nommé Search et
un bouton nommé Again. Quand l’utilisateur clique sur le bouton Search, le
premier fichier du répertoire spécifié est trouvé et son nom et sa taille en octets
sont affichés dans l’intitulé du libellé. A chaque fois que l’utilisateur clique sur
le bouton Again, le nom et la taille du fichier correspondant suivant sont affichés
dans le libellé :
TSearchRec SearchRec; // variable globale
void __fastcall TForm1::SearchClick(TObject *Sender)
{
FindFirst("c:\\Program Files\\bcb6\\bin\\*.*", faAnyFile, SearchRec);
Label1->Caption = SearchRec->Name + " occupe " + IntToStr(SearchRec.Size) + " octets";
}
void __fastcall TForm1::AgainClick(TObject *Sender)
{
if (FindNext(SearchRec) == 0)
Label1->Caption = SearchRec->Name + " occupe " + IntToStr(SearchRec.Size) + " octets";
else
FindClose(SearchRec);
}
Remarque
Dans les applications multi-plates-formes, vous devez remplacer les chemins
d’accès codés en dur par le chemin d’accès correct du système, ou utiliser des
variables d’environnement (page Variables d’environnement après avoir choisi
Outils|Options d’environnement) pour les représenter.
Utilisation de BaseCLX
4-9
Utilisation des fichiers
Modification d’un nom de fichier
Pour changer le nom d’un fichier, utilisez la fonction RenameFile :
extern PACKAGE bool __fastcall RenameFile(const AnsiString OldName, const AnsiString
NewName);
RenameFile remplace un nom de fichier, identifié par OldFileName, par le nom
spécifié par NewFileName. Si l’opération réussit, RenameFile renvoie true. Si le
fichier ne peut pas être renommé (par exemple, si un fichier appelé NewFileName
existe déjà), RenameFile renvoie false. Par exemple :
if (!RenameFile("OLDNAME.TXT","NEWNAME.TXT"))
ErrorMsg("Erreur en renommant le fichier!");
Il n’est pas possible de renommer (déplacer) un fichier entre plusieurs lecteurs
en utilisant RenameFile. Pour ce faire, vous devez commencer par copier le
fichier, puis supprimer le fichier original.
Remarque
RenameFile, dans la bibliothèque d’exécution BaseCLX, est une enveloppe pour la
fonction MoveFile de l’API Windows, et MoveFile ne fonctionne donc pas non
plus d’un lecteur à l’autre.
Routines date-heure de fichier
Les routines FileAge, FileGetDate et FileSetDate agissent sur les valeurs date-heure
du système d’exploitation. FileAge renvoie le marqueur date-heure d’un fichier,
ou -1 si le fichier n’existe pas. FileSetDate définit le marqueur date-heure du
fichier spécifié et renvoie zéro en cas de réussite ou un code d’erreur en cas
d’échec. FileGetDate renvoie un marqueur date-heure pour le fichier spécifié ou -1
si le handle est invalide.
Comme la plupart des routines de manipulation de fichier, FileAge utilise un
nom de fichier sous forme de chaîne. Par contre, FileGetDate et FileSetDate
utilisent un paramètre entier qui accepte un handle de fichier. Pour obtenir le
handle de fichier, vous pouvez
• Utiliser la fonction FileOpen ou la fonction FileCreate pour créer un nouveau
fichier ou ouvrir un fichier existant. FileOpen et FileCreate renvoient toutes
deux le handle du fichier.
• Instancier TFileStream pour créer ou ouvrir un fichier. Utilisez ensuite sa
propriété Handle. Voir “Utilisation de flux de fichier” à la page 4-6 pour plus
d’informations.
Copie d’un fichier
La bibliothèque d’exécution BaseCLX ne fournit pas de routines pour copier un
fichier. Mais si vous écrivez des applications uniquement pour Windows, vous
pouvez appeler directement la fonction CopyFile de l’API Windows pour copier
un fichier. Comme la plupart des routines de la bibliothèque d’exécution,
CopyFile accepte comme paramètre un nom de fichier et non un handle de
fichier. Faites attention lorsque vous copiez un fichier, car les attributs du fichier
existant sont copiés dans le nouveau fichier, alors que les attributs de sécurité ne
sont pas copiés. CopyFile est également utile pour déplacer des fichiers d’un
4-10
Guide du développeur
Utilisation des fichiers ini et du registre
lecteur à un autre, car ni la fonction RenameFile, ni la fonction MoveFile de l’API
Windows ne peuvent renommer ou déplacer des fichiers entre lecteurs. Pour
plus d’informations, voir l’aide en ligne de Windows.
Utilisation des fichiers ini et du registre
De nombreuses applications utilisent des fichiers ini pour stocker des
informations de configuration. BaseCLX inclut deux classes pour travailler avec
des fichiers ini : TIniFile et TMemIniFile. L’utilisation des fichiers ini présente
l’avantage qu’ils peuvent être utilisés dans des applications multi-plates-formes
et qu’ils sont faciles à lire et à modifier. Pour plus d’informations sur ces classes,
voir “Utilisation de TIniFile et TMemIniFile” à la page 4-11.
De nombreuses applications Windows remplacent l’utilisation des fichiers ini par
la base des registres. La base des registres Windows est une base de données
hiérarchique qui agit comme un espace de stockage centralisé pour les
informations de configuration. La VCL inclut des classes pour travailler avec la
base de registres. Alors qu’elles ne font techniquement pas partie de BaseCLX
(car elles sont uniquement disponibles sous Windows), deux de ces classes,
TRegistryIniFile et TRegistry, sont traitées ici à cause de leur similitude avec les
classes pour la manipulation des fichiers ini.
TRegistryIniFile est utile pour les applications multi-plates-formes, car elle partage
un ancêtre commun (TCustomIniFile) avec les classes fonctionnant avec les
fichiers ini. Si vous vous limitez aux méthodes de l’ancêtre commun
(TCustomIniFile), votre application peut travailler sur les deux applications avec
un minimum de code conditionnel. TRegistryIniFile est traitée dans “Utilisation
de TRegistryIniFile” à la page 4-13.
Pour les applications qui ne sont pas multi-plates-formes, vous pouvez utiliser la
classe TRegistry. Les propriétés et méthodes de TRegistry possèdent des noms qui
correspondent plus directement à la manière dont la base des registres est
organisée, car elles n’ont pas à être compatibles avec les classes pour les fichiers
ini. TRegistry est traitée dans “Utilisation de TRegistry” à la page 4-13.
Utilisation de TIniFile et TMemIniFile
Le format de fichier ini est toujours utilisé, et de nombreux fichiers de
configuration (comme le fichier DSK de configuration du bureau) sont dans ce
format. Ce format est particulièrement utile dans des applications multi-platesformes, où vous ne pouvez pas toujours compter sur une base de registres pour
stocker les informations de configuration. BaseCLX fournit deux classes, TIniFile
et TMemIniFile, pour faciliter grandement la lecture et l’écriture des fichiers ini.
Sous Linux, TMemIniFile et TIniFile sont identiques. Sous Windows, TIniFile
travaille directement avec le fichier ini sur disque, tandis que TMemIniFile stocke
toutes les modifications en mémoire et ne les écrit sur disque que lorsque vous
appelez la méthode UpdateFile.
Quand vous instanciez l’objet TIniFile ou TMemIniFile, il faut transmettre au
constructeur un paramètre spécifiant le nom du fichier ini. Si le fichier n’existe
Utilisation de BaseCLX
4-11
Utilisation des fichiers ini et du registre
pas déjà, il est créé automatiquement. Vous êtes alors libre de lire les valeurs en
utilisant différentes méthodes de lecture, comme ReadString, ReadDate, ReadInteger
ou ReadBool. Par ailleurs, si vous souhaitez lire une section entière du fichier ini,
vous pouvez utiliser la méthode ReadSection. De même, vous pouvez écrire des
valeurs en utilisant WriteBool, WriteInteger, WriteDate ou WriteString.
L’exemple suivant lit des informations de configuration d’un fichier ini dans le
constructeur d’une fiche et écrit des valeurs dans le gestionnaire d’événement
OnClose.
__fastcall TForm1::TForm1(TComponent *Owner) : TForm(Owner)
{
TIniFile *ini;
ini = new TIniFile( ChangeFileExt( Application->ExeName, ".INI" ) );
Top
= ini->ReadInteger( "Form", "Top", 100 );
Left
= ini->ReadInteger( "Form", "Left", 100 );
Caption = ini->ReadString( "Form", "Caption",
"Default Caption" );
ini->ReadBool( "Form", "InitMax", false ) ?
WindowState = wsMaximized :
WindowState = wsNormal;
delete ini;
}
void __fastcall TForm1::FormClose(TObject *Sender, TCloseAction &Action)
{
TIniFile *ini;
ini = new TIniFile(ChangeFileExt( Application->ExeName, ".INI" ) );
ini->WriteInteger( "Form", "Top", Top );
ini->WriteInteger( "Form", "Left", Left );
ini->WriteString ( "Form", "Caption", Caption );
ini->WriteBool ( "Form", "InitMax",
WindowState == wsMaximized );
delete ini;
}
Chacune des routines Read attend trois paramètres. Le premier identifie la
section du fichier ini. Le deuxième paramètre identifie la valeur à lire et le
troisième est une valeur par défaut à utiliser si la section ou la valeur n’existe
pas dans le fichier ini. De même que les méthodes Read gèrent de manière
élégante la non existence d’une section ou d’une valeur, les routines Write
crééent la section et/ou la valeur si elle n’existe pas. L’exemple précédent crée
un fichier ini la première fois qu’il est exécuté, et ce fichier est de la forme :
[Form]
Top=185
Left=280
Caption=Default Caption
InitMax=0
Lors des exécutions ultérieures de cette application, les valeurs ini sont lues lors
de la création de la fiche et réécrites dans l’événement OnClose.
4-12
Guide du développeur
Utilisation des fichiers ini et du registre
Utilisation de TRegistryIniFile
De nombreuses applications Windows 32 bits stockent leurs informations dans la
base de registres plutôt que dans des fichiers ini, car cette base est hiérarchique
et ne souffre pas des limitations de taille inhérentes aux fichiers ini. Si vous êtes
habitué à utiliser des fichiers ini et souhaitez transférer vos informations de
configuration dans la base de registres, vous pouvez utiliser la classe
TRegistryIniFile. Vous pouvez également utiliser TRegistryIniFile dans des
applications multi-plates-formes si vous voulez utiliser la base de registres sous
Windows et un fichier ini sous Linux. Vous pouvez écrire la plus grande partie
de votre application de façon à ce qu’elle utilise le type TCustomIniFile. Vous
devez uniquement introduire des conditions dans le code qui crée une instance
de TRegistryIniFile (sous Windows) ou TMemIniFile (sous Linux) et l’affecter à
l’objet TCustomIniFile utilisé par votre application.
TRegistryIniFile crée une similitude entre les entrées de la base de registres et
celles du fichier ini. Toutes les méthodes de TIniFile et TMemIniFile (lecture et
écriture) existent dans TRegistryIniFile.
Quand vous créez un objet TRegistryIniFile, le paramètre que vous transmettez au
constructeur (correspondant au nom du fichier pour un objet IniFile ou
TMemIniFile) devient une valeur de clé sous la clé de l’utilisateur dans la base de
registres. Toutes les sections et valeurs commencent à partir de la clé ainsi
transmise. TRegistryIniFile simplifie considérablement l’interfaçage avec la base de
registres, de sorte que vous pourriez vouloir l’utiliser à la place du composant
TRegistry, même si vous ne migrez pas un code existant ou si vous n’écrivez pas
une application multi-plates-formes.
Utilisation de TRegistry
Si vous écrivez une application uniquement sous Windows et si vous connaissez
bien la structure de la base de registres, vous pouvez utiliser TRegistry. A la
différence de TRegistryIniFile, qui utilise les mêmes propriétés et méthodes des
autres composants du fichier ini, les propriétés et les méthodes de TRegistry
correspondent plus directement à la structure de la base de registres. Par
exemple, TRegistry vous permet de spécifier la clé racine et la sous-clé, alors que
TRegistry adopte HKEY_CURRENT_USER comme clé racine. En plus des
méthodes pour l’ouverture, la fermeture, l’enregistrement, le déplacement et la
suppression des clés, TRegistry vous permet de spécifier le niveau d’accès que
vous voulez utiliser.
Remarque
TRegistry ne peut pas être utilisé en programmation multiplates-formes.
L’exemple suivant lit une valeur dans une entrée de registre :
#include <Registry.hpp>
AnsiString GetRegistryValue(AnsiString KeyName)
{
AnsiString S;
TRegistry *Registry = new TRegistry(KEY_READ);
try
{
Registry->RootKey = HKEY_LOCAL_MACHINE;
Utilisation de BaseCLX
4-13
Utilisation des listes
// False car nous ne voulons pas la créer si elle n’existe pas
Registry->OpenKey(KeyName,false);
S = Registry->ReadString("VALUE1");
}
__finally
{
delete Registry;
}
return S;
}
Utilisation des listes
BaseCLX inclut de nombreuses classes représentant des listes ou des collections
d’éléments. Ces classes varient en fonction des types d’éléments qu’elles
contiennent, des opérations qu’elles supportent et de leur persistance.
Le tableau suivant énumère les différentes classes de listes et indique les types
d’éléments qu’elles contiennent :
Tableau 4.5 Classes pour la gestion de listes
Objet
Gère
TList
Une liste de pointeurs
TThreadList
Une liste de pointeurs adaptée aux threads
TBucketList
Une liste de pointeurs à accès direct
TObjectBucketList
Une liste d’instances d’objet à accès direct
TObjectList
Une liste d’instances d’objets, gérée en mémoire
TComponentList
Une liste de composants gérée en mémoire (c’est-à-dire d’instances de
classes dérivées de TComponent)
TClassList
Une liste de références de classes (métaclasses)
TInterfaceList
Une liste de pointeurs d’interfaces.
TQueue
Une liste de pointeurs premier entré, premier sorti
TStack
Une liste de pointeurs dernier entré, premier sorti
TObjectQueue
Une liste d’objets premier entré, premier sorti
TObjectStack
Une liste d’objets dernier entré, premier sorti
TCollection
Classe de base pour de nombreuses classes spécialisées d’éléments typés.
TStringList
Une liste de chaînes
THashedStringList
Une liste de chaînes de la forme Nom=Valeur, à accès direct pour les
performances.
Opérations de listes courantes
Bien que les différentes classes de listes contiennent différents types d’éléments
et possèdent différents ancêtres, la plupart d’entre elles partagent un ensemble
commun de méthodes pour l’ajout, la suppression, la réorganisation et l’accès
aux éléments de la liste.
4-14
Guide du développeur
Utilisation des listes
Ajout d’éléments de liste
La plupart des classes de listes possèdent une méthode Add qui vous permet
d’ajouter un élément à la fin de la liste (si la liste n’est pas triée) ou à sa position
appropriée (si la liste est triée). Généralement, la méthode Add prend comme
paramètre l’élément que vous ajoutez à la liste et renvoie la position dans la liste
où l’élément a été ajouté. Dans le cas de listes de compartiments (TBucketList et
TObjectBucketList), Add ne prend pas uniquement l’élément à ajouter, mais
également une donnée que vous pouvez lui associer. Dans le cas des collections,
Add ne prend aucun paramètre, mais crée un nouvel élément qu’il ajoute. La
méthode Add sur les collections renvoie l’élément qui a été ajouté, pour que vous
puissiez affecter des valeurs aux propriétés de ce nouvel élément.
Certaines classes de listes possèdent une méthode Insert en plus de la méthode
Add. Insert fonctionne de la même façon que la méthode Add, mais elle possède
un paramètre supplémentaire qui vous permet de spécifier la position dans la
liste où vous souhaitez voir le nouvel élément. Si une classe possède une
méthode Add, elle possède également une méthode Insert si la position des
éléments n’est pas prédéterminée. Par exemple, vous ne pouvez pas utiliser Insert
avec les listes triées, car les éléments doivent être dans l’ordre de tri, et vous ne
pouvez pas utiliser Insert avec les listes de compartiments, car l’algorithme de
hachage détermine la position de l’élément.
Les seules classes ne possédant pas de méthode Add sont les listes ordonnées.
Les listes ordonnées sont constituées de files et de piles. Pour ajouter des
éléments à une liste ordonnée, utilisez plutôt la méthode Push. Push, comme Add,
prend un élément comme paramètre et l’insère à la position correcte.
Suppression d’éléments de liste
Pour supprimer un seul élément d’une des classes de listes, utilisez soit la
méthode Delete soit la méthode Remove. Delete accepte un seul paramètre, l’indice
de l’élément à supprimer. Remove accepte également un seul paramètre, mais ce
paramètre est une référence de l’élément à supprimer, plutôt que son indice.
Certaines classes de listes prennent uniquement en charge une méthode Delete,
d’autres prennent uniquement en charge une méthode Remove et certaines
prennent en charge les deux.
Comme pour l’ajout d’éléments, les listes ordonnées se comportent différemment
de toutes les autres listes. Au lieu d’utiliser une méthode Delete ou Remove, vous
supprimez un élément d’une liste ordonnée en appelant sa méthode Pop. Pop
n’accepte aucun argument, car il n’existe qu’un seul élément qui puisse être
supprimé.
Si vous souhaitez supprimer tous les éléments de la liste, vous pouvez appeler la
méthode Clear. Clear est disponible pour toutes les listes à l’exception des listes
ordonnées.
Accès aux éléments de la liste
Toutes les classes de listes (à l’exception de TThreadList et des listes ordonnées)
possèdent une propriété qui vous permet d’accéder aux éléments de la liste.
Cette propriété s’appelle généralement Items. Pour les listes de chaînes, la
Utilisation de BaseCLX
4-15
Utilisation des listes
propriété s’appelle Strings, et pour les listes de compartiments, elle s’appelle
Data. La propriété Items, Strings ou Data est une propriété indexée, avec laquelle
vous pouvez spécifier l’élément auquel vous voulez accéder.
Avec TThreadList, vous devez verrouiller la liste avant de pouvoir accéder aux
éléments. Lorsque vous verrouillez la liste, la méthode LockList renvoie un objet
TList que vous pouvez utiliser pour accéder aux éléments.
Les listes ordonnées vous permettent d’accéder à l’élément “supérieur” de
la liste. Vous pouvez obtenir une référence sur cet élément en appelant la
méthode Peek.
Réorganisation d’éléments de liste
Certaines classes de listes possèdent des méthodes qui vous permettent de
réorganiser les éléments dans la liste. Certaines possèdent une méthode Exchange,
qui échange les positions de deux éléments. Certaines possèdent une méthode
Move qui vous permet de déplacer un élément vers un emplacement spécifié.
Certaines possèdent une méthode Sort qui vous permet de trier les éléments
dans la liste.
Pour connaître les méthodes disponibles, consultez l’aide en ligne pour la classe
de listes que vous utilisez.
Listes persistantes
Les listes persistantes peuvent être enregistrées dans un fichier de fiche. C’est la
raison pour laquelle elles sont souvent utilisées comme type d’une propriété
publiée sur un composant. Vous pouvez ajouter des éléments à la liste lors de la
conception, et ces éléments sont enregistrés avec l’objet afin d’être présents
lorsque le composant qui les utilise est chargé en mémoire à l’exécution. Il existe
deux types principaux de listes persistantes : les listes de chaînes et les
collections.
Parmi les listes de chaînes figurent TStringList et THashedStringList. Comme leur
nom l’indique, les listes de chaînes contiennent des chaînes. Elles fournissent une
prise en charge particulière des chaînes de la forme Nom=Valeur, pour vous
permettre de rechercher la valeur associée à un nom. De plus, la plupart des
listes de chaînes vous permettent d’associer un objet à chaque chaîne de la liste.
Les listes de chaînes sont décrites plus en détail dans “Utilisation des listes de
chaînes” à la page 4-17.
Les collections dérivent de la classe TCollection. Chaque descendant de TCollection
est spécialement conçu pour gérer une classe particulière d’éléments, où cette
classe dérive de TCollectionItem. Les collections prennent en charge de
nombreuses opérations de listes courantes. Toutes les collections sont conçues
pour être le type d’une propriété publiée, et un grand nombre ne peuvent pas
fonctionner indépendamment de l’objet qui les utilise pour une implémentation
sur la base de ses propriétés. Lors de la conception, la propriété dont la valeur
est une collection peut utiliser l’éditeur de collection pour vous permettre
4-16
Guide du développeur
Utilisation des listes de chaînes
d’ajouter, de supprimer et de réorganiser les éléments. L’éditeur de collection
fournit une interface utilisateur commune pour la manipulation des collections.
Utilisation des listes de chaînes
Un des types de listes les plus couramment utilisés est une liste de chaînes de
caractères. Par exemple, pour les éléments d’une boîte à options, les lignes d’un
mémo, les noms de fonte ou les noms des lignes et colonnes d’une grille de
chaînes. BaseCLX fournit une interface courante pour toute liste de caractères par
l’intermédiaire d’un objet appelé TStrings et ses descendants comme TStringList
et THashedStringList. TStringList implémente les propriétés et méthodes abstraites
introduites par TStrings, et introduit les propriétés, événements et méthodes pour
• Trier les chaînes de la liste,
• Interdire les chaînes en double dans les listes triées,
• Répondre aux changements du contenu de la liste.
Outre les fonctionnalités concernant la gestion de listes de chaînes, ces objets
permettent une interopérabilité simple ; vous pouvez ainsi modifier les lignes
d’un mémo (descendant de TStrings), puis utiliser ces lignes comme éléments
d’une boîte à options (également descendant de TStrings).
Une propriété liste de chaînes apparaît dans l’inspecteur d’objets avec TStrings
dans la colonne des valeurs. Double-cliquez sur TStrings pour ouvrir l’éditeur de
liste de chaînes qui vous permet de modifier, d’ajouter ou de supprimer des
chaînes.
Vous pouvez également manipuler les objets listes de chaînes lors de l’exécution
pour effectuer les opérations suivantes :
•
•
•
•
Lecture et enregistrement des listes de chaînes
Création d’une nouvelle liste de chaînes
Manipulation des chaînes d’une liste
Association d’objets à une liste de chaînes
Lecture et enregistrement des listes de chaînes
Les objets listes de chaînes disposent des méthodes SaveToFile et LoadFromFile qui
permettent de stocker une liste de chaînes dans un fichier texte ou de charger un
fichier texte dans une liste de chaînes. Chaque ligne du fichier texte correspond à
une chaîne de la liste. En utilisant ces méthodes, vous pouvez, par exemple,
créer un éditeur de texte simple en chargeant un fichier dans un composant
mémo ou enregistrer les listes d’éléments de boîtes à options.
L’exemple suivant charge une copie du fichier WIN.INI dans un champ mémo et
en effectue une copie de sauvegarde nommée WIN.BAK.
void __fastcall EditWinIni()
{
AnsiString FileName = "C:\\WINDOWS\\WIN.INI"; // définit le nom du fichier
Utilisation de BaseCLX
4-17
Utilisation des listes de chaînes
Form1->Memo1->Lines->LoadFromFile(FileName); // lit depuis le fichier
Form1->Memo1->Lines->SaveToFile(ChangeFileExt(FileName, ".BAK")); // enregistre
// la sauvegarde
}
Création d’une nouvelle liste de chaînes
Habituellement, les listes de chaînes font partie intégrante de composants.
Néanmoins, il est parfois commode de créer des listes de chaînes autonomes qui
n’ont pas de composant associé (par exemple, pour stocker les chaînes d’une
table de référence). La manière de créer et de gérer une liste de chaînes varie
selon que la liste est une liste à court terme (construite, utilisée et détruite dans
une même routine) ou une liste à long terme (disponible jusqu’à l’arrêt de
l’application). Quel que soit le type de liste de chaînes créé, n’oubliez pas que
c’est à vous de libérer la liste quand vous n’en n’avez plus besoin.
Listes de chaînes à court terme
Si vous utilisez une liste de chaînes uniquement pour la durée d’une seule
routine, vous pouvez la créer, l’utiliser et la détruire au même emplacement.
C’est la méthode la plus fiable pour utiliser des objets listes de chaînes. Comme
l’objet liste de chaînes alloue la mémoire pour lui-même et pour ses chaînes, il
est important de protéger l’allocation en utilisant un bloc try...__finally afin de
garantir que l’objet libère sa mémoire même si une exception a lieu.
1 Construire l’objet liste de chaînes.
2 Utiliser la liste de chaînes dans la partie try d’un bloc try...__finally.
3 Libérer l’objet liste de chaînes dans la partie __finally.
Le gestionnaire d’événement suivant répond au choix d’un bouton en
construisant un objet liste de chaînes, en l’utilisant puis en le détruisant :
void __fastcall TForm1::ButtonClick1(TObject *Sender)
{
TStringList *TempList = new TStringList; // déclare la liste
try{
// utilise la liste de chaînes
}
__finally{
delete TempList; // détruit l’objet liste
}
}
Listes de chaînes à long terme
Si la liste de chaînes doit être disponible tout au long de l’exécution de votre
application, vous devez construire la liste au démarrage de l’application et la
détruire avant la fermeture de l’application.
1 Dans le fichier unité de la fiche principale de votre application, ajoutez un
champ de type TStrings à la déclaration de la fiche.
4-18
Guide du développeur
Utilisation des listes de chaînes
2 Ecrivez un constructeur de la fiche principale qui s’exécute avant que la fiche
n’apparaisse. Ce gestionnaire d’événement doit créer une liste de chaînes et
l’affecter au champ déclaré dans la première étape.
3 Ecrivez un gestionnaire d’événement qui libère la liste de chaînes dans
l’événement OnClose de la fiche.
L’exemple suivant utilise une liste de chaînes à long terme pour stocker les clics
de la souris dans la fiche principale, puis enregistre la liste dans un fichier avant
l’arrêt de l’application.
//--------------------------------------------------------------------------#include <vcl.h>
#pragma hdrstop
#include "Unit1.h"
//--------------------------------------------------------------------------#pragma package(smart_init)
#pragma resource "*.dfm"
TForm1 *Form1;
//--------------------------------------------------------------------------__fastcall TForm1::TForm1(TComponent* Owner)
: TForm(Owner)
{
ClickList = new TStringList;
}
//--------------------------------------------------------------------------void __fastcall TForm1::FormClose(TObject *Sender, TCloseAction &Action)
{
ClickList->SaveToFile(ChangeFileExt(Application->ExeName, ".LOG"));//enregistre la liste
delete ClickList;
}
//--------------------------------------------------------------------------void __fastcall TForm1::FormMouseDown(TObject *Sender, TMouseButton Button,
TShiftState Shift, int X, int Y)
{
TVarRec v[] = {X,Y};
ClickList->Add(Format("Clic à (%d, %d)",v,ARRAYSIZE(v) - 1));// ajoute une chaîne
// à la liste
}
}
Manipulation des chaînes d’une liste
Les opérations couramment effectuées sur les listes de chaînes sont les suivantes :
•
•
•
•
•
•
•
•
Comptage des chaînes d’une liste
Accès à une chaîne spécifique
Recherche de la position d’une chaîne dans la liste
Parcours des chaînes d’une liste
Ajout d’une chaîne à une liste
Déplacement d’une chaîne dans une liste
Suppression d’une chaîne d’une liste
Copie de la totalité d’une liste de chaînes
Utilisation de BaseCLX
4-19
Utilisation des listes de chaînes
Comptage des chaînes d’une liste
La propriété en lecture seule Count renvoie le nombre de chaînes dans la liste.
Comme les listes de chaînes utilisent des indices de base zéro, Count correspond
à l’indice de la dernière chaîne plus un.
Accès à une chaîne spécifique
La propriété tableau Strings contient les chaînes de la liste, référencées par un
indice de base zéro.
StringList1->Strings[0] = “Première chaîne.”;
Recherche d’éléments dans une liste de chaînes
Pour rechercher une chaîne dans une liste de chaînes, utilisez la méthode
IndexOf. IndexOf renvoie l’indice de la première chaîne de la liste qui correspond
au paramètre transmis, et renvoie –1 si la chaîne transmise en paramètre n’est
pas trouvée. IndexOf recherche uniquement une correspondance exacte ; si vous
voulez obtenir des chaînes de correspondance partielle, vous devez parcourir la
liste de chaînes.
Vous pouvez, par exemple, utiliser IndexOf pour déterminer si un nom de fichier
donné se trouve dans les éléments Items d’une boîte liste :
if (FileListBox1->Items->IndexOf("WIN.INI") > -1) ...
Parcours des chaînes d’une liste
Pour parcourir les chaînes d’une liste, utilisez une boucle for allant de zéro à
Count – 1.
L’exemple suivant convertit en majuscules chaque chaîne d’une boîte liste.
void __fastcall TForm1::Button1Click(TObject *Sender)
{
for (int i = 0; i < ListBox1->Items->Count; i++)
ListBox1->Items->Strings[i] = UpperCase(ListBox1->Items->Strings[i]);
}
Ajout d’une chaîne à une liste
Pour ajouter une chaîne à la fin d’une liste de chaînes, utilisez la méthode Add
en lui transmettant en paramètre la nouvelle chaîne. Pour insérer une chaîne
dans la liste, appelez la méthode Insert en lui transmettant deux paramètres : la
chaîne et l’indice à laquelle elle doit être placée. Si, par exemple, vous voulez
placer la chaîne “Trois” en troisième position dans une liste, utilisez :
StringList1->Insert(2, "Trois");
Pour ajouter à une liste les chaînes d’une autre liste, appelez AddStrings :
StringList1->AddStrings(StringList2); // ajoute à StringList1 les chaînes de StringList2
4-20
Guide du développeur
Utilisation des listes de chaînes
Déplacement d’une chaîne dans une liste
Pour déplacer une chaîne dans une liste de chaînes, appelez la méthode Move en
lui transmettant deux paramètres : l’indice en cours de la chaîne et son nouvel
indice. Par exemple, pour déplacer la troisième chaîne de la liste en cinquième
position, utilisez :
StringListObject->Move(2, 4);
Suppression d’une chaîne d’une liste
Pour supprimer une chaîne d’une liste de chaînes, appelez la méthode Delete de
la liste en lui transmettant l’indice de la chaîne à supprimer. Si vous ne
connaissez pas l’indice de la chaîne à supprimer, utilisez la méthode IndexOf
pour le déterminer. Pour supprimer toutes les chaînes de la liste, utilisez la
méthode Clear.
L’exemple suivant utilise IndexOf et Delete pour trouver et supprimer une
chaîne :
int BIndex = ListBox1->Items->IndexOf("bureaucratie");
if (BIndex > -1)
ListBox1->Items->Delete(BIndex);
Copie de la totalité d’une liste de chaînes
Vous pouvez utiliser la méthode Assign pour copier les chaînes d’une liste source
vers une liste de destination en remplaçant le contenu de la liste de destination.
Pour ajouter les chaînes sans remplacer la liste de destination, utilisez la
méthode AddStrings. Par exemple,
Memo1->Lines->Assign(ComboBox1->Item)s; // remplace les chaînes existantes
copie les lignes d’une boîte à options dans un mémo (en écrasant le contenu du
mémo), alors que :
Memo1->Lines->AddStrings(ComboBox1->Items);// ajoute les chaînes à la fin
ajoute au mémo les lignes de la boîte à options.
Quand vous effectuez une copie locale d’une liste de chaînes, utilisez la méthode
Assign. Si vous affectez une variable liste de chaînes à une autre :
StringList1 = StringList2;
l’objet liste de chaînes initial est perdu, ce qui peut donner des résultats
imprévisibles.
Association d’objets à une liste de chaînes
Outre les chaînes stockées dans sa propriété Strings, une liste de chaînes peut
gérer des références à des objets dans sa propriété Objects. Comme Strings, Objects
est un tableau d’indice zéro. Le plus souvent, Objects sert à associer des bitmaps
à des chaînes dans des contrôles dessinés par le propriétaire.
Utilisez la méthode AddObject ou InsertObject pour ajouter en une seule étape
une chaîne et un objet associé à la liste. IndexOfObject renvoie l’indice de la
première chaîne de la liste associée à l’objet spécifié. Les méthodes comme Delete,
Utilisation de BaseCLX
4-21
Utilisation des chaînes
Clear et Move agissent à la fois sur les chaînes et les objets ; ainsi, la suppression
d’une chaîne supprime également l’éventuel objet correspondant.
Pour associer un objet à une chaîne existante, affectez l’objet à la propriété
Objects pour le même indice. Vous ne pouvez pas ajouter d’objet sans ajouter
une chaîne correspondante.
Utilisation des chaînes
La bibliothèque d’exécution BaseCLX propose de nombreuses routines de
manipulation des chaînes spécialisées pour les différents types chaînes. Il existe
des routines pour les chaînes étendues, AnsiStrings et les chaînes à zéro terminal
(char *). Les routines gérant les chaînes à zéro terminal utilisent le zéro terminal
pour déterminer la longueur de la chaîne. Les rubriques suivantes fournissent
une présentation de nombreuses routines de manipulation de chaînes dans la
bibliothèque d’exécution.
Routines manipulant les caractères étendus
Les chaînes étendues sont utilisées dans diverses situations. Certaines
technologies, comme XML, utilisent des chaînes étendues comme type natif.
Vous pouvez également choisir d’utiliser des chaînes étendues car elles
simplifient certains problèmes de gestion de chaînes dans les applications avec
plusieurs paramètres régionaux cibles. L’utilisation d’un schéma de codage avec
des caractères étendus présente l’avantage que vous pouvez réaliser sur les
chaînes des hypothèses qui ne sont pas valables avec les systèmes MBCS. Il
existe en effet une relation directe entre le nombre d’octets de la chaîne et son
nombre de caractères. Il n’y a pas le risque, comme avec les jeux de caractères
MBCS, de couper un caractère en deux ou de confondre le deuxième octet d’un
caractère avec le début d’un autre caractère.
Un inconvénient de l’utilisation des caractères étendus réside dans le fait que la
plupart des contrôles VCL représentent les valeurs de type chaîne sous la forme
de chaînes mono-octets ou MBCS. (Les contrôles CLX utilisent généralement les
chaînes étendues.) La conversion entre le système de caractères étendus et le
système MBCS à chaque définition d’une propriété de chaîne ou d’une lecture de
sa valeur peut nécessiter d’énormes quantités de code supplémentaire et ralentir
votre application. Mais vous pouvez choisir la conversion en caractères étendus
pour certains algorithmes de traitement des chaînes qui tirent profit de la
correspondance 1:1 entre caractères et WideChar.
Les fonctions suivantes effectuent des conversions entre les chaînes de caractères
mono-octet standards (ou les chaînes MBCS) et les chaînes Unicode :
•
•
•
•
•
4-22
StringToWideChar
WideCharLenToString
WideCharLenToStrVar
WideCharToString
WideCharToStrVar
Guide du développeur
Utilisation des chaînes
De plus, les fonctions suivantes effectuent des conversions entre WideStrings et
d’autres représentations :
•
•
•
•
UCS4StringToWideString
WideStringToUCS4String
VarToWideStr
VarToWideStrDef
Les routines suivantes fonctionnent directement avec des WideStrings :
•
•
•
•
•
•
•
•
•
WideCompareStr
WideCompareText
WideSameStr
WideSameText
WideSameCaption (uniquement CLX)
WideFmtStr
WideFormat
WideLowerCase
WideUpperCase
Enfin, certaines routines incluent des surcharges pour travailler avec des chaînes
étendues :
•
•
•
•
•
UniqueString
Length
Trim
TrimLeft
TrimRight
Routines couramment utilisées pour les AnsiStrings
Les routines de gestion AnsiString couvrent plusieurs domaines fonctionnels.
Dans ces domaines, certaines routines sont utilisées dans le même but mais
varient dans l’utilisation de critères particuliers dans leurs calculs. Les tableaux
suivants énumèrent ces routines en les regroupant selon les domaines
fonctionnels suivants :
•
•
•
•
Comparaison
Conversion majuscules/minuscules
Modification
Sous-chaînes
Lorsqu’il y a lieu, les tableaux indiquent également si la routine satisfait le
critère suivant :
• Différence majuscules/minuscules : si les paramètres de localisation sont
utilisés, ils déterminent la définition des caractères majuscules/minuscules. Si
la routine n’utilise pas les paramètres de localisation, les analyses sont fondées
sur la valeur scalaire des caractères. Si la routine ne tient pas compte des
différences majuscules/minuscules, une fusion logique des caractères
majuscules et minuscules déterminée par un modèle prédéfini est effectuée.
Utilisation de BaseCLX
4-23
Utilisation des chaînes
• Utilisation des paramètres de localisation : cela permet de personnaliser votre
application pour des localisations spécifiques, en particulier dans le cas des
environnements pour les langues asiatiques. Dans la plupart des localisations,
les caractères minuscules sont censés être inférieurs aux caractères majuscules
correspondants. C’est l’opposé de l’ordre ASCII dans lequel les caractères
minuscules sont supérieurs aux caractères majuscules. Les routines qui
utilisent la localisation Windows commencent généralement par Ansi
(AnsiXXX).
• Gestion des jeux de caractères multi-octets (MBCS) : les MBCS sont utilisés
pour écrire du code pour les localisations extrême-orientales. Les caractères
multi-octets sont représentés par un mélange de codes de caractères sur un à
deux octets, le nombre d’octets ne correspond donc pas systématiquement à la
longueur de la chaîne. Les routines qui gèrent les MBCS analysent les
caractères sur un ou deux octets.
ByteType et StrByteType déterminent si un octet donné est l’octet de tête d’un
caractère sur deux octets. Faites attention en manipulant des caractères multioctets à ne pas tronquer une chaîne en coupant en deux un caractère sur deux
octets. Ne transmettez pas de caractères comme paramètre d’une fonction ou
d’une procédure, puisque la taille d’un caractère ne peut pas être déterminée à
l’avance. Il faut, à la place, transmettre un pointeur sur un caractère ou une
chaîne. Pour davantage d’informations sur MBCS, voir “Codage de l’application”
à la page 16-2 dans le chapitre 16, “Création d’applications internationales”.
Tableau 4.6 Routines de comparaison de chaînes
4-24
Routine
Différence MAJ/min
Utilisation
des paramètres
de localisation
AnsiCompareStr
Oui
Oui
Gestion MBCS
Oui
AnsiCompareText
Non
Oui
Oui
AnsiCompareFileName
Non
Oui
Oui
AnsiMatchStr
Oui
Oui
Oui
AnsiMatchText
Non
Oui
Oui
AnsiContainsStr
Oui
Oui
Oui
AnsiContainsText
Non
Oui
Oui
Oui
AnsiStartsStr
Oui
Oui
AnsiStartsText
Non
Oui
Oui
AnsiEndsStr
Oui
Oui
Oui
AnsiEndsText
Non
Oui
Oui
AnsiIndexStr
Oui
Oui
Oui
AnsiIndexText
Non
Oui
Oui
CompareStr
Oui
Non
Non
CompareText
Non
Non
Non
AnsiResemblesText
Non
Non
Non
Guide du développeur
Utilisation des chaînes
Tableau 4.7 Routines de conversion majuscules/minuscules
Remarque
Routine
Utilisation
des paramètres
de localisation
Gestion MBCS
AnsiLowerCase
Oui
Oui
AnsiLowerCaseFileName
Oui
Oui
AnsiUpperCaseFileName
Oui
Oui
AnsiUpperCase
Oui
Oui
LowerCase
Non
Non
UpperCase
Non
Non
Les routines utilisées pour les noms de fichiers sous forme de chaîne,
AnsiCompareFileName, AnsiLowerCaseFileName et AnsiUpperCaseFileName, utilisent
la localisation Windows. Vous devez toujours utiliser des noms de fichiers
portables, car la localisation (le jeu de caractères) utilisée pour les noms de
fichiers peut différer de celle de l’interface utilisateur par défaut.
Tableau 4.8 Routines de modification de chaîne
Routine
Différence MAJ/min
AdjustLineBreaks
ND
Gestion MBCS
Oui
AnsiQuotedStr
ND
Oui
AnsiReplaceStr
Oui
Oui
AnsiReplaceText
Non
Oui
StringReplace
précisé par un indicateur
Oui
ReverseString
ND
Non
StuffString
ND
Non
Trim
ND
Oui
TrimLeft
ND
Oui
TrimRight
ND
Oui
WrapText
ND
Oui
Tableau 4.9 Routines de sous-chaînes
Routine
Différence MAJ/min
AnsiExtractQuotedStr
ND
Gestion MBCS
Oui
AnsiPos
Oui
Oui
IsDelimiter
Oui
Oui
IsPathDelimiter
Oui
Oui
LastDelimiter
Oui
Oui
LeftStr
ND
Non
RightStr
ND
Non
MidStr
ND
Non
QuotedStr
Non
Non
Utilisation de BaseCLX
4-25
Utilisation des chaînes
Routines couramment utilisées pour les chaînes à zéro terminal
Il est possible de regrouper les routines manipulant des chaînes à zéro terminal
en plusieurs catégories fonctionnelles. Dans ces catégories, certaines routines sont
utilisées dans le même but mais varient dans l’utilisation de critères particuliers
dans leurs calculs. Les tableaux suivants présentent ces routines en les
regroupant selon les catégories suivantes :
•
•
•
•
•
Comparaison
Conversion majuscules/minuscules
Modification
Sous-chaînes
Copie
Lorsqu’il y a lieu, les tableaux indiquent également si la routine tient compte de
la casse, si elle utilise la configuration locale et/ou accepte les jeux de caractères
MBCS (Multi-Byte character Sets).
Tableau 4.10 Routines de comparaison de chaînes à zéro terminal
Routine
Différence MAJ/min
Utilisation
des paramètres
de localisation
Gestion MBCS
AnsiStrComp
Oui
Oui
Oui
AnsiStrIComp
Non
Oui
Oui
AnsiStrLComp
Oui
Oui
Oui
AnsiStrLIComp
Non
Oui
Oui
StrComp
Oui
Non
Non
StrIComp
Non
Non
Non
StrLComp
Oui
Non
Non
StrLIComp
Non
Non
Non
Tableau 4.11 Routines de conversion majuscules/minuscules pour les chaînes à zéro terminal
Routine
Utilisation
des paramètres
de localisation
Gestion MBCS
AnsiStrLower
Oui
Oui
AnsiStrUpper
Oui
Oui
StrLower
Non
Non
StrUpper
Non
Non
Tableau 4.12 Routines de modification de chaîne
Routine
StrCat
StrLCat
4-26
Guide du développeur
Impression
Tableau 4.13 Routines de sous-chaînes
Routine
Différence MAJ/min
Gestion MBCS
AnsiStrPos
Oui
Oui
AnsiStrScan
Oui
Oui
AnsiStrRScan
Oui
Oui
StrPos
Oui
Non
StrScan
Oui
Non
StrRScan
Oui
Non
Tableau 4.14 Routines de copie de chaînes
Routine
StrCopy
StrLCopy
StrECopy
StrMove
StrPCopy
StrPLCopy
Impression
Techniquement parlant, la classe TPrinter n’appartient pas à BaseCLX car il
existe deux versions séparées, une pour la VCL (dans l’unité Printers) et une
pour CLX (dans l’unité QPrinters). L’objet VCL TPrinter encapsule les détails de
l’impression sous Windows. L’objet CLX TPrinter est un dispositif de peinture
qui opère sur une imprimante. Il génère du code postscript et l’envoie à lpr, lp
ou à toute autre commande d’impression. Les deux versions de TPrinter sont
toutefois très similaires.
Pour obtenir une liste des imprimantes installées et disponibles, utilisez la
propriété Printers. Les deux objets imprimante utilisent un TCanvas (similaire au
TCanvas de la fiche), ce qui signifie que tout ce qui peut être dessiné sur une
fiche peut également être imprimé. Pour imprimer une image, appelez d’abord la
méthode BeginDoc, puis les routines des dessins de canevas à imprimer (y
compris du texte en utilisant la méthode TextOut), et envoyez la tâche à
l’imprimante en appelant la méthode EndDoc.
Cet exemple utilise un bouton et un mémo sur une fiche. Quand l’utilisateur
clique sur le bouton, le contenu du mémo s’imprime avec une marge de
200 pixels autour de la page.
Pour exécuter cet exemple, vous devez inclure <Printers.hpp> dans votre fichier
d’unité.
void __fastcall TForm1::Button1Click(TObject *Sender)
{
TPrinter *Prntr = Printer();
TRect r = Rect(200,200,Prntr->PageWidth - 200,Prntr->PageHeight- 200);
Utilisation de BaseCLX
4-27
Conversion de mesures
Prntr->BeginDoc();
for( int i = 0; i < Memo1->Lines->Count; i++)
Prntr->Canvas->TextOut(200,200 + (i *
Prntr->Canvas->TextHeight(Memo1->Lines->Strings[i])),
Memo1->Lines->Strings[i]);
Prntr->Canvas->Brush->Color = clBlack;
Prntr->Canvas->FrameRect(r);
Prntr->EndDoc();
}
Pour davantage d’informations sur l’utilisation de l’objet TPrinter, voir dans
l’aide en ligne la rubrique TPrinter.
Conversion de mesures
L’unité ConvUtils déclare une fonction Convert que vous pouvez utiliser pour
convertir des mesures entre plusieurs unités. Vous pouvez effectuer des
conversions entre unités compatibles, par exemple des pouces et des pieds ou
des jours et des semaines. Les unités mesurant les mêmes types d’éléments sont
dites appartenir à la même famille de conversion. Les unités que vous convertissez
doivent appartenir à la même famille de conversion. Pour plus d’informations
sur les conversions, reportez-vous à la section suivante “Exécution des
conversions” et à Convert dans l’aide en ligne.
L’unité StdConvs définit plusieurs familles de conversion et les unités de mesure
de chacune de ces familles. De plus, vous pouvez créer vos propres familles de
conversion et leurs unités associées en utilisant les fonctions
RegisterConversionType et RegisterConversionFamily. Pour savoir comment étendre
les conversions et les unités de conversion, reportez-vous à la section “Ajout de
nouveaux types de mesure” à la page 4-29 et à Convert dans l’aide en ligne.
Exécution des conversions
Vous pouvez utiliser la fonction Convert pour exécuter des conversions simples
ou complexes. Cette fonction emploie une syntaxe simple et une syntaxe moins
simple réservée aux conversions entre types de mesure complexes.
Exécution des conversions simples
Vous pouvez utiliser la fonction Convert pour convertir une mesure d’une unité
vers une autre. La fonction Convert effectue des conversions entre unités
mesurant le même type d’objet (distance, surface, temps, température, etc.).
Pour utiliser Convert, vous devez spécifier l’unité à partir de laquelle la
conversion s’effectue et l’unité de destination. Vous utilisez le type TConvType
pour identifier les unités de mesure.
Par exemple, le code qui suit convertit une température de degrés Fahrenheit en
degrés Kelvin :
TempInKelvin = Convert(StrToFloat(Edit1->Text), tuFahrenheit, tuKelvin);
4-28
Guide du développeur
Conversion de mesures
Exécution des conversions complexes
Vous pouvez aussi utiliser la fonction Convert pour effectuer des conversions
plus complexes entre des rapports de deux types de mesure. C’est le cas lorsque
vous effectuez des conversions de miles par heure en mètres par minute pour
exprimer une vitesse ou de gallons par minute en litres par heure pour exprimer
un débit.
Par exemple, l’appel suivant convertit des miles par gallon en kilomètres
par litre :
double nKPL = Convert(StrToFloat(Edit1.Text), duMiles, vuGallons, duKilometers, vuLiter);
Les unités que vous convertissez doivent appartenir à la même famille de
conversion (elles doivent mesurer la même chose). Si les unités ne sont pas
compatibles, Convert déclenche une exception EConversionError. Vous pouvez
vérifier si deux valeurs TConvType appartiennent à la même famille de
conversion en appelant CompatibleConversionTypes.
L’unité StdConvs définit plusieurs familles de valeurs TConvType. Voir Variables
des familles de conversion dans l’aide en ligne pour avoir la liste des familles
d’unités de mesure prédéfinies et les unités de mesure de chaque famille.
Ajout de nouveaux types de mesure
Si vous voulez effectuer des conversions entre unités de mesure non encore
définies dans l’unité StdConvs, il vous faut créer une nouvelle famille de
conversion pour représenter ces unités de mesure (valeurs TConvType). Quand
deux valeurs TConvType sont recensées dans la même famille de conversion, la
fonction Convert peut effectuer la conversion entre ces mesures en utilisant les
unités représentées par ces valeurs TConvType.
Vous devez d’abord obtenir les valeurs de TConvFamily en recensant une famille
de conversion à l’aide de la fonction RegisterConversionFamily. Une fois que vous
avez obtenu une valeur de TConvFamily (en recensant une nouvelle famille de
conversion ou en utilisant une des variables globales de l’unité StdConvs), vous
pouvez utiliser la fonction RegisterConversionType pour ajouter les nouvelles
unités à la famille de conversion. Les exemples suivants montrent comment
procéder.
Création d’une famille de conversion simple et ajout d’unités
Vous pouvez, par exemple, créer une nouvelle famille de conversion et ajouter
de nouveaux types de mesure lorsque vous effectuez entre de longues périodes
de temps (mois ou siècles) des conversions qui risquent d’être inexactes.
Pour mieux comprendre, la famille cbTime utilise le jour comme unité de base.
L’unité de base est celle qui est utilisée pour effectuer toutes les conversions à
l’intérieur de cette famille. Donc, toutes les conversions doivent être faites en
jours. Une perte de précision peut se produire lors de conversions utilisant
comme unités le mois ou plus (année, décennie, siècle, millénaire), car la
Utilisation de BaseCLX
4-29
Conversion de mesures
conversion entre jours et mois, jours et années, etc. n’est pas exacte. Les mois ont
différentes longueurs, les années ont des facteurs de correction pour les années
bissextiles, les secondes supplémentaires, etc.
Si vous utilisez uniquement des unités de mesure égales ou supérieures au mois,
vous pouvez créer une famille de conversion plus précise en prenant l’année
comme unité de base. Cet exemple crée une nouvelle famille de conversion
nommée cbLongTime.
Déclaration des variables
D’abord, vous devez déclarer des variables pour les identificateurs. Les
identificateurs sont utilisés dans la famille de conversion LongTime et les unités
de mesure qui sont ses membres :
tConvFamily cbLongTime;
TConvType ltMonths;
TConvType ltYears;
TConvType ltDecades;
TConvType ltCenturies;
TConvType ltMillennia;
Recensement de la famille de conversion
Ensuite, recensez la famille de conversion :
cbLongTime := RegisterConversionFamily (‘Longues durées’);
Bien que la procédure UnregisterConversionFamily soit fournie, il n’est pas
nécessaire de dé-recenser les familles de conversion si l’unité qui les définit n’est
pas supprimée à l’exécution. Elles sont automatiquement nettoyées quand
l’application s’arrête.
Recensement des unités de mesure
Ensuite, vous devez recenser les unités de mesure dans la famille de conversion
que vous venez de créer. Utilisez la fonction RegisterConversionType, qui recense
les unités de mesure dans une famille spécifiée. Vous devez définir l’unité de
base, ici l’année, et les autres unités en utilisant un facteur indiquant leur
rapport avec l’unité de base. Le facteur de ltMonths est ainsi 1/12 puisque l’unité
de base de la famille LongTime est l’année. Vous fournissez également la
description des unités dans lesquelles vous effectuez la conversion.
Le code de recensement des unités de mesure est le suivant :
ltMonths = RegisterConversionType(cbLongTime,”Months”,1/12);
ltYears = RegisterConversionType(cbLongTime,”Years”,1);
ltDecades = RegisterConversionType(cbLongTime,”Decades”,10);
ltCenturies = RegisterConversionType(cbLongTime,”Centuries”,100);
ltMillennia = RegisterConversionType(cbLongTime,”Millennia”,1000);
Utilisation des nouvelles unités
Vous pouvez maintenant utiliser les unités que vous venez de recenser afin
d’effectuer des conversions. La fonction Convert globale peut effectuer la
4-30
Guide du développeur
Conversion de mesures
conversion entre tous les types recensés dans la famille de conversion
cbLongTime.
Ainsi, au lieu d’utiliser l’appel Convert suivant,
Convert(StrToFloat(Edit1->Text),tuMonths,tuMillennia);
vous pouvez utiliser celui-ci pour obtenir une meilleure précision :
Convert(StrToFloat(Edit1->Text),ltMonths,ltMillennia);
Utilisation d’une fonction de conversion
Dans les cas où la conversion est plus complexe, vous pouvez utiliser une
syntaxe différente et spécifier une fonction qui effectue la conversion au lieu
d’utiliser un facteur de conversion. Par exemple, vous ne pouvez pas convertir
des températures à l’aide d’un facteur de conversion, puisque les échelles de
température ont des origines différentes.
Cet exemple, qui est converti à partir de l’unité StdConvs, montre comment
recenser un type de conversion en fournissant des fonctions de conversion à
partir et à destination des unités de base.
Déclaration des variables
D’abord, vous devez déclarer des variables pour les identificateurs. Les
identificateurs sont utilisés dans la famille de conversion cbTemperature et les
unités de mesure sont ses membres :
TConvFamily cbTemperature;
TConvType tuCelsius;
TConvType tuKelvin;
TConvType tuFahrenheit;
Remarque
Les unités de mesure présentées ici forment un sous-ensemble des unités de
température réellement recensées dans l’unité StdConvs.
Recensement de la famille de conversion
Ensuite, recensez la famille de conversion :
cbTemperature = RegisterConversionFamily (“Temperature”);
Recensement de l’unité de base
Ensuite, définissez et recensez l’unité de base de la famille de conversion, qui est
dans cet exemple le degré Celsius. Remarquez que dans le cas de l’unité de base,
nous pouvons utiliser un facteur de conversion simple, car il n’y a pas de
conversion réelle à effectuer :
tuCelsius = RegisterConversionType(cbTemperature,”Celsius”,1);
Utilisation de BaseCLX
4-31
Conversion de mesures
Ecriture des méthodes de conversion à destination et à partir de l’unité de
base
Vous devez écrire le code qui effectue la conversion pour chaque échelle de
température à destination et à partir des degrés Celsius, car celles-ci ne peuvent
pas se baser sur un facteur simple. Ces fonctions sont traduites à partir de
l’unité StdConvs :
double __fastcall FahrenheitToCelsius(const double AValue)
{
return (((AValue - 32) * 5) / 9);
}
double __fastcall CelsiusToFahrenheit(const double AValue)
{
return (((AValue * 9) / 5) + 32);
}
double __fastcall KelvinToCelsius(const double AValue)
{
return (AValue - 273.15);
}
double __fastcall CelsiusToKelvin(const double AValue)
{
return (AValue + 273.15);
}
Recensement des autres unités
Maintenant que vous disposez des fonctions de conversion, vous pouvez
recenser les autres unités de mesure dans la famille de conversion. Vous incluez
également la description des unités.
Le code de recensement des autres unités de la famille est le suivant :
tuKelvin = RegisterConversionType(cbTemperature, “Kelvin”, KelvinToCelsius,
CelsiusToKelvin);
tuFahrenheit = RegisterConversionType(cbTemperature, “Fahrenheit”, FahrenheitToCelsius,
CelsiusToFahrenheit);
Utilisation des nouvelles unités
Vous pouvez maintenant utiliser les unités que vous venez de recenser pour
effectuer des conversions dans vos applications. La fonction Convert globale peut
effectuer la conversion entre tous les types recensés dans la famille de conversion
cbTemperature. Par exemple, le code suivant permet de convertir en degrés Kelvin
une valeur exprimée en degrés Fahrenheit.
Convert(StrToFloat(Edit1->Text), tuFahrenheit, tuKelvin);
Utilisation d’une classe pour gérer les conversions
Vous pouvez toujours utiliser des fonctions de conversion pour recenser une
unité de conversion. Il existe cependant des cas où il est nécessaire de créer un
4-32
Guide du développeur
Conversion de mesures
nombre inutilement grand de fonctions qui font toutes essentiellement la même
chose.
Si vous pouvez écrire un ensemble de fonctions de conversion qui diffèrent
uniquement par la valeur d’un paramètre ou d’une variable, vous pouvez créer
une classe pour gérer ces conversions. Par exemple, il existe un ensemble de
techniques standard pour les conversions entre les différentes monnaies
européennes depuis l’introduction de l’euro. Bien que les facteurs de conversion
restent constants (contrairement au facteur de conversion entre les dollars et les
euros, par exemple), vous ne pouvez pas utiliser un facteur de conversion simple
pour convertir correctement les monnaies européennes, pour deux raisons :
• La conversion doit arrondir à un nombre de décimales spécifique à la
monnaie.
• L’approche facteur de conversion utilise un facteur inverse à celui spécifié par
les conversions standard en euro.
Tout peut cependant être géré par les fonctions de conversion de la façon
suivante :
double __fastcall FromEuro(const double AValue, const double Factor, TRoundToRange FRound)
{
return(RoundTo(AValue * Factor, FRound));
}
double __fastcall ToEuro(const double AValue, const double Factor)
{
return (AValue / Factor);
}
Le problème est que cette approche nécessite des paramètres supplémentaires
pour la fonction de conversion, ce qui signifie que vous ne pouvez pas recenser
simplement la même fonction pour chaque monnaie européenne. Afin d’éviter
d’écrire deux nouvelles fonctions de conversion pour chacune des monnaies
européennes, vous pouvez utiliser le même couple de fonctions en les rendant
membres d’une classe.
Création de la classe de conversion
La classe doit être un descendant de TConvTypeFactor. TConvTypeFactor définit
deux méthodes, ToCommon et FromCommon, pour convertir à destination et à
partir des unités de base d’une famille de conversion (dans ce cas, à destination
et à partir des euros). Comme les fonctions que vous utilisez directement pour le
recensement d’une unité de conversion, ces méthodes n’ont pas de paramètres
supplémentaires, et vous devez fournir le nombre de décimales de l’arrondi et le
facteur de conversion en tant que membres privés de votre classe de conversion.
Ce point est illustré dans l’exemple EuroConv du répertoire demos\ConvertIt
(voir euroconv.pas) :
class PASCALIMPLEMENTATION TConvTypeEuroFactor : public Convutils::TConvTypeFactor
{
private:
TRoundToRange FRound;
public:
Utilisation de BaseCLX
4-33
Conversion de mesures
__fastcall TConvTypeEuroFactor(const TConvFamily AConvFamily,
const AnsiString ADescription, const double AFactor, const TRoundToRange ARound);
TConvTypeFactor(AConvFamily, ADescription, AFactor);
virtual double ToCommon(const double AValue);
virtual double FromCommon(const double AValue);
}
Le constructeur attribue des valeurs à ces membres privés :
__fastcall TConvTypeEuroFactor::TConvTypeEuroFactor(const TConvFamily AConvFamily,
const AnsiString ADescription, const double AFactor, const TRoundToRange ARound):
TConvTypeFactor(AConvFamily, ADescription, AFactor);
{
FRound = ARound;
}
Les deux fonctions de conversion utilisent tout simplement ces membres privés :
virtual double TConvTypeEuroFactor::ToCommon(const double AValue)
{
return (RoundTo(AValue * Factor, FRound));
}
virtual double TConvTypeEuroFactor::ToCommon(const double AValue)
{
return (AValue / Factor);
}
Déclaration des variables
Maintenant que vous disposez d’une classe de conversion, commencez comme
avec toute autre famille de conversion, en déclarant les identificateurs :
TConvFamily cbEuro;
TConvType euEUR; //
TConvType euBEF; //
TConvType euDEM; //
TConvType euGRD; //
TConvType euESP; //
TConvType euFFR; //
TConvType euIEP; //
TConvType euITL; //
TConvType euLUF; //
TConvType euNLG; //
TConvType euATS; //
TConvType euPTE; //
TConvType euFIM; //
Euros
Francs belges
Marks allemands
Drachmes grecs
Pesetas espagnoles
Francs français
Livres irlandaises
Lires italiennes
Francs luxembourgeois
Florins hollandais
Schillings autrichiens
Escudos portugais
Marks finlandais
Recensez la famille de conversion et les autres unités
Vous êtes maintenant prêt à recenser la famille de conversion et les unités
monétaires européennes, en utilisant votre nouvelle classe de conversion.
Recensez cette famille de conversion de la même manière que vous avez recensé
les autres :
cbEuro := RegisterConversionFamily (‘Monnaie européenne’);
4-34
Guide du développeur
Conversion de mesures
Pour recencer chaque type de conversion, créez une instance de la classe de
conversion qui reflète les propriété de facteur et d’arrondi de cette monnaie, et
appelez la méthode RegisterConversionType :
TConvTypeInfo *pInfo = new TConvTypeEuroFactor(cbEuro, “EUEuro”, 1.0, -2);
if (!RegisterConversionType(pInfo, euEUR))
delete pInfo;
pInfo = new TConvTypeEuroFactor(cbEuro, “BelgianFrancs”, 40.3399, 0);
if (!RegisterConversionType(pInfo, euBEF))
delete pInfo;
pInfo = new TConvTypeEuroFactor(cbEuro, “GermanMarks”, 1.95583, -2);
if (!RegisterConversionType(pInfo, euDEM))
delete pInfo;
pInfo = new TConvTypeEuroFactor(cbEuro, “GreekDrachmas”, 340.75, 0);
if (!RegisterConversionType(pInfo, euGRD)
delete pInfo;
pInfo = new TConvTypeEuroFactor(cbEuro, “SpanishPesetas”, 166.386, 0);
if (!RegisterConversionType(pInfo, euESP)
delete pInfo;
pInfo = new TConvTypeEuroFactor(cbEuro, “FrenchFrancs”, 6.55957, -2);
if (!RegisterConversionType(pInfo, euFFR)
delete pInfo;
pInfo = new TConvTypeEuroFactor(cbEuro, “IrishPounds”, 0.787564, -2);
if (!RegisterConversionType(pInfo, euIEP)
delete pInfo;
pInfo = new TConvTypeEuroFactor(cbEuro, “ItalianLire”, 1936.27, 0);
if (!RegisterConversionType(pInfo, euITL)
delete pInfo;
pInfo = new TConvTypeEuroFactor(cbEuro, “LuxembourgFrancs”, 40.3399, -2);
if (!RegisterConversionType(pInfo, euLUF)
delete pInfo;
pInfo = new TConvTypeEuroFactor(cbEuro, “DutchGuilders”, 2.20371, -2);
if (!RegisterConversionType(pInfo, euNLG)
delete pInfo;
pInfo = new TConvTypeEuroFactor(cbEuro, “AutstrianSchillings”, 13.7603, -2);
if (!RegisterConversionType(pInfo, euATS)
delete pInfo;
pInfo = new TConvTypeEuroFactor(cbEuro, “PortugueseEscudos”, 200.482, -2);
if (!RegisterConversionType(pInfo, euPTE)
delete pInfo;
pInfo = new TConvTypeEuroFactor(cbEuro, “FinnishMarks”, 5.94573, 0);
if (!RegisterConversionType(pInfo, euFIM)
delete pInfo;
Utilisation des nouvelles unités
Vous pouvez maintenant utiliser les unités que vous venez de recenser pour
effectuer des conversions dans vos applications. La fonction Convert globale peut
convertir toutes les monnaies européennes que vous avez recensées avec la
nouvelle famille cbEuro. Par exemple, le code suivant convertit en marks
allemands une valeur exprimée en lires italiennes :
Edit2->Text = FloatToStr(Convert(StrToFloat(Edit1->Text), euITL, euDEM));
Utilisation de BaseCLX
4-35
Création d’espaces de dessin
Création d’espaces de dessin
La classe TCanvas encapsule un contexte de périphérique Windows dans la VCL
et un périphérique de dessin (dispositif de dessin Qt) dans CLX. Elle gère tous
les dessins pour les deux fiches, les conteneurs visuels (tels que les volets) et
l’objet imprimante (voir “Impression” page 4-27). En utilisant l’objet canevas,
vous n’avez plus besoin d’allouer crayons, pinceaux ou palettes : ils sont alloués
et libérés automatiquement.
TCanvas propose un grand nombre de routines de dessin primitives pour
dessiner des lignes, des formes, des polygones, du texte, etc. dans tout contrôle
contenant un canevas. Par exemple, voici un gestionnaire d’événement de bouton
qui dessine une ligne du coin supérieur gauche vers le milieu de la fiche, et
affiche du texte brut sur cette fiche :
void __fastcall TForm1::Button1Click(TObject *Sender)
{
Canvas->Pen->Color = clBlue;
Canvas->MoveTo( 10, 10 );
Canvas->LineTo( 100, 100 );
Canvas->Brush->Color = clBtnFace;
Canvas->Font->Name = "Arial";
Canvas->TextOut( Canvas->PenPos.x, Canvas->PenPos.y,"Fin de la ligne" );
}
Dans les applications Windows, l’objet TCanvas vous protège également contre
les erreurs graphiques courantes de Windows, par exemple en restaurant les
contextes de périphériques, crayons, pinceaux, etc. à la valeur qu’ils avaient
avant l’opération de dessin. TCanvas est utilisé partout dans C++Builder lorsqu’il
est nécessaire ou possible de dessiner, et il permet de le faire d’une manière à la
fois simple et fiable.
Pour une liste complète des propriétés et méthodes de TCanvas, voir la référence
en ligne.
4-36
Guide du développeur
Chapitre
5
Utilisation des composants
Chapitre 5
De nombreux composants sont fournis par l’environnement intégré de
développement (EDI) dans la palette des composants. Vous sélectionnez des
composants dans la palette et les déposez dans une fiche ou un module de
données. Vous concevez l’interface utilisateur de l’application en disposant des
composants visuels, boutons ou listes, sur une fiche. Vous pouvez également
placer des composants non visuels, comme les composants d’accès aux données,
dans une fiche ou un module de données.
A première vue, les composants C++Builder ressemblent aux autres classes C++.
Mais, il existe des différences entre les composants de C++Builder et les
hiérarchies de classes standard C++ avec lesquelles travaillent la plupart des
programmeurs C++. Voici certaines de ces différences :
• Tous les composants C++Builder dérivent de TComponent.
• Les composants sont la plupart du temps utilisés tels quels et modifiés par le
biais de leurs propriétés, au lieu de servir de “classes de base” à sous-classer
pour ajouter des fonctionnalités ou modifier celles qui existent. Quand un
composant est dérivé, on ajoute généralement du code spécifique aux
fonctions membres de gestion des événements existants.
• Les composants sont alloués uniquement sur le tas et non sur la pile (c’est-àdire, qu’ils doivent être créés avec l’opérateur new).
• Les propriétés des composants contiennent de façon intrinsèque des
informations de type à l’exécution.
• Des composants peuvent être ajoutés à la palette de l’interface utilisateur de
C++Builder et manipulés sur une fiche.
Les composants offrent souvent un meilleur degré d’encapsulation que les classes
C++ standard. Considérez, par exemple, l’utilisation d’une boîte de dialogue
contenant un bouton poussoir. Dans un programme Windows C++ développé en
utilisant les composants de la VCL, lorsqu’un utilisateur clique sur le bouton, le
système génère un message WM_LBUTTONDOWN. Le programme doit
Utilisation des composants
5-1
Initialisation des propriétés d’un composant
intercepter ce message (généralement dans une instruction switch, une
correspondance de message ou une table de réponse) et le diriger vers une
routine qui s’exécutera en réponse au message.
La plupart des messages Windows (VCL) ou événements système (CLX) sont
gérés par les composants de C++Builder. Quand vous voulez répondre à un
message, il vous suffit de fournir un gestionnaire d’événement.
Le chapitre 8, “Conception de l’interface utilisateur des applications”, donne des
détails sur l’utilisation des fiches, comme la création dynamique de fiches
modales, la transmission de paramètres aux fiches et la récupération de données
à partir des fiches.
Initialisation des propriétés d’un composant
Les propriétés publiées peuvent être initialisées à la conception avec l’inspecteur
d’objets ou, dans certains cas, avec des éditeurs de propriétés spécifiques. Pour
spécifier des propriétés à l’exécution, il suffit d’initialiser leur valeur dans le code
source de votre application.
Pour des informations sur les propriétés de chaque composant, consultez l’aide
en ligne.
Initialisation des propriétés à la conception
A la conception, quand vous sélectionnez un composant d’une fiche, l’inspecteur
d’objets affiche ses propriétés publiées et vous permet (si c’est approprié) de les
modifier. Utilisez la touche Tab pour vous déplacer entre la colonne des valeurs
(à droite) et la colonne des propriétés (à gauche). Si le curseur est dans la
colonne des propriétés, vous pouvez vous positionner sur une propriété en
tapant les premières lettres de son nom. Pour les propriétés de type booléen ou
énuméré, vous pouvez choisir une valeur dans une liste déroulante ou parcourir
les valeurs en double-cliquant dans la colonne des valeurs.
Si un symbole plus (+) apparaît à côté du nom de la propriété, vous pouvez
faire apparaître une liste de sous-valeurs pour la propriété en cliquant sur le
symbole plus ou en tapant ‘+’. Si un symbole moins (-) apparaît à côté du nom
de la propriété, vous pouvez faire apparaître une liste de sous-valeurs pour la
propriété en cliquant sur le symbole moins ou en tapant ‘-’.
Par défaut, les propriétés de la catégorie Héritage ne sont pas affichées. Pour
modifier les filtres d’affichage, cliquez avec le bouton droit de la souris dans
l’inspecteur d’objets et choisissez Voir. Pour davantage d’informations, voir
“catégories de propriétés” dans l’aide en ligne.
Si plusieurs composants sont sélectionnés, l’inspecteur d’objets affiche toutes les
propriétés, sauf Name, communes aux composants sélectionnés. Si la valeur d’une
propriété partagée n’est pas la même pour tous les composants sélectionnés,
l’inspecteur d’objets affiche soit la valeur par défaut, soit la valeur de la
5-2
Guide du développeur
Appel de méthodes
propriété pour le premier composant sélectionné. Quand vous modifiez une
propriété partagée, la modification s’applique à tous les composants sélectionnés.
La modification de propriétés liées au code dans l’inspecteur d’objets, comme le
nom d’un gestionnaire d’événement, modifie automatiquement le code source
correspondant. De plus la modification du code source, par exemple le nom
d’une méthode gestionnaire d’événement dans la déclaration de classe d’une
fiche, est immédiatement reflétée dans l’inspecteur d’objets.
Utilisation des éditeurs de propriétés
Certaines propriétés, comme Font utilisent des éditeurs de propriétés spécifiques.
Quand une telle propriété est sélectionnée dans l’inspecteur d’objets, un bouton
points de suspension (...) apparaît à côté de sa valeur. Pour ouvrir l’éditeur de
propriété, double-cliquez dans la colonne des valeurs, cliquez sur le bouton points
de suspension ou tapez Ctrl+Entrée quand la focalisation se trouve sur la propriété
ou sur sa valeur. Pour certains composants, il suffit de double-cliquer sur le
composant dans la fiche pour ouvrir un éditeur de propriété.
Les éditeurs de propriétés permettent de définir des propriétés complexes à
partir d’une seule boîte de dialogue. Elles valident les saisies et permettent
souvent de prévisualiser les effets d’une affectation.
Initialisation des propriétés à l’exécution
Vous pouvez à l’exécution utiliser votre code source pour affecter une valeur à
toute propriété accessible en écriture. Vous pouvez ainsi, définir de manière
dynamique le libellé d’une fiche :
Form1->Caption = MyString;
Appel de méthodes
Une méthode s’appelle comme une procédure ou une fonction ordinaire. Par
exemple, les contrôles visuels disposent de la méthode Repaint qui rafraîchit
l’image du contrôle à l’écran. Vous pouvez appeler la méthode Repaint d’un objet
grille de dessin de la manière suivante :
DrawGrid1->Repaint;
Comme pour les propriétés, c’est la portée d’une méthode qui impose ou pas
l’utilisation de qualificateurs. Par exemple, pour redessiner une fiche depuis le
gestionnaire d’événement de l’un des contrôles enfant de la fiche, il n’est pas
nécessaire de préfixer l’appel de méthode avec le nom de la fiche :
void __fastcall TForm1::Button1Click(TObject *Sender)
{
Repaint;
}
Utilisation des composants
5-3
Utilisation des événements et des gestionnaires d’événements
Utilisation des événements et des gestionnaires d’événements
Dans C++Builder, l’essentiel du code que vous écrivez est exécuté, directement
ou indirectement, en réponse à des événements. Un événement est un type
particulier de propriété qui représente une situation à l’exécution, généralement
une action de l’utilisateur. Le code qui répond directement à un événement, ce
qu’on appelle un gestionnaire d’événement, est une méthode d’un objet. Les
sections suivantes expliquent comment :
•
•
•
•
•
•
Générer un nouveau gestionnaire d’événement
Générer le gestionnaire de l’événement par défaut d’un composant
Rechercher un gestionnaire d’événement
Associer un événement à un gestionnaire d’événement existant
Associer des événements de menu à des gestionnaires d’événements
Supprimer un gestionnaire d’événement
Génération d’un nouveau gestionnaire d’événement
C++Builder peut créer le squelette de gestionnaires d’événements pour les fiches
et les autres composants. Pour créer un gestionnaire d’événement,
1 Sélectionnez un composant.
2 Cliquez dans la page Evénements de l’inspecteur d’objets. La page
Evénements de l’inspecteur d’objets affiche tous les événements définis pour le
composant sélectionné.
3 Sélectionnez l’événement de votre choix puis double-cliquez dans la colonne
valeur ou appuyez sur Ctrl+Entrée.
4 Entrez le code que vous voulez exécuter lorsque l’événement se produit.
Génération du gestionnaire de l’événement par défaut d’un
composant
Certains composants ont un événement par défaut, celui que le composant a le
plus souvent besoin de gérer. Par exemple, l’événement par défaut d’un bouton
est OnClick. Pour créer un gestionnaire de l’événement par défaut, double-cliquez
sur le composant dans le concepteur de fiche, cela génère le squelette de la
procédure de gestion de l’événement et ouvre l’éditeur de code en plaçant le
curseur à l’intérieur du corps de la procédure où il ne vous reste plus qu’à
ajouter du code.
Certains composants n’ont pas d’événement par défaut. D’autres, comme le
biseau (TBevel), n’ont pas du tout d’événement. D’autres composants encore
peuvent réagir différemment si vous double-cliquez dessus dans le concepteur
de fiche. Par exemple, plusieurs composants ouvrent un éditeur de propriété par
défaut ou une autre boîte de dialogue quand on double-clique dessus à la
conception.
5-4
Guide du développeur
Utilisation des événements et des gestionnaires d’événements
Recherche de gestionnaires d’événements
Si vous avez généré le gestionnaire de l’événement par défaut d’un composant
en double-cliquant dessus dans le concepteur de fiche, vous pouvez revenir
dessus en recommençant. Double-cliquez sur le composant ; l’éditeur de code
s’ouvre, le curseur positionné sur le début du corps du gestionnaire
d’événement.
Pour rechercher le gestionnaire d’un événement qui n’est pas l’événement par
défaut,
1 Dans la fiche, sélectionnez le composant dont vous recherchez le gestionnaire
d’événement.
2 Dans l’inspecteur d’objets, cliquez sur l’onglet Evénements.
3 Sélectionnez l’événement dont vous recherchez le gestionnaire et doublecliquez dans la colonne des valeurs. L’éditeur de code s’ouvre, le curseur
positionné sur le début du corps du gestionnaire d’événement.
Association d’un événement à un gestionnaire d’événement
existant
Vous pouvez réutiliser le code en écrivant des gestionnaires d’événements qui
gèrent plusieurs événements de composants. Par exemple, de nombreuses
applications proposent des turboboutons qui sont l’équivalent de commandes de
la barre des menus. Quand un bouton initie la même action qu’une commande
de menu, vous pouvez écrire un seul gestionnaire d’événement et l’affecter à
l’événement OnClick du bouton et de l’élément de menu.
Pour associer un événement à un gestionnaire d’événement existant,
1 Dans la fiche, sélectionnez le composant dont vous voulez gérer un
événement.
2 Dans la page Evénements de l’inspecteur d’objets, sélectionnez l’événement
auquel vous voulez attacher un gestionnaire.
3 Cliquez sur le bouton flèche vers le bas à côté de l’événement afin d’ouvrir
une liste des gestionnaires d’événements existants. La liste ne propose que les
gestionnaires d’événements écrits pour des événements portant le même nom
dans la même fiche. Sélectionnez dans la liste en cliquant sur un nom de
gestionnaire d’événement.
Cette manière de procéder est un moyen simple de réutiliser des gestionnaires
d’événements. Cependant, les listes d’actions et dans la VCL, les bandes
d’actionsconstituent un outil plus puissant permettant de centraliser l’organisation
du code répondant à des commandes de l’utilisateur. Les listes d’actions peuvent
être utilisées dans les applications multiplates-formes, alors que les bandes
d’actions ne le peuvent pas. Pour davantage d’informations sur les listes
d’actions et les bandes d’actions, voir “Organisation des actions pour les barres
d’outils et les menus” à la page 8-18.
Utilisation des composants
5-5
Utilisation des événements et des gestionnaires d’événements
Utilisation du paramètre Sender
Dans un gestionnaire d’événement, le paramètre Sender indique le composant qui
a reçu l’événement et qui a donc appelé le gestionnaire. Il est parfois pratique de
partager entre plusieurs composants un même gestionnaire d’événement qui doit
se comporter différemment selon le composant qui l’a appelé. Pour ce faire, vous
pouvez utiliser le paramètre Sender.
Affichage et codage d’événements partagés
Si des composants partagent des événements, vous pouvez afficher leurs
événements partagés dans l’inspecteur d’objets. Commencez par sélectionner les
composants en maintenant enfoncée la touche Maj et en cliquant dessus dans le
concepteur de fiche ; puis, choisissez l’onglet Evénements de l’inspecteur d’objets.
Dans la colonne des valeurs de l’inspecteur d’objets, vous pouvez alors créer un
nouveau gestionnaire d’événement ou affecter un gestionnaire d’événement
existant aux événements partagés.
Association d’événements de menu à des gestionnaires
d’événements
Le concepteur de menus, utilisé pour les composants MainMenu et PopupMenu,
vous permet de spécifier simplement des menus déroulants ou surgissants dans
une application. Pour que les menus fonctionnent, il faut néanmoins que chaque
élément de menu réponde à l’événement OnClick qui se produit à chaque fois
que l’utilisateur choisit l’élément de menu ou appuie sur sa touche de raccourci.
Cette section explique comment associer des gestionnaires d’événements aux
éléments de menu. Pour plus d’informations sur le concepteur de menus et les
composants associés, voir “Création et gestion de menus” à la page 8-32.
Pour créer un gestionnaire d’événement pour un élément de menu,
1 Ouvrez le concepteur de menus en double-cliquant sur un composant
MainMenu ou PopupMenu.
2 Sélectionnez un élément de menu dans le concepteur de menus. Dans
l’inspecteur d’objets, vérifiez qu’une valeur est attribuée à la propriété Name
de l’élément.
3 Dans le concepteur de menus, double-cliquez sur l’élément de menu.
C++Builder génère un gestionnaire d’événement dans l’éditeur de code.
4 Entrez le code que vous voulez exécuter lorsque l’utilisateur choisit la
commande de menu.
Pour associer un élément de menu à un gestionnaire d’événement OnClick
existant :
1 Ouvrez le concepteur de menus en double-cliquant sur un composant
MainMenu ou PopupMenu.
5-6
Guide du développeur
Composants multiplates-formes ou non multiplates-formes
2 Sélectionnez un élément de menu dans le concepteur de menus. Dans
l’inspecteur d’objets, vérifiez qu’une valeur est attribuée à la propriété Name
de l’élément.
3 Dans la page Evénements de l’inspecteur d’objets, cliquez sur le bouton flèche
vers le bas à côté de OnClick afin d’ouvrir une liste des gestionnaires
d’événements existants. La liste ne propose que les gestionnaires d’événements
écrits pour des événements OnClick dans la fiche. Sélectionnez un gestionnaire
d’événement en cliquant sur son nom dans la liste.
Suppression de gestionnaires d’événements
Quand vous supprimez un composant d’une fiche en utilisant le concepteur de
fiche, C++Builder retire le composant de la déclaration de type de la fiche. Mais,
il ne supprime pas les méthodes associées car elles peuvent être appelées par
d’autres composants de la fiche. Vous pouvez supprimer manuellement une
méthode (comme un gestionnaire d’événement) mais si vous le faites, vous
devez supprimer la déclaration avancée de la méthode et son implémentation ;
sinon, vous obtiendrez une erreur de compilation lors de la génération du projet.
Composants multiplates-formes ou non multiplates-formes
La palette de composants contient une sélection de composants qui gèrent une
grande variété d’opérations de programmation. Les composants ayant des
fonctions similaires sont regroupés en pages. Par exemple, les composants les
plus utilisés, comme ceux qui créent des menus, des boîtes d’édition ou des
boutons, se trouvent dans la page Standard de la palette des composants. Les
pages apparaissant dans la configuration par défaut dépendent de la version que
vous utilisez.
Le tableau 5.1 énumère les pages définies par défaut et les composants, y
compris ceux qui ne sont pas multiplates-formes, disponibles pour la création
des applications. Vous pouvez utiliser tous les composants CLX dans les
applications Windows et Linux. Vous pouvez utiliser certains composants non
visuels spécifiques à la VCL dans les applications CLX uniquement Windows,
cependant, les applications ne seront pas multiplates-formes sauf si vous isolez
ces parties du code.
Tableau 5.1 Pages de la palette de composants
Nom de page
Description
Multiplate-forme ?
Standard
Contrôles standard, menus
Oui
Supplément
Contrôles spécialisés
Oui sauf ApplicationEvents,
ActionManager, ActionMainMenuBar,
ActionToolBar et CustomizeDlg.
LCDNumber existe uniquement en CLX.
Utilisation des composants
5-7
Composants multiplates-formes ou non multiplates-formes
Tableau 5.1 Pages de la palette de composants (suite)
5-8
Nom de page
Description
Multiplate-forme ?
Win32 (VCL)/
Contrôles
communs
(CLX)
Contrôles Windows courants
La plupart des composants de la page
Win32 sont proposés dans la page
Contrôles Windows courants affichée
lors de la création d’une application
CLX.
RichEdit, UpDown, HotKey, Animate,
DataTimePicker, MonthCalendar,
Coolbar, PageScroller et ComboBoxEx
existent uniquement dans la VCL.
TextBrowser, TextViewer, IconViewer et
SpinEdit sont CLX uniquement.
Système
Composants et contrôles
permettant un accès au niveau
du système, y compris les
timers, le multimédia et le
DDE.
Non, sauf Timer et PaintBox qui sont
dans la page Supplément lors de la
création d’une application CLX.
AccèsBD
Composants pour le travail
avec les données des bases de
données qui ne sont pas liées à
un mécanisme d’accès aux
données particulier
Oui sauf XMLTransform,
XMLTransformProvider et
XMLTransformClient.
ContrôleBD
Contrôles visuels orientés
données
Oui sauf pour DBRichEdit,
DBCtrlGrid et DBChart.
dbExpress
Contrôles de bases de données
qui utilisent dbExpress, une
couche multiplate-forme
indépendante des bases de
données qui fournit des
méthodes pour le traitement
SQL dynamique. Elle définit
une interface commune
permettant d’accéder aux
serveurs SQL.
Oui
DataSnap
Composants utilisés pour créer
des applications de bases de
données multiniveaux.
Non
BDE
Composants qui fournissent
l’accès aux données via le BDE
(Borland Database Engine).
Non
ADO
Composants permettant
d’accéder aux données par le
biais du modèle ADO.
Non
InterBase
Composants fournissant un
accès direct aux bases de
données InterBase.
Oui
InterBaseAdmin
Composants accèdant aux
appels de l’API InterBase
Services.
Oui
Guide du développeur
Composants multiplates-formes ou non multiplates-formes
Tableau 5.1 Pages de la palette de composants (suite)
Nom de page
Description
Multiplate-forme ?
InternetExpress
Composants qui sont
simultanément une application
serveur web et le client d’une
application de base de données
multiniveau.
Non
Internet
Composants pour les protocoles
de communication Internet et
les applications Web.
Non
WebSnap
Composants pour la
construction d’applications
serveur Web.
Non
FastNet
Contrôles Internet NetMasters.
Non
QReport
Composants QuickReport
utilisés pour créer des états
incorporés.
Non
Dialogues
Boîtes de dialogue les plus
utilisées.
Oui sauf pour OpenPictureDialog,
SavePictureDialog, PrintDialog et
PrinterSetupDialog.
Win 3.1
Ancien style des composants
Win 3.1
Non
Exemples
Composants personnalisés
exemple.
Non
ActiveX
Exemples de contrôles ActiveX ;
voir la documentation Microsoft
(msdn.microsoft.com).
Non
COM+
Composant pour la gestion des
événements COM+.
Non
WebServices
Composants pour écrire des
applications qui implémentent
ou utilisent des services Web
basés sur SOAP
Non
Serveurs
Exemples serveur COM pour
Microsoft Excel, Word, etc. (voir
la documentation MSDN
Microsoft)
Non
Indy - Clients
Composants Internet
multiplates-formes pour le
client (composants Internet
Winshoes à code source libre).
Oui
Indy - Serveurs
Composants Internet
multiplates-formes pour le
serveur (composants Internet
Winshoes à code source libre).
Oui
Indy - Divers
Composants Internet
multiplates-formes
supplémentaires (composants
Internet Winshoes à code
source libre).
Oui
Utilisation des composants
5-9
Composants multiplates-formes ou non multiplates-formes
Vous pouvez ajouter, retirer ou réorganiser les composants de la palette et vous
pouvez créer des modèles de composants et des cadres qui regroupent plusieurs
composants.
L’aide en ligne propose des informations sur les composants de la palette.
Cependant, certains des composants des pages ActiveX, Serveurs et Exemples
sont proposés uniquement à titre d’exemple et ne sont pas documentés.
Pour davantage d’informations sur les différences entre la VCL et CLX, voir
chapitre 14, “Développement d’applications multiplates-formes”.
Ajout de composants personnalisés à la palette des composants
Vous pouvez installer des composants personnalisés, conçus par vous ou acquis
séparément, dans la palette de composants et les utiliser dans vos applications.
Pour écrire un composant, voir Partie V, “Création de composants personnalisés”
Pour installer un composant existant, voir “Installation de paquets de
composants” à la page 15-6.
5-10
Guide du développeur
Chapitre
6
Manipulation des contrôles
Chapitre 6
Les contrôles sont des composants visuels avec lesquels l’utilisateur peut
interagir à l’exécution. Ce chapitre décrit un ensemble de fonctionnalités
communes à de nombreux contrôles.
Implémentation du glisser-déplacer dans les contrôles
Le glisser-déplacer est souvent une façon pratique de manipuler des objets.
Les utilisateurs peuvent ainsi faire glisser des contrôles entiers, ou bien extraire
des éléments de contrôles (tels que des boîtes liste ou des vues arborescentes) en
les faisant glisser sur d’autres contrôles.
•
•
•
•
•
•
Début de l’opération glisser-déplacer
Acceptation des éléments à déplacer
Déplacement des éléments
Fin de l’opération glisser-déplacer
Personnalisation du glisser-déplacer avec un objet déplacement
Changement du pointeur de la souris
Début de l’opération glisser-déplacer
Chaque contrôle possède une propriété appelée DragMode qui détermine la façon
dont les opérations glisser sont démarrées. Si la propriété DragMode est à
dmAutomatic, l’opération glisser commence automatiquement quand l’utilisateur
clique sur le bouton de la souris alors que le curseur se trouve au-dessus d’un
contrôle. Le plus souvent, vous donnerez à DragMode la valeur dmManual (la
valeur par défaut) et lancerez l’opération glisser en gérant les événements bouton
de souris enfoncé.
Pour faire glisser un contrôle manuellement, appelez la méthode BeginDrag du
contrôle. BeginDrag requiert un paramètre booléen appelé Immediate et un
Manipulation des contrôles
6-1
Implémentation du glisser-déplacer dans les contrôles
paramètre entier appelé Threshold. Si vous transmettez true pour Immediate,
l’opération glisser commence immédiatement. Si vous transmettez false,
l’opération glisser ne commence pas avant que l’utilisateur ne déplace la souris
du nombre de pixels spécifié par Threshold. Si Threshold vaut -1, la valeur par
défaut est utilisée. L’appel de
BeginDrag (false, -1);
permet au contrôle d’accepter les clics de la souris sans lancer une opération
glisser.
Vous pouvez imposer des conditions pour commencer l’opération glisser, par
exemple vérifier le bouton de souris enfoncé par l’utilisateur, en testant les
paramètres du gestionnaire de l’événement bouton de souris enfoncé, avant
l’appel à BeginDrag. Le code qui suit, par exemple, gère l’événement bouton de
souris enfoncé dans une boîte liste de fichiers en ne lançant l’opération glisser
que si le bouton gauche de la souris a été enfoncé.
void __fastcall TFMForm::FileListBox1MouseDown(TObject *Sender,
TMouseButton Button, TShiftState Shift, int X, int Y)
{
if (Button == mbLeft)// ne glisser que si le bouton gauche est enfoncé
{
TFileListBox *pLB = (TFileListBox *)Sender; // transtypage sur TFileListBox
if (pLB->ItemAtPos(Point(X,Y), true) >= 0) // y a-t-il un élément ici ?
pLB->BeginDrag(false, -1);
// si oui, le faire glisser
}
}
Acceptation des éléments à déplacer
Quand l’utilisateur fait glisser quelque chose sur un contrôle, celui-ci reçoit un
événement OnDragOver. Il doit alors indiquer s’il peut accepter l’élément dans le
cas où l’utilisateur le lâcherait à cet emplacement. L’aspect du curseur change
pour indiquer si le contrôle peut accepter l’élément que l’utilisateur fait glisser.
Pour accepter les éléments que l’utilisateur fait glisser sur un contrôle, attachez
un gestionnaire à l’événement OnDragOver du contrôle.
L’événement “glisser-dessus” a un paramètre appelé Accept que le gestionnaire
d’événement peut définir à true pour indiquer qu’il accepte l’élément. Accept
change le type de curseur en curseur d’acceptation ou pas.
L’événement “glisser-dessus” présente d’autres paramètres, dont la source de
l’opération glisser et l’emplacement actuel du curseur de la souris, que le
gestionnaire d’événement peut utiliser pour déterminer s’il doit accepter le
déplacement. Dans l’exemple ci-dessous, une arborescence de répertoires accepte
les objets déplacés seulement s’ils viennent d’une boîte liste de fichiers.
void __fastcall TForm1::TreeView1DragOver(TObject *Sender, TObject *Source,
int X, int Y, TDragState State, bool &Accept)
{
if (Source->InheritsFrom(__classid(TFileListBox)))
Accept = true;
}
6-2
Guide du développeur
Implémentation du glisser-déplacer dans les contrôles
Déplacement des éléments
Si un contrôle indique qu’il peut accepter un élément déplacé, il doit le traiter
s’il est effectivement lâché. Pour gérer les éléments lâchés, attachez un
gestionnaire à l’événement OnDragDrop du contrôle qui accepte l’opération
lâcher. Comme l’événement “glisser-dessus”, l’événement “glisser-déplacer”
indique la source de l’élément déplacé et les coordonnées du curseur de la souris
lorsqu’il est au-dessus du contrôle acceptant l’élément. Le dernier paramètre
vous permet de contrôler le chemin emprunté par un élément au cours de
l’opération glisser ; vous pouvez, par exemple, utiliser ces informations pour
modifier la couleur affichée par les composants si un élément est déposé.
Dans l’exemple suivant, une arborescence de répertoires, qui accepte les éléments
déplacés depuis une boîte liste de fichiers, répond en déplaçant les fichiers vers
le répertoire sur lequel ils sont lâchés :
void __fastcall TForm1::TreeView1DragDrop(TObject *Sender, TObject *Source,
int X, int Y){
if (Source->InheritsFrom(__classid(TFileListBox)))
{
TTreeNode *pNode = TreeView1->GetNodeAt(X,Y); // pNode est la cible du déplacement
AnsiString NewFile = pNode->Text + AnsiString("/") +
ExtractFileName(FileList->FileName); // crée un nom de fichier pour la cible
MoveFileEx(FileListBox1->FileName.c_str(), NewFile.c_str(),
MOVEFILE_REPLACE_EXISTING | MOVEFILE_COPY_ALLOWED); // déplace le fichier
}
}
Fin de l’opération glisser-déplacer
Une opération glisser se termine lorsque l’élément est déplacé avec succès ou
qu’il est relâché au-dessus d’un contrôle qui ne peut pas l’acccepter. A ce stade
un événement “fin-glisser” est envoyé au contrôle à partir duquel le déplacement
a commencé. Pour permettre à un contrôle de répondre quand des éléments en
sont extraits, attachez un gestionnaire à l’événement OnEndDrag du contrôle.
Le paramètre le plus important dans un événement OnEndDrag est appelé Target,
il indique quel contrôle, le cas échéant, accepte l’élément déplacé. Si Target est
null, cela signifie qu’aucun contrôle ne l’accepte. L’événement OnEndDrag
comprend aussi les coordonnées du contrôle de réception.
Dans cet exemple, une boîte liste de fichiers gère un événement “fin-glisser” en
mettant à jour sa liste de fichiers.
void __fastcall TFMForm::FileListBox1EndDrag(TObject *Sender, TObject *Target,
Y)
if (Target)
FileListBox1->Update();
};
int X, int
Manipulation des contrôles
6-3
Implémentation du glisser-ancrer dans les contrôles
Personnalisation du glisser-déplacer avec un objet déplacement
Vous pouvez utiliser un descendant de TDragObject pour personnaliser le
comportement glisser-déplacer d’un objet. Les événements “glisser-dessus” et
“glisser-déplacer” standard indiquent la source de l’élément glissé et les
coordonnées du curseur de souris au-dessus du contrôle qui l’accepte. Pour
obtenir des informations supplémentaires sur l’état en cours, dérivez un objet
glissé de TDragObject ou TDragObjectEx (VCL uniquement) et surchargez ses
méthodes virtuelles. L’objet glissé doit être créé dans l’événement OnStartDrag.
Normalement, le paramètre source des événements “glisser-dessus” et “glisserdéplacer” est le contrôle qui commence l’opération glisser. Si plusieurs sortes de
contrôles peuvent commencer une opération impliquant le même type de
données, la source doit gérer chaque sorte de contrôle. Lorsque vous utilisez un
descendant de TDragObject, toutefois, la source est l’objet glissé lui-même ; si
chaque contrôle crée le même type d’objet glissé dans son événement
OnStartDrag, la cible doit gérer uniquement une sorte d’objet. Les événements
“glisser-dessus” et “glisser-déplacer” peuvent indiquer si la source est un objet
glissé, en opposition au contrôle, en appelant la fonction IsDragObject.
Les descendants de TDragObjectEx (VCL uniquement) sont libérés
automatiquement alors que les descendants de TDragObject ne le sont pas. Si
vous avez des descendants de TDragObject qui ne sont pas explicitement libérés,
vous pouvez les modifier de façon à ce qu’ils dérivent de TDragObjectEx au lieu
de surveiller les perte de mémoire.
Les objets glissés vous permettent de déplacer des éléments entre une fiche
implémentée dans le fichier exécutable principal de l’application et une fiche
implémentée en utilisant une DLL, ou entre des fiches implémentées en utilisant
différentes DLL.
Changement du pointeur de la souris
Il est possible de personnaliser l’aspect du pointeur de la souris lors d’opérations
glisser en définissant la propriété DragCursor du composant source (VCL
seulement).
Implémentation du glisser-ancrer dans les contrôles
Les descendants de TWinControl peuvent faire office de sites ancrés et les
descendants de TControl peuvent faire office de fenêtres enfant ancrées dans les
sites d’ancrage. Par exemple, pour fournir un site d’ancrage sur le bord gauche
de la fenêtre d’une fiche, alignez un volet sur le bord gauche de la fiche et
faites-en un site d’ancrage. Lorsque des contrôles ancrables sont déplacés vers le
volet puis lâchés, ils deviennent des contrôles enfant du volet.
• Transformation d’un contrôle fenêtré en un site d’ancrage
• Transformation d’un contrôle en un enfant ancrable
6-4
Guide du développeur
Implémentation du glisser-ancrer dans les contrôles
• Contrôle de l’ancrage des contrôles enfant
• Contrôle du désancrage des contrôles enfant
• Contrôle de la réponse des contrôles enfant aux opérations glisser-ancrer
Remarque
Les propriétés de glisser-ancrer sont disponibles dans la VCL, mais non
dans CLX.
Transformation d’un contrôle fenêtré en un site d’ancrage
Pour transformer un contrôle fenêtré en un site d’ancrage :
1 Mettez la propriété DockSite à true.
2 Si l’objet site ancré ne doit apparaître que lorsqu’il contient un client ancré,
mettez sa propriété AutoSize à true. Lorsque AutoSize est à true, le site ancré a
pour taille 0 jusqu’à ce qu’il accepte d’ancrer un contrôle enfant, après quoi il
est redimensionné de sorte qu’il englobe le contrôle enfant.
Transformation d’un contrôle en un enfant ancrable
Pour transformer un contrôle en un enfant ancrable :
1 Mettez sa propriété DragKind à dkDock. Lorsque DragKind est à dkDock, le fait
de faire glisser le contrôle déplace ce dernier vers un nouveau site d’ancrage
ou désancre le contrôle qui devient une fenêtre flottante. Lorsque DragKind est
à dkDrag (valeur par défaut), le fait de faire glisser le contrôle démarre une
opération glisser-déplacer qui doit être implémentée à l’aide des événements
OnDragOver, OnEndDrag et OnDragDrop.
2 Mettez sa propriété DragMode à dmAutomatic. Lorsque DragMode est à
dmAutomatic, le glissement (glisser-déplacer ou ancrage, suivant DragKind) est
automatiquement lancé lorsque l’utilisateur commence à faire glisser le
contrôle avec la souris. Lorsque DragMode est à dmManual, vous pouvez
commencer une opération glisser-ancrer (ou glisser-déplacer) en appelant la
méthode BeginDrag.
3 Définissez sa propriété FloatingDockSiteClass pour indiquer le descendant
TWinControl qui doit héberger le contrôle lorsqu’il est désancré et devient une
fenêtre flottante. Lorsque le contrôle est libéré et hors d’un site d’ancrage, un
contrôle fenêtré de cette classe est dynamiquement créé et devient le parent de
l’enfant ancrable. Si le contrôle enfant ancrable est un descendant de
TWinControl, il n’est pas nécessaire de créer un site ancré flottant séparé pour
héberger le contrôle, bien qu’il soit possible de spécifier une fiche pour obtenir
une bordure et une barre de titre. Pour ignorer une fenêtre conteneur
dynamique, attribuez à FloatingDockSiteClass la même classe que le contrôle et
elle deviendra une fenêtre flottante sans parent.
Manipulation des contrôles
6-5
Implémentation du glisser-ancrer dans les contrôles
Contrôle de l’ancrage des contrôles enfant
Un site d’ancrage accepte automatiquement les contrôles enfant lorsqu’ils sont
libérés au-dessus de lui. Pour la plupart des contrôles, le premier enfant est
ancré pour remplir la zone client, le deuxième divise cette dernière en différentes
régions, et ainsi de suite. Les contrôles de page ancrent les enfants dans de
nouvelles feuilles à onglets (ou fusionnent dans des feuilles à onglets si l’enfant
est un autre contrôle de page).
Trois événements permettent aux sites d’influer sur l’ancrage des contrôles
enfant :
__property TGetSiteInfoEvent OnGetSiteInfo = {read=FOnGetSiteInfo, write=FOnGetSiteInfo};
typedef void __fastcall (__closure *TGetSiteInfoEvent)(System::TObject* Sender, TControl*
DockClient, Windows::TRect &InfluenceRect, const Windows::TPoint &MousePos, bool &CanDock);
OnGetSiteInfo intervient sur le site d’ancrage lorsque l’utilisateur fait glisser un
enfant ancrable sur le contrôle. Il permet au site d’indiquer s’il accepte en tant
qu’enfant le contrôle spécifié par le paramètre DockClient et, si tel est le cas, où
l’enfant doit se trouver en vue de son ancrage. Lorsque OnGetSiteInfo intervient,
InfluenceRect est initialisée selon les coordonnées d’écran du site d’ancrage et
CanDock est intialisée à true. Une région d’ancrage plus limitée peut être créée en
changeant InfluenceRect et l’enfant peut être rejeté en mettant CanDock à false.
__property TDockOverEvent OnDockOver = {read=FOnDockOver, write=FOnDockOver};
typedef void __fastcall (__closure *TDockOverEvent)(System::TObject* Sender,
TDragDockObject* Source, int X, int Y, TDragState State, bool &Accept);
OnDockOver intervient sur le site d’ancrage lorsque l’utilisateur fait glisser un
enfant ancrable sur le contrôle. Il est analogue à l’événement OnDragOver au
cours d’une opération normale de glisser-déposer. Utilisez-le pour indiquer que
l’enfant peut être relâché en vue de son ancrage, en initialisant la propriété
Accept. Si le contrôle ancrable est rejeté par le gestionnaire d’événement
OnGetSiteInfo (par exemple, si le type de contrôle est incorrect), OnDockOver ne
se produit pas.
__property TDockDropEvent OnDockDrop = {read=FOnDockDrop, write=FOnDockDrop};
typedef void __fastcall (__closure *TDockDropEvent)(System::TObject* Sender,
TDragDockObject* Source, int X, int Y);
OnDockDrop intervient sur le site d’ancrage lorsque l’utilisateur relâche l’enfant
ancrable sur le contrôle. Il est analogue à l’événement OnDragDrop au cours
d’une opération normale de glisser-déposer. Utilisez-le pour faire en sorte
d’accepter le contrôle en tant que contrôle enfant. L’accès au contrôle enfant peut
être obtenu à l’aide de la propriété Control de TDockObject spécifié par le
paramètre Source.
Contrôle du désancrage des contrôles enfant
Un site d’ancrage permet de désancrer automatiquement les contrôles enfant
lorsqu’ils sont déplacés et que leur propriété DragMode vaut dmAutomatic.
6-6
Guide du développeur
Manipulation du texte dans les contrôles
Les sites d’ancrage peuvent réagir lorsque les contrôles enfant sont retirés, et
même empêcher le désancrage, dans un gestionnaire d’événement OnUnDock :
__property TUnDockEvent OnUnDock = {read=FOnUnDock, write=FOnUnDock};
typedef void __fastcall (__closure *TUnDockEvent)(System::TObject* Sender, TControl*
Client, TWinControl* NewTarget, bool &Allow);
Le paramètre Client indique le contrôle enfant qui tente un désancrage et le
paramètre Allow permet au site d’ancrage (Sender) de rejeter le désancrage.
Lorsque vous implémentez un gestionnaire d’événement OnUnDock, il peut être
utile de connaître les autres enfants éventuellement ancrés. Ces informations
figurent dans la propriété en lecture seule DockClients, qui est un tableau indexé
de TControl. Le nombre de clients ancrés est donné par la propriété en lecture
seule DockClientCount.
Contrôle de la réponse des contrôles enfant aux opérations
glisser-ancrer
Les contrôles enfant ancrables disposent de deux événements qui interviennent
au cours des opérations glisser-ancrer. OnStartDock, analogue à l’événement
OnStartDrag d’une opération glisser-déposer, permet au contrôle enfant ancrable
de créer un objet glisser personnalisé. OnEndDock, comme OnEndDrag, se produit
lorsque l’opération glisser s’achève.
Manipulation du texte dans les contrôles
Les sections suivantes expliquent comment utiliser les différentes fonctions des
contrôles éditeur de texte formaté et des contrôles mémo. Certaines de ces
fonctions peuvent aussi être utilisées avec les contrôles éditeur.
•
•
•
•
•
•
•
•
•
•
Définition de l’alignement du texte
Ajout de barres de défilement en mode exécution
Ajout de l’objet presse-papiers
Sélection de texte
Sélection de la totalité d’un texte
Opérations couper, copier et coller
Suppression du texte sélectionné
Désactivation des éléments de menu
Ajout d’un menu surgissant
Gestion de l’événement OnPopup
Définition de l’alignement du texte
Dans un composant mémo ou éditeur de texte formaté, le texte peut être aligné
à gauche, à droite ou centré. Pour modifier l’alignement du texte, spécifiez la
propriété Alignment du composant. L’alignement n’est appliqué que si la
propriété WordWrap est à true ; si le retour à la ligne automatique est désactivé, il
n’existe pas de marge sur laquelle s’aligner.
Manipulation des contrôles
6-7
Manipulation du texte dans les contrôles
Par exemple, le code suivant de l’exemple RichEdit définit l’alignement selon le
bouton choisi :
switch((int)RichEdit1->Paragraph->Alignment)
{
case 0: LeftAlign->Down = true; break;
case 1: RightAlign->Down = true; break;
case 2: CenterAlign->Down = true; break;
}
Ajout de barres de défilement en mode exécution
Les composants mémo ou éditeur de texte formaté peuvent contenir des barres
de défilement horizontales ou verticales ou les deux, selon les besoins. Lorsque
le retour à la ligne automatique est actif, le composant n’a besoin que d’une
barre de défilement vertical. Si l’utilisateur désactive le retour à la ligne
automatique, le composant a besoin aussi d’une barre de défilement horizontal,
puisque le texte n’est plus limité par le bord droit de l’éditeur.
Pour ajouter des barres de défilement en mode exécution :
1 Déterminez si le texte peut dépasser la marge droite. Dans la majorité des cas,
cela implique de tester si le retour à la ligne automatique est activé. Vous
devrez aussi vérifier qu’il existe réellement des lignes dépassant la largeur du
contrôle.
2 Définissez la propriété ScrollBars du composant mémo ou éditeur de texte
formaté de façon à inclure ou à exclure les barres de défilement.
L’exemple suivant attache le gestionnaire de l’événement OnClick à l’élément de
menu Caractères | Retour à la ligne.
void __fastcall TEditForm::WordWrap1Click(TObject *Sender)
{
Editor->WordWrap = !(Editor->WordWrap); // inverser le passage à la ligne
if (Editor->WordWrap)
Editor->ScrollBars = ssVertical;
// si passage à la ligne besoin seulement
// d’une verticale
else
Editor->ScrollBars = ssBoth;
// s’il n’a a pas passage à la ligne, il peut
// avoir besoin des deux
WordWrap1->Checked = Editor->WordWrap;
// Vérifier l’élément de menu pour être
// cohérent avec la propriété
}
Les composants mémo ou éditeur de texte formaté ne gèrent pas les barres de
défilement exactement de la même manière. Le composant éditeur de texte
formaté peut dissimuler ses barres de défilement si le texte ne sort pas des
limites du composant. Le composant Mémo affiche toujours les barres de
défilement lorsqu’elles ont été activées.
6-8
Guide du développeur
Manipulation du texte dans les contrôles
Ajout de l’objet Clipboard
La plupart des applications manipulant du texte permettent aux utilisateurs de
déplacer un texte sélectionné d’un document vers un autre, même s’il s’agit
d’une autre application. L’objet Clipboard de C++Builder encapsule un pressepapiers (comme le Presse-papiers de Windows) et inclut les méthodes permettant
de couper, de copier et de coller du texte (ainsi que d’autres formats, par
exemple les graphiques). L’objet Clipboard est déclaré dans l’unité Clipbrd.
Pour ajouter l’objet Clipboard à une application,
1 Sélectionnez l’unité utilisant le presse-papiers.
2 Dans le fichier .h de la fiche, ajoutez
#include <vcl\Clipbrd.hpp>
Sélection de texte
Pour transférer du texte d’un contrôle de saisie dans le presse-papiers, il faut
d’abord sélectionner ce texte. La possibilité de mettre en surbrillance le texte
sélectionné est intégrée aux composants éditeur. Lorsque l’utilisateur sélectionne
un texte, celui-ci apparaît en surbrillance.
Le tableau suivant dresse la liste des propriétés fréquemment utilisées pour la
manipulation du texte sélectionné.
Tableau 6.1 Propriétés du texte sélectionné
Propriété
Description
SelText
Contient une chaîne représentant le texte sélectionné dans le composant.
SelLength
Contient la longueur d’une chaîne sélectionnée.
SelStart
Contient la position de départ d’une chaîne relativement au début du
tampon de texte d’un contrôle de saisie.
Sélection de la totalité d’un texte
La méthode SelectAll sélectionne la totalité du texte présent dans le composant
mémo ou éditeur de texte formaté. C’est particulièrement utile quand le contenu
de l’éditeur dépasse la zone visible du composant. Dans les autres cas, les
utilisateurs peuvent sélectionner du texte à l’aide du clavier ou de la souris.
Pour sélectionner la totalité du contenu d’un contrôle de saisie comme le
composant mémo ou éditeur de texte formaté, appelez la méthode SelectAll du
contrôle RichEdit1.
Par exemple,
void __fastcall TMainForm::SelectAll(TObject *Sender)
{
RichEdit1->SelectAll();
// Sélectionner tout le texte du contrôle RichEdit1
}
Manipulation des contrôles
6-9
Manipulation du texte dans les contrôles
Couper, copier et coller du texte
Les applications utilisant l’unité Clipbrd peuvent couper, copier et coller du texte,
des graphiques et des objets, dans le presse-papiers. Les composants éditeur qui
encapsulent les contrôles de manipulation de texte standard disposent tous de
méthodes intégrées autorisant les interactions avec le presse-papiers. Pour plus
d’informations sur l’utilisation des graphiques et du presse-papiers, voir
“Utilisation du presse-papiers avec les graphiques” à la page 10-23.
Pour couper, copier ou coller du texte avec le presse-papiers, appelez
respectivement les méthodes CutToClipboard, CopyToClipboard et
PasteFromClipboard du composant.
Par exemple, le code suivant attache des gestionnaires aux événements OnClick
des commandes Edition | Couper, Edition | Copier et Edition | Coller :
void
{
}
void
{
}
void
{
}
__fastcall TMainForm::EditCutClick(TObject* Sender)
RichEdit1->CutToClipboard();
__fastcall TMainForm::EditCopyClick(TObject* Sender)
RichEdit1->CopyToClipboard();
__fastcall TMainForm::EditPasteClick(TObject* Sender)
RichEdit1->PasteFromClipboard();
Effacement du texte sélectionné
Vous pouvez effacer le texte sélectionné dans un éditeur sans le placer dans le
presse-papiers. Pour ce faire, appelez la méthode ClearSelection de l’éditeur. Par
exemple, s’il existe un élément Supprimer dans le menu Edition, votre code peut
ressembler à :
void __fastcall TMainForm::EditDeleteClick(TObject *Sender)
{
RichEdit1->ClearSelection();
}
Désactivation des éléments de menu
Il est souvent utile de désactiver des commandes de menus sans pour autant les
retirer du menu. Dans un éditeur de texte, par exemple, si aucun texte n’est
sélectionné, les commandes Couper, Copier et Supprimer du menu Edition sont
inapplicables. L’activation ou la désactivation des éléments de menu peut être
déclenchée lorsque l’utilisateur sélectionne le menu. Pour désactiver un élément
de menu, donnez la valeur false à sa propriété Enabled.
Dans l’exemple suivant, un gestionnaire est attaché à l’événement OnClick d’un
élément Edition appartenant à la barre de menu d’une fiche enfant. Il définit la
propriété Enabled des éléments Couper, Copier et Supprimer dans le menu
Edition, selon que du texte est sélectionné ou non dans le composant RichEdit1.
6-10
Guide du développeur
Manipulation du texte dans les contrôles
La commande Coller sera activée ou désactivée selon que le presse-papiers
contient ou non du texte.
void __fastcall TMainForm::EditEditClick(TObject *Sender)
{
// Active ou désactive l’élément de menu Coller
Paste1->Enabled = Clipboard()->HasFormat(CF_TEXT);
bool HasSelection = (RichEdit1->SelLength > 0); // true si du texte est sélectionné
Cut1->Enabled = HasSelection; // activer l’élément de menu si HasSelection vaut true
Copy1->Enabled = HasSelection;
Delete1->Enabled = HasSelection;
}
La méthode HasFormat du presse-papiers renvoie une valeur booléenne indiquant
si le presse-papiers contient des objets, du texte ou des images d’un format
particulier. En appelant HasFormat avec le paramètre CF_TEXT, vous pouvez
déterminer si le presse-papiers contient du texte, et activer ou désactiver
l’élément Coller selon le cas.
Pour plus d’informations sur l’utilisation du presse-Papiers avec des graphiques,
voir chapitre 10, “Utilisation des graphiques et du multimédia”.
Ajout d’un menu surgissant
Les menus surgissants (ou locaux) sont d’un usage courant et faciles à mettre en
œuvre dans toute sorte d’application. Ils réduisent le nombre d’opérations
nécessaires à la réalisation des tâches : en cliquant avec le bouton droit de la
souris sur l’espace de travail de l’application, l’utilisateur accède à une liste
regroupant les commandes les plus fréquemment utilisées.
Dans une application éditeur de texte, par exemple, vous pouvez ajouter un
menu surgissant qui comporte les commandes d’édition Couper, Copier et
Coller. Ces éléments de menu surgissant peuvent utiliser les mêmes gestionnaires
d’événements que les éléments correspondants du menu Edition. Il n’est pas
nécessaire de créer des raccourcis clavier, ni des touches raccourci pour les
menus surgissants, car les éléments des menus qui leur correspondent en
possèdent généralement.
La propriété PopupMenu d’une fiche indique quel menu surgissant doit s’afficher
lorsque l’utilisateur clique avec le bouton droit de la souris sur la fiche. Les
différents contrôles possèdent aussi leurs propriétés PopupMenu qui ont priorité
sur la propriété de la fiche, permettant de définir des menus personnalisés pour
des contrôles particuliers.
Pour ajouter un menu surgissant à une fiche,
1 Placez un composant menu surgissant sur la fiche.
2 Utilisez le concepteur de menus pour définir les éléments du menu surgissant.
3 Définissez par le nom du composant menu surgissant la propriété PopupMenu
de la fiche ou du contrôle devant faire apparaître le menu.
Manipulation des contrôles
6-11
Ajout de graphiques à des contrôles
4 Attachez les gestionnaires aux événements OnClick des éléments du menu
surgissant.
Gestion de l’événement OnPopup
Il peut être nécessaire de préparer certains éléments d’un menu surgissant avant
d’afficher celui-ci, comme vous devez spécifier les éléments activés ou désactivés
d’un menu normal. Avec un menu normal, l’événement OnClick correspondant à
l’affichage du menu est généralement associé au titre de ce menu, comme décrit
dans la section “Désactivation des éléments de menu” à la page 6-10.
Comme les menus surgissants n’ont pas de barre de menu, vous devez gérer
l’événement dans le composant lui-même. Le composant menu surgissant offre
pour cela un événement particulier appelé OnPopup.
Pour préparer des éléments d’un menu surgissant avant de les afficher,
1 Sélectionnez le composant menu surgissant.
2 Attachez un gestionnaire à son événement OnPopup.
3 Ecrivez dans le gestionnaire d’événement le code activant, désactivant,
dissimulant ou affichant les éléments du menu.
Dans le code suivant, un gestionnaire existant pour l’événement Edit1Click décrit
précédemment dans la section “Désactivation des éléments de menu” à la
page 6-10, est attaché à l’événement OnPopup du composant menu surgissant.
Une ligne de code est ajoutée à Edit1Click pour chaque élément du menu
surgissant.
void __fastcall TMainForm::EditEditClick(TObject *Sender)
{
// activer ou désactiver l’élément de menu Coller
Paste1->Enabled = Clipboard()->HasFormat(CF_TEXT);
Paste2->Enabled = Paste1->Enabled;
// Ajouter cette ligne
bool HasSelection = (RichEdit1->SelLength > 0); // true si le texte est sélectionné
Cut1->Enabled = HasSelection;
// activer l’élément de menu si HasSelection vaut true
Cut2->Enabled = HasSelection;
// ajouter cette ligne
Copy1->Enabled = HasSelection;
Copy2->Enabled = HasSelection; // ajouter cette ligne
Delete1->Enabled = HasSelection;
}
Ajout de graphiques à des contrôles
Plusieurs contrôles permettent de personnaliser la manière dont le contrôle est
restitué. Ce sont les boîtes liste, boîtes à options, menus, en-têtes, contrôles
onglets, vues liste, barres d’état, vues arborescentes et barres d’état. Au lieu
d’utiliser la méthode standard dessinant un contrôle ou chacun de ses éléments,
le propriétaire du contrôle (généralement la fiche) dessine ces éléments en mode
exécution. L’utilisation la plus courante de ces contrôles dessinés par le
propriétaire est de remplacer le texte par des dessins ou d’ajouter des dessins au
6-12
Guide du développeur
Ajout de graphiques à des contrôles
texte des éléments. Pour des informations sur l’utilisation du style “dessiné par
le propriétaire” pour ajouter des images aux menus, voir “Ajout d’images à des
éléments de menu” à la page 8-39.
Les contrôles dessinés par le propriétaire ont un point commun : ils contiennent
tous des listes d’éléments. Généralement, il s’agit de listes de chaînes qui sont
affichées sous forme de texte ou de liste d’objets contenant des chaînes qui sont
affichées sous forme de texte. Il est possible d’associer un objet à chaque élément
de ces listes et d’utiliser l’objet lorsque vous dessinez un élément.
Dans C++Builder, la création d’un contrôle dessiné par le propriétaire se fait
généralement en trois étapes :
1 Spécification du style dessiné par le propriétaire
2 Ajout d’objets graphiques à une liste de chaînes
3 Dessiner des éléments dessinés par le propriétaire
Spécification du style dessiné par le propriétaire
Pour personnaliser le dessin d’un contrôle, vous devez spécifier des gestionnaires
d’événements qui restituent l’image du contrôle quand il doit être dessiné.
Certains contrôles reçoivent automatiquement ces événements. Par exemple, les
vues liste ou arborescentes et les barres d’outils reçoivent les événements aux
diverses étapes du processus de dessin sans avoir à définir la moindre propriété.
Ces événements ont des noms de la forme “OnCustomDraw” ou
“OnAdvancedCustomDraw”.
D’autres contrôles nécessitent l’initialisation d’une propriété avant de recevoir
les événements de dessin personnalisé. Les boîtes liste, les boîtes à options, les
en-têtes et les barres d’état ont une propriété appelée Style. La propriété Style
détermine si le contrôle utilise le dessin par défaut (appelé style “standard”) ou
bien le dessin effectué par le propriétaire. Les grilles utilisent une propriété
appelée DefaultDrawing qui permet d’activer ou de désactiver le dessin par
défaut. Les vues listes et les contrôles onglets ont une propriété appelée
OwnerDraw qui active ou désactive le dessin par défaut.
Pour les boîtes liste et les boîtes à options, il y a plusieurs styles dessinés par
le propriétaire, appelés fixed et variable, comme décrit dans le tableau suivant.
Les autres contrôles sont toujours “fixes” : bien que la taille de l’élément
contenant du texte soit variable, la taille de chaque élément est fixée avant le
dessin du contrôle.
Tableau 6.2 Comparaison entre les styles “fixed” et “variable”
Styles dessinés
par le propriétaire
Signification
Exemples
Fixed
Chaque élément est de la même hauteur,
déterminée par la propriété ItemHeight.
lbOwnerDrawFixed,
csOwnerDrawFixed
Variable
Chaque élément peut avoir une hauteur
différente qui dépend des données au
moment de l’exécution.
lbOwnerDrawVariable,
csOwnerDrawVariable
Manipulation des contrôles
6-13
Ajout de graphiques à des contrôles
Ajout d’objets graphiques à une liste de chaînes
Toute liste de chaînes est capable de contenir une liste d’objets en plus de sa
liste de chaînes.
Dans une application de gestion de fichiers, par exemple, vous devez ajouter un
bitmap indiquant le type du lecteur à la lettre le désignant. Pour cela, vous
devez ajouter les images bitmap à l’application, puis les copier à l’endroit
approprié dans la liste de chaînes, comme le décrivent les sections suivantes.
Ajout d’images à une application
Un contrôle image est un contrôle non visuel qui contient une image graphique
(un bitmap, par exemple). Les contrôles image servent à afficher des images
graphiques sur une fiche, mais vous pouvez aussi les utiliser pour stocker des
images cachées que vous utiliserez dans votre application. Par exemple, il est
possible de stocker des images bitmap pour les contrôles dessinés par le
propriétaire dans des contrôles image cachés, comme décrit ci-dessous :
1 Ajoutez des contrôles image à la fiche principale.
2 Définissez leurs propriétés Name.
3 Donnez la valeur false à la propriété Visible de chaque contrôle image.
4 Définissez la propriété Picture de chaque contrôle image par le bitmap
souhaité en utilisant l’éditeur d’image depuis l’inspecteur d’objets.
Les contrôles image seront invisibles lorsque vous exécuterez l’application.
Ajout d’images à une liste de chaînes
Une fois que vous avez des images graphiques dans une application, vous
pouvez les associer aux chaînes de la liste. Vous pouvez soit ajouter les objets
en même temps que les chaînes, soit les associer à des chaînes qui ont déjà été
ajoutées. Si vous disposez de toutes les données dont vous avez besoin, vous
ajouterez sans doute les chaînes et les objets en même temps.
L’exemple suivant montre comment ajouter des images à une liste de chaînes.
Ce code est extrait d’une application de gestion de fichiers dans laquelle chaque
lecteur correct est représenté par une lettre et est associé à un bitmap indiquant
le type du lecteur. L’événement OnCreate se présente comme suit :
void __fastcall TFMForm::FormCreate(TObject *Sender)
{
int AddedIndex;
char DriveName[4] = "A:\\";
for (char Drive = ’A’; Drive <= ’Z’; Drive++) // Essayer tous les lecteurs
{
DriveName[0] = Drive;
switch (GetDriveType(DriveName))
{
case DRIVE_REMOVABLE:// Ajouter un élément à la liste
DriveName[1] = ’\0’; // convertir la lettre du lecteur en chaîne
AddedIndex = DriveList->Items->AddObject(DriveName,
6-14
Guide du développeur
Ajout de graphiques à des contrôles
Floppy->Picture->Graphic);
DriveName[1] = ’:’ // Remplacer le deux points
break;
case DRIVE_FIXED:// Ajouter un élément à la liste
DriveName[1] = ’\0’; // convertir la lettre du lecteur en chaîne
AddedIndex = DriveList->Items->AddObject(DriveName,
Fixed->Picture->Graphic);
DriveName[1] = ’:’ // Remplacer le deux points
break;
case DRIVE_REMOTE:// Ajouter un élément à la liste
DriveName[1] = ’\0’; // convertir la lettre du lecteur en chaîne
AddedIndex = DriveList->Items->AddObject(DriveName,
Network->Picture->Graphic);
DriveName[1] = ’:’ // Remplacer le deux points
break;
}
if ((int)(Drive - ’A’) == getdisk()) // lecteur en cours?
DriveList->ItemIndex = AddedIndex; // en faire l’élément en cours de la liste
}
}
Dessiner des éléments dessinés par le propriétaire
Lorsque vous avez spécifié qu’un contrôle est dessiné par le propriétaire
(en initialisant une propriété ou en définissant un gestionnaire d’événement),
le contrôle n’est plus dessiné à l’écran. Au lieu de cela, le système d’exploitation
génère un événement pour chaque élément visible du contrôle. C’est votre
application qui gère ces événements et dessine les éléments.
Pour dessiner les éléments d’un contrôle dessiné par le propriétaire, suivez les
étapes indiquées ci-après. Ces étapes se répètent pour chaque élément visible
du contrôle, mais vous utiliserez le même gestionnaire d’événement pour tous.
1 Le cas échéant, dimensionnez l’élément.
Si les éléments sont tous de même taille (par exemple, avec un style de boîte liste
lsOwnerDrawFixed), cette opération n’est pas nécessaire.
2 Dessinez l’élément.
Dimensionnement des éléments dessinés par le propriétaire
Avant de laisser votre application dessiner chaque élément d’un contrôle de taille
variable lorsqu’il est dessiné par le propriétaire, le système d’exploitation génère
un événement de type measure-item. Cet événement indique à l’application
l’endroit où l’élément apparaîtra sur le contrôle.
C++Builder détermine la taille probable de l’élément (généralement juste assez
grand pour afficher le texte de l’élément dans la police de caractères active).
Votre application peut gérer l’événement et modifier la zone rectangle choisie.
Par exemple, si vous comptez remplacer le texte de l’élément par une image
bitmap, vous modifierez le rectangle pour qu’il soit de la taille du bitmap.
Manipulation des contrôles
6-15
Ajout de graphiques à des contrôles
Si vous voulez avoir à la fois l’image et le texte, vous ajusterez la taille du
rectangle pour qu’il puisse contenir les deux.
Pour changer la taille d’un élément dessiné par le propriétaire, attachez un
gestionnaire à l’événement measure-item dans le contrôle dessiné par le
propriétaire. Le nom de l’événement peut varier en fonction du contrôle. Les
boîtes liste et les boîtes à options utilisent OnMeasureItem. Les grilles n’ont pas ce
type d’événement.
L’événement définissant la taille utilise deux paramètres importants : l’indice et
la taille de l’élément. Cette taille est variable : l’application peut l’augmenter ou
la diminuer. La position des éléments suivants dépend de la taille des éléments
précédents.
Par exemple, dans une boîte liste variable dessinée par le propriétaire, si
l’application définit la hauteur du premier élément à cinq pixels, le second
élément commence au sixième pixel depuis le haut, et ainsi de suite. Dans les
boîtes liste et dans les boîtes à options, le seul aspect des éléments que
l’application puisse changer est la hauteur. La largeur de l’élément est toujours
celle du contrôle.
Les grilles dessinées par le propriétaire ne peuvent pas modifier la taille des
cellules au fur et à mesure qu’elles sont dessinées. En effet, la taille des lignes et
des colonnes est définie avant le dessin par les propriétés ColWidths et
RowHeights.
Le code suivant, attaché à l’événement OnMeasureItem du composant boîte liste
dessinée par le propriétaire, augmente la hauteur de chaque élément de liste
pour permettre de placer l’image bitmap associée.
void __fastcall TForm1::ListBox1MeasureItem(TWinControl *Control, int Index,
// Remarquez que Height est transmis par adresse
int &Height)
{
int BitmapHeight = ((TBitmap *)ListBox1->Items->Objects[Index])->Height + 2;
// S’assurer que l’élément de la liste est assez haut pour le bitmap (plus 2)
if (BitmapHeight > Height)
Height = BitmapHeight;
}
Remarque
Vous devez transtyper les éléments à partir de la propriété Objects dans la liste
de chaînes. Objects est une propriété de type TObject, aussi peut-elle contenir
n’importe quel type d’objet. Lorsque vous extrayez un objet d’un tableau, vous
devez le transtyper afin qu’il reprenne le type des éléments.
Dessin des éléments par le propriétaire
Lorsqu’une application doit dessiner ou redessiner un contrôle dessiné par le
propriétaire, le système d’exploitation génère un événement de type draw-item
pour chaque élément visible du contrôle. Selon le contrôle, l’élément peut
également recevoir les événements de dessin pour l’élément pris comme un tout
ou comme sous-éléments.
6-16
Guide du développeur
Ajout de graphiques à des contrôles
Pour dessiner chaque élément d’un contrôle dessiné par le propriétaire, attachez
un gestionnaire à l’événement draw-item de ce contrôle.
Les noms des événements relatifs aux objets dessinés par le propriétaire
commencent généralement par :
• OnDraw, comme OnDrawItem ou OnDrawCell
• OnCustomDraw, comme OnCustomDrawItem
• OnAdvancedCustomDraw, comme OnAdvancedCustomDrawItem
L’événement draw-item contient des paramètres identifiant l’élément à dessiner, le
rectangle dans lequel il s’inscrit et, habituellement, des informations sur son état
(actif, par exemple). L’application gère chaque événement en plaçant l’élément
approprié dans le rectangle transmis.
Par exemple, le code suivant montre comment dessiner des éléments dans une
boîte liste ayant un bitmap associé à chaque chaîne. Il attache ce gestionnaire à
l’événement OnDrawItem :
void __fastcall TForm1::ListBox1DrawItem(TWinControl *Control, int Index,
TRect &Rect, TOwnerDrawState State)
TBitmap *Bitmap = (TBitmap *)ListBox1->Items->Objects[Index];
ListBox1->Canvas->Draw(R.Left, R.Top + 2, Bitmap); // dessiner le bitmap
ListBox1->Canvas->TextOut(R.Left + Bitmap->Width + 2, R.Top + 2,
ListBox1->Items->Strings[Index]);
// et écrire le texte à sa droite
}
Manipulation des contrôles
6-17
6-18
Guide du développeur
Chapitre
7
Création d’applications,
de composants et de bibliothèques
Chapitre 7
Ce chapitre donne un aperçu de la manière d’utiliser C++Builder pour créer des
applications, des bibliothèques et des composants.
Création d’applications
L’utilisation principale de C++Builder est la conception et la génération des types
d’applications suivants :
•
•
•
•
Les applications d’interface utilisateur graphique
Les applications console
Les applications service (pour les applications Windows seulement)
Paquets et DLL
Les applications d’interface utilisateur graphique (GUI) ont en général une
interface qui facilite leur utilisation. Les applications console s’exécutent dans
une fenêtre console. Les applications service s’exécutent en tant que services
Windows. Ces applications sont compilées en tant qu’exécutables, avec du code
de démarrage.
Vous pouvez créer d’autres types de projets, comme les paquets et les DLL,
bibliothèques de liaison dynamique. Ces applications produisent du code
exécutable sans code de démarrage. Reportez-vous à “Création de paquets et de
DLL” à la page 7-10.
Applications d’interface utilisateur graphique
Une application d’interface utilisateur graphique, GUI, est une application conçue
en utilisant des fonctionnalités graphiques, fenêtres, menus, boîtes de dialogue,
et des fonctionnalités qui rendent cette application facile à utiliser. Quand vous
Création d’applications, de composants et de bibliothèques
7-1
Création d’applications
compilez une application GUI, un fichier exécutable contenant du code de
démarrage est créé. Généralement, l’exécutable fournit les fonctions de base de
votre programme et les programmes simples sont souvent composés uniquement
d’un fichier exécutable. Vous pouvez aussi étendre une application en appelant
des DLL, des paquets ou d’autres bibliothèques complétant un exécutable.
C++Builder offre deux modèles d’interface utilisateur d’application :
• L’interface de document unique (abrégé en anglais par SDI)
• L’interface de document multiple (abrégé en anglais par MDI)
Outre le modèle d’implémentation de votre application, le comportement de
votre projet à la conception, comme celui de l’application à l’exécution, peut être
manipulé par des options de projet de l’EDI.
Modèles d’interfaces utilisateur
Toute fiche peut être implémentée comme une fiche d’interface de document
multiple (MDI) ou comme une fiche d’interface de document unique (SDI). Dans
une application MDI, plusieurs documents ou fenêtres enfant peuvent être
ouverts dans une seule fenêtre parent. Cela est courant dans les applications
comme les tableurs ou les traitements de texte. Par contre, une application SDI
ne contient normalement qu’une seule vue de document. Pour faire de votre
fiche une application SDI, affectez la valeur fsNormal à la propriété FormStyle de
votre objet Form.
Pour davantage d’informations sur le développement de l’interface utilisateur
d’une application, voir chapitre 8, “Conception de l’interface utilisateur des
applications”.
Applications SDI
Pour créer une nouvelle application SDI :
1 Sélectionnez Fichier|Nouveau|Autre pour afficher la boîte de dialogue
Nouveaux éléments.
2 Cliquez sur l’onglet Projets et double-cliquez sur Application SDI.
3 Cliquez sur OK.
Par défaut, la propriété FormStyle de l’objet Form a la valeur fsNormal,
C++Builder suppose que toute nouvelle application est une application SDI.
Applications MDI
Pour créer une nouvelle application MDI :
1 Sélectionnez Fichier|Nouveau|Autre pour afficher la boîte de dialogue
Nouveaux éléments.
2 Cliquez sur l’onglet Projets et double-cliquez sur Application MDI.
3 Cliquez sur OK.
Les applications MDI nécessitent plus de réflexion et sont plus complexes à
concevoir que les applications SDI. Les applications MDI contiennent des fenêtres
7-2
Guide du développeur
Création d’applications
enfant qui se trouvent dans la fenêtre client ; la fiche principale contient des
fiches enfant. Affectez la propriété FormStyle de l’objet TForm pour spécifier si la
fiche est un enfant (fsMDIForm) ou si c’est la fiche principale (fsMDIChild). Pour
éviter d’avoir à redéfinir à plusieurs reprises les propriétés des fenêtres enfant,
vous avez intérêt à définir une classe de base pour les fiches enfant et à dériver
chaque fiche enfant de cette classe.
Les applications MDI proposent souvent des options du menu principal comme
Cascade et Mosaïque pour afficher plusieurs fenêtres de diverses manières.
Quand une fenêtre enfant est réduite, son icône est placée dans la fenêtre parent
MDI.
Pour résumer, pour créer les fenêtres d’une application MDI :
1 Créez la fenêtre principale, ou fenêtre parent MDI. Initialisez sa propriété
FormStyle à fsMDIForm.
2 Créez un menu pour la fenêtre principale proposant les options Fichier|
Ouvrir, Fichier|Enregistrer et un menu Fenêtre proposant les options Cascade,
Mosaïque et Réorganiser.
3 Créez les fiches enfant MDI et initialisez leur propriété FormStyle à fsMDIChild.
Définition des options de l’EDI, du projet et de la compilation
Choisissez Projet|Options pour spécifier les diverses options de votre projet.
Pour plus d’informations, voir l’aide en ligne.
Définition des options de projet par défaut
Pour modifier les options de projet par défaut qui s’appliquent à tout nouveau
projet, définissez les options de la boîte de dialogue Options de projet, puis
cochez la case Défaut en bas à droite de la fenêtre. Tous les nouveaux projets
utiliseront ensuite les options en cours comme options par défaut.
Modèles de programmation
Les modèles de programmation sont des structures communément appelées
squelettes que vous pouvez ajouter au code source puis remplir. Certains modèles
de code standard, comme les déclarations de tableaux, de classes ou de fonction,
ainsi que de nombreuses instructions, sont livrés avec C++Builder.
Vous pouvez aussi écrire vos propres modèles de code pour les structures que
vous utilisez souvent. Par exemple, si vous voulez utiliser une boucle for dans
votre code, insérez le modèle suivant :
for (; ;)
{
}
Pour insérer un modèle de code dans l’éditeur de code, appuyez sur Ctrl-j et
sélectionnez le modèle que vous voulez utiliser. Vous pouvez ajouter vos propres
modèles à cette collection.
Création d’applications, de composants et de bibliothèques
7-3
Création d’applications
Pour ajouter un modèle :
1 Choisissez Outils|Options de l’éditeur.
2 Choisissez l’onglet Audit de code.
3 Dans la section Modèles, choisissez Ajouter.
4 Saisissez le nom du modèle après Raccourci et entrez une description brève
du nouveau modèle, puis cliquez sur OK.
5 Ajoutez le modèle de code dans la boîte de saisie Code.
6 Cliquez sur OK.
Applications console
Les applications console sont des programmes 32 bits exécutés sans interface
graphique, généralement dans une fenêtre console. Habituellement, ces
applications ne nécessitent pas une saisie utilisateur importante et accomplissent
un jeu limité de fonctions.
Pour créer une nouvelle application console :
1 Choisissez Fichier|Nouveau|Autre puis double-cliquez sur Expert Console
dans la boîte de dialogue Nouveaux éléments.
2 Dans la boîte de dialogue Expert console, cochez l’option Application console,
choisissez le type de source (C ou C++) de la fiche principale du projet ou
spécifiez un fichier préexistant contenant une fonction main ou winmain, puis
choisissez le bouton OK.
C++Builder crée alors un fichier projet pour le type de fichier source spécifié et
affiche l’éditeur de code.
Utilisation de la VCL et de la CLX dans les applications console
Remarque
A la création d’une nouvelle application console, l’EDI ne crée pas une nouvelle
fiche. Seul l’éditeur de code apparaît.
Vous pouvez néanmoins utiliser des objets VCL et CLX dans les applications
console. Pour ce faire, vous devez spécifier dans l’expert Console que vous
utiliserez la VCL ou la CLX (cochez l’option Utiliser VCL ou Utiliser CLX). Si
vous ne le faites pas, vous ne pourrez pas utiliser ultérieurement les classes de la
VCL ou de la CLX dans cette application. Sinon, des erreurs du lieur se
produiront.
Les applications console doivent gérer toutes les exceptions afin d’empêcher des
fenêtres d’afficher une boîte de dialogue lors de l’exécution.
Applications service
Les applications service reçoivent les requêtes des applications client, traitent ces
requêtes et renvoient les informations aux applications client. Habituellement,
elles s’exécutent en arrière-plan, sans nécessiter de saisie utilisateur importante.
7-4
Guide du développeur
Création d’applications
Un serveur Web, FTP ou de messagerie électronique est un exemple
d’application service.
Pour créer une application qui implémente un service Win32 :
1 Choisissez Fichier|Nouveau|Autre et double-cliquez sur Application Service
dans la boîte de dialogue Nouveaux éléments. Cela ajoute à votre projet une
variable globale appelée Application de type TServiceApplication.
2 La fenêtre Service qui apparaît correspond à un service (TService).
Implémentez le service en initialisant ses propriétés et ses gestionnaires
d’événements dans l’inspecteur d’objets.
3 Vous pouvez ajouter des services supplémentaires en choisissant Fichier|
Nouveau|Autre et en double-cliquant sur Service dans la boîte de dialogue
Nouveaux éléments. N’ajoutez pas de services à une application qui n’est pas
une application service. En effet, même si un objet TService est ajouté,
l’application ne génère pas les événements nécessaires, ni ne fait les appels
Windows appropriés au service.
4 Une fois que votre application service est construite, vous pouvez installer ses
services avec le SCM (Service Control Manager). Les autres applications
peuvent alors lancer vos services en envoyant des requêtes au SCM.
Pour installer les services de votre application, exécutez-la à l’aide de l’option /
INSTALL. L’application installe ses services puis quitte, en affichant un message
de confirmation si les services sont correctement installés. Vous pouvez
supprimer l’affichage du message de confirmation en exécutant l’application
service à l’aide de l’option /SILENT.
Pour désinstaller les services de votre application, exécutez-la depuis la ligne de
commande à l’aide de l’option /UNINSTALL. (Vous pouvez aussi utiliser
l’option /SILENT pour supprimer le message de confirmation lors de la
désinstallation).
Exemple
Le service suivant contient un TServerSocket dont le port est initialisé à 80. C’est
le port par défaut des navigateurs Web pour envoyer des requêtes à des
serveurs Web et celui utilisé par les serveurs Web pour répondre aux
navigateurs Web. Cet exemple spécifique produit, dans le répertoire C:\Temp,
un document texte appelé WebLogxxx.log (où xxx correspond au ThreadID). Il
ne doit y avoir qu’un seul serveur surveillant un port donné, donc si vous
utilisez déjà un serveur Web, vous devez vous assurer qu’il n’est pas à l’écoute
(le service doit être arrêté).
Pour voir les résultats : ouvrez un navigateur Web sur la machine locale, et pour
l’adresse, entrez “localhost” (sans les guillemets). Eventuellement, le navigateur
va faire une erreur de dépassement de délai mais vous devez obtenir un fichier
appelé Weblogxxx.log dans le répertoire C:\Temp.
1 Pour créer l’exemple, choisissez Fichier|Nouveau|Autre et sélectionnez
Application Service dans la boîte de dialogue Nouveaux éléments. La fenêtre
Service1 apparaît.
2 Ajoutez un composant ServerSocket de la page Internet de la palette de
composants à la fenêtre service (Service1).
Création d’applications, de composants et de bibliothèques
7-5
Création d’applications
3 Ajoutez une donnée membre privée de type TMemoryStream à la classe
TService1. L’en-tête de l’unité doit ressembler à :
//--------------------------------------------------------------------------#ifndef Unit1H
#define Unit1H
//--------------------------------------------------------------------------#include <SysUtils.hpp>
#include <Classes.hpp>
#include <SvcMgr.hpp>
#include <ScktComp.hpp>
//--------------------------------------------------------------------------class TService1 : public TService
{
__published:// IDE-managed Components
TServerSocket *ServerSocket1;
private:// Déclarations utilisateur
TMemoryStream *Stream; // ajoutez cette ligne
public:// Déclarations utilisateur
__fastcall TService1(TComponent* Owner);
PServiceController __fastcall GetServiceController(void);
friend void __stdcall ServiceController(unsigned CtrlCode);
};
//--------------------------------------------------------------------------extern PACKAGE TService1 *Service1;
//--------------------------------------------------------------------------#endif
4 Sélectionnez ServerSocket1, le composant ajouté à l’étape 1. Dans l’inspecteur
d’objets, double-cliquez sur l’événement OnClientRead et ajoutez le gestionnaire
d’événement suivant :
void __fastcall TService1::ServerSocket1ClientRead(TObject *Sender,
TCustomWinSocket *Socket)
{
char *Buffer = NULL;
int len = Socket->ReceiveLength();
while (len > 0)
{
try
{
Buffer = (char *)malloc(len);
Socket->ReceiveBuf((void *)Buffer, len);
Stream->Write(Buffer, len);
}
__finally
{
free(Buffer);
}
Stream->Seek(0, soFromBeginning);
AnsiString LogFile = "C:\\Temp\\WebLog";
LogFile = LogFile + IntToStr(ServiceThread->ThreadID) + ".log";
Stream->SaveToFile(LogFile);
}
}
7-6
Guide du développeur
Création d’applications
5 Sélectionnez enfin Service1 en cliquant sur la zone client de la fenêtre (mais
pas sur le composant ServiceSocket). Dans l’inspecteur d’objets, double-cliquez
sur l’événement OnExecute et ajoutez le gestionnaire d’événement suivant :
void __fastcall TService1::Service1Execute(TService *Sender)
{
Stream = new TMemoryStream();
try
{
ServerSocket1->Port = 80; // port WWW
ServerSocket1->Active = true;
while (!Terminated)
ServiceThread->ProcessRequests(true);
ServerSocket1->Active = false;
}
__finally
{
delete Stream;
}
}
Quand vous écrivez votre application service, vous devez tenir compte des
éléments suivants :
• Threads de service
• Propriétés de nom d’un service
• Débogage d’applications service
Remarque
Les applications service fonctionnement uniquement sous Windows.
Threads de service
Chaque service dispose de son propre thread (TServiceThread), donc si votre
application service implémente plusieurs services, vous devez vous assurer que
l’implémentation de vos services est compatible avec l’utilisation de threads. La
classe TServiceThread est ainsi conçue de façon à implémenter le service dans le
gestionnaire d’événement OnExecutede TService. Le thread du service dispose de
sa propre méthode Execute qui contient une boucle appelant les gestionnaires
OnStart et OnExecute du service avant de traiter de nouvelles requêtes.
Comme le traitement des requêtes de service peut prendre longtemps et que
l’application service peut recevoir simultanément plusieurs requêtes d’un ou de
plusieurs clients, il est plus efficace de lancer un nouveau thread (dérivé de
TThread et non de TServiceThread) pour chaque requête et de déplacer
l’implémentation du service dans la méthode Execute du nouveau thread. Cela
permet à la boucle Execute du thread du service de traiter continuellement de
nouvelles requêtes sans avoir à attendre la fin du gestionnaire OnExecute du
service. L’exemple suivant en est une illustration.
Exemple
Ce service sonne tous les 500 millisecondes depuis le thread standard. Il gère la
pause, la reprise et l’arrêt du thread quand on indique au service de se
suspendre, de reprendre ou de s’arrêter.
1 Choisissez Fichier|Nouveau|Autre et double-cliquez sur Application Service
dans la boîte de dialogue Nouveaux éléments. La fenêtre Service1 apparaît.
Création d’applications, de composants et de bibliothèques
7-7
Création d’applications
2 Dans le fichier en-tête de l’unité, déclarez un nouveau descendant de TThread
nommé TSparkyThread. C’est le thread qui réalise le travail pour le service.
Il doit être déclaré comme suit :
class TSparkyThread : public TThread
{
private:
protected:
void __fastcall Execute();
public:
__fastcall TSparkyThread(bool CreateSuspended);
};
3 Dans le fichier .cpp de l’unité, créez une variable globale pour une instance de
TSparkyThread :
TSparkyThread *SparkyThread;
4 Ajoutez le code suivant au fichier .cpp pour le constructeur de
TSparkyThread :
__fastcall TSparkyThread::TSparkyThread(bool CreateSuspended)
: TThread(CreateSuspended)
{
}
5 Ajoutez le code suivant au fichier .cpp pour la méthode Execute de
TSparkyThread (la fonction thread) :
void __fastcall TSparkyThread::Execute()
{
while (!Terminated)
{
Beep();
Sleep(500);
}
}
6 Sélectionnez la fenêtre service (Service1) et double-cliquez sur l’événement
OnStart dans l’inspecteur d’objets. Ajoutez le gestionnaire d’événement
OnStart suivant :
void __fastcall TService1::Service1Start(TService *Sender, bool &Started)
{
SparkyThread = new TSparkyThread(false);
Started = true;
}
7 Double-cliquez sur l’événement OnContinue dans l’inspecteur d’objets. Ajoutez
le gestionnaire d’événement OnContinue suivant :
void __fastcall TService1::Service1Continue(TService *Sender, bool &Continued)
{
SparkyThread->Resume();
Continued = true;
}
7-8
Guide du développeur
Création d’applications
8 Double-cliquez sur l’événement OnPause dans l’inspecteur d’objets. Ajoutez le
gestionnaire d’événement OnPause suivant :
void __fastcall TService1::Service1Pause(TService *Sender, bool &Paused)
{
SparkyThread->Suspend();
Paused = true;
}
9 Enfin, double-cliquez sur l’événement OnStop dans l’inspecteur d’objets.
Ajoutez le gestionnaire d’événements OnStop suivant :
void __fastcall TService1::Service1Stop(TService *Sender, bool &Stopped)
{
SparkyThread->Terminate();
Stopped = true;
}
Dans le cadre du développement d’applications serveur, la décision de lancer un
nouveau thread dépend de la nature du service rendu, du nombre prévu de
connexions et du nombre prévu de processeurs dont dispose la machine
exécutant le service.
Propriétés de nom d’un service
La VCL propose des classes permettant de créer des applications service sur le
plate-forme Windows (non disponible pour les applications multiplates-formes).
Il s’agit de TService et de TDependency. Quand vous utilisez ces classes, les
diverses propriétés de nom peuvent être source de confusion. Cette section décrit
leurs différences.
Les services ont des noms d’utilisateur (appelés Nom de démarrage du service)
qui sont associés à des mots de passe, des noms d’affichage utilisés pour
l’affichage dans les fenêtres gestionnaire et éditeur et des noms réels (le nom du
service). Les dépendances peuvent être des services ou des groupes d’ordre de
chargement. Elles ont également des noms et des noms d’affichage. De plus,
comme les objets service dérivent de TComponent, ils héritent de la propriété
Name. Les paragraphes suivants décrivent ces diverses propriétés de nom.
Propriétés de TDependency
La propriété DisplayName de TDependency est à la fois le nom d’affichage et le
nom réel du service. Elle est presque toujours identique à la propriété Name
TDependency.
Propriétés de nom de TService
La propriété Name de TService est héritée de TComponent. C’est le nom du
composant et également le nom du service. Pour les dépendances qui sont des
services, cette propriété est identique aux propriétés Name et DisplayName de
TDependency.
TService::DisplayName est le nom affiché dans la fenêtre du gestionnaire de
service. Il diffère souvent du nom réel du service (TService::Name,
TDependency::DisplayName, TDependency::Name). Remarquez que généralement le
Création d’applications, de composants et de bibliothèques
7-9
Création de paquets et de DLL
nom d’affichage (DisplayName) n’est pas le même pour le service et pour la
dépendance.
Les noms de démarrage de service sont distincts du nom d’affichage et du nom
réel du service. Un ServiceStartName est la valeur saisie du nom d’utilisateur
dans la boîte de dialogue de démarrage sélectionnée depuis le gestionnaire de
contrôle de service.
Débogage d’applications service
Vous pouvez déboguer les applications service en attachant le processus de
l’application service lors de son exécution (c’est-à-dire en commençant par
démarrer le service puis en l’attachant au débogueur). Pour effectuer
l’attachement au processus d’application service, choisissez Exécuter|Attacher au
processus et sélectionnez l’application service dans la boîte de dialogue
résultante.
Dans certains cas, cette approche peut échouer en raison de droits insuffisants.
Si cela se produit, vous pouvez utiliser le gestionnaire de contrôle de service
pour permettre à votre service de fonctionner avec le débogueur :
1 Créez une clé (options d’exécution du fichier image) dans l’emplacement de
registre suivant :
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion
2 Créez une sous-clé portant le même nom que votre service (par exemple,
MONSERV.EXE). Ajoutez à cette sous-clé une valeur de type REG_SZ,
nommée Debugger. Utilisez le chemin complet de BCB.exe comme valeur de
chaîne.
3 Dans l’applet Services du panneau de configuration, sélectionnez votre service,
cliquez sur l’option de démarrage et activez l’option permettant au service
d’interagir avec le bureau.
Avec les systèmes Windows NT, vous pouvez utiliser une autre approche pour
déboguer les applications service. Cependant cette approche peut être difficile car
elle nécessite des intervalles de temps courts :
1 Lancez d’abord l’application dans le débogueur. Patientez quelques secondes
jusqu’à la fin du chargement.
2 Démarrez rapidement le service à partir du panneau de configuration ou de la
ligne de commande :
start MyServ
Vous devez lancer le service rapidement (dans les 15 à 30 secondes du
démarrage de l’application) car l’application se terminera si aucun service n’est
lancé.
Création de paquets et de DLL
Les bibliothèques de liaison dynamique (DLL) sont des modules de code
compilés qui fonctionnent en conjonction avec un exécutable pour proposer des
7-10
Guide du développeur
Création de paquets et de DLL
fonctionnalités à une application. Vous pouvez créer des DLL dans des
programmes multiplates-formes. Cependant, sous Linux, les DLL et les paquets
sont recompilés en tant qu’objets partagés.
Les paquets sont des DLL spéciales utilisées par les applications C++Builder, par
l’EDI ou les deux à la fois. Il y a deux sortes de paquets : les paquets d’exécution
et les paquets de conception. Les paquets d’exécution fournissent des
fonctionnalités à un programme lors de son exécution. Les paquets de conception
permettent d’étendre les fonctionnalités de l’EDI.
Les DLL et les bibliothèques doivent gérer toutes les exceptions pour empêcher
l’affichage d’erreurs ou d’avertissements dans des boîtes de dialogue Windows.
Les directives du compilateur suivantes peuvent être placées dans les fichiers
projet bibliothèque :
Tableau 7.1 Directives de compilation pour les bibliothèques
Directive
de compilation
Description
{$LIBPREFIX ’chaîne’}
Ajoute le préfixe spécifié au nom du fichier de destination. Par
exemple, vous pouvez spécifier {$LIBPREFIX ’dcl’} pour un paquet
de conception, ou utiliser {$LIBPREFIX ’ ’} pour éliminer totalement
le préfixe.
{$LIBSUFFIX ’chaîne}
Ajoute le suffixe spécifié au nom du fichier de destination, avant
l’extension. Par exemple, utilisez {$LIBSUFFIX ’-2.1.3’} dans
quelquechose.cpp pour générer quelquechose-2.1.3.bpl.
{$LIBVERSION
’chaîne’}
Ajoute une seconde extension au nom du fichier de destination
après l’extension .bpl. Par exemple, utilisez {$LIBVERSION ’2.1.3’}
dans quelquechosecpp pour générer quelquechose.bpl.2.1.3.
Pour davantage d’informations sur les paquets, voir chapitre 15, “Utilisation des
paquets et des composants”.
Utilisation des paquets et des DLL
Pour la plupart des applications écrites avec C++Builder, les paquets offrent une
plus grande flexibilité et sont plus simples à créer que les DLL. Dans certaines
situations, les DLL sont mieux adaptées à vos projets que des paquets :
• Votre module de code doit être appelé par une application qui n’a pas été
conçue avec C++Builder.
• Vous étendez les fonctionnalités d’un serveur Web.
• Vous créez un module de code qui doit être utilisé par des développeurs
extérieurs.
• Votre projet est un conteneur OLE.
Vous ne pouvez pas transmettre des informations de type à l’exécution (RTTI)
entre DLL ou d’une DLL à un exécutable. C’est parce que les DLL maintiennent
leurs propres informations sur les symboles. Si vous avez besoin de transmettre
Création d’applications, de composants et de bibliothèques
7-11
Utilisation des DLL dans C++Builder
un objet TStrings à partir d’une DLL en utilisant un opérateur is ou as, créez un
paquet plutôt qu’une DLL. Les paquets partagent les informations sur les
symboles.
Utilisation des DLL dans C++Builder
Une DLL Windows peut être utilisée dans une application C++Builder comme
dans n’importe quelle application C++.
Pour charger statiquement une DLL lors du chargement de votre application
C++Builder, liez le fichier d’importation de bibliothèque de cette DLL à
l’application C++Builder lors de la liaison. Pour ajouter une bibliothèque
d’importation dans une application C++Builder, choisissez Projet|Ajouter au
projet et sélectionnez le fichier .LIB à ajouter.
Les fonctions exportées de la DLL sont alors disponibles dans l’application.
Prototypez les fonctions de la DLL utilisées dans l’application avec le
modificateur __declspec (dllimport) :
__declspec(dllimport) type_renvoyé nom_fonction_importée(paramètres);
Pour charger dynamiquement une DLL pendant l’exécution d’une application
C++Builder, incluez la bibliothèque d’importation comme pour un chargement
statique, puis spécifiez l’option de délai de chargement du lieur dans la page
Projet|Options|Lieur évolué. Vous pouvez également utiliser la fonction
LoadLibrary() de l’API Windows pour charger la DLL, puis utilisez la fonction
GetProcAddress() de l’API pour disposer d’un pointeur sur les fonctions
spécifiques à utiliser.
Pour plus d’informations sur l’utilisation des DLL, voir la Référence Win32 SDK
de Microsoft®.
Création de DLL dans C++Builder
La création de DLL est la même en C++Builder et en C++ standard :
1 Choisissez Fichier|Nouveau|Autre pour afficher la boîte de dialogue
Nouveaux éléments.
2 Double-cliquez sur l’icône Expert DLL.
3 Choisissez le type de fichier source (C ou C++) du module principal.
4 Si vous voulez utiliser le style MSVC++ et employer DllMain comme point
d’entrée de la DLL, cochez l’option VC++, sinon DllEntryPoint est utilisé
comme point d’entrée.
5 Cochez l’option Utiliser VCL ou Utiliser CLX pour créer une DLL contenant
des composants VCL ou CLX. Cette option est disponible uniquement pour les
modules source C++.
Voir “Création de DLL contenant des composants VCL et CLX” à la page 7-13.
7-12
Guide du développeur
Création de DLL contenant des composants VCL et CLX
6 Si la DLL doit être multithread, cochez l’options Multithread.
7 Cliquez sur OK.
Les fonctions exportées doivent être identifiées dans le code par le modificateur
__declspec (dllexport) comme en Borland C++ ou en Microsoft Visual C++. Par
exemple, le code suivant est correct en C++Builder, comme dans les autres
compilateurs C++ :
// MyDLL.cpp
double dblValue(double);
double halfValue(double);
extern "C" __declspec(dllexport) double changeValue(double, bool);
double dblValue(double value)
{
return value * value;
};
double halfValue(double value)
{
return value / 2.0;
}
double changeValue(double value, bool whichOp)
{
return whichOp ? dblValue(value) : halfValue(value);
}
Dans le code ci-dessus, la fonction changeValue est exportée, et de ce fait
utilisable par les applications appelantes. Les fonctions dblValue et halfValue sont
internes et ne peuvent être appelées hors de la DLL.
Pour plus d’informations sur la création de DLL, voir la Référence Win32 SDK de
Microsoft®.
Création de DLL contenant des composants VCL et CLX
L’un des avantages des DLL consiste à pouvoir utiliser une DLL créée avec un
outil de développement dans une application utilisant un autre outil de
développement. Si une DLL contient des composants VCL ou CLX (par exemple,
une fiche) devant être utilisés par les applications appelantes, il est nécessaire de
fournir des routines d’interface exportées utilisant les conventions d’appel
standard, en évitant le substantypage C++ et sans exiger pour fonctionner que
l’application appelante gère les bibliothèques VCL et CLX. Pour créer des
composants VCL ou CLX pouvant être exportés, utilisez les paquets d’exécution.
Pour plus d’informations, voir chapitre 15, “Utilisation des paquets
et des composants”.
Création d’applications, de composants et de bibliothèques
7-13
Création de DLL contenant des composants VCL et CLX
Par exemple, vous voulez créer une DLL afin d’afficher une simple boîte de
dialogue :
Voici le code de la DLL boîte de dialogue :
// DLLMAIN.H
//--------------------------------------------------------------------#ifndef dllMainH
#define dllMainH
//--------------------------------------------------------------------#include <Classes.hpp>
#include <vcl\Controls.hpp>
#include <vcl\StdCtrls.hpp>
#include <vcl\Forms.hpp>
//--------------------------------------------------------------------class TYesNoDialog : public TForm
{
// Composants gérés par l’EDI
__published:
TLabel *LabelText;
TButton *YesButton;
TButton *NoButton;
void __fastcall YesButtonClick(TObject *Sender);
void __fastcall NoButtonClick(TObject *Sender);
// Déclarations de l’utilisateur
private:
bool returnValue;
// Déclarations de l’utilisateur
public:
virtual __fastcall TYesNoDialog(TComponent *Owner);
bool __fastcall GetReturnValue();
};
// fonction d’interface exportée
extern "C" __declspec(dllexport) bool InvokeYesNoDialog();
//--------------------------------------------------------------------extern TYesNoDialog *YesNoDialog;
//--------------------------------------------------------------------#endif
// DLLMAIN.CPP
//--------------------------------------------------------------------#include <vcl\vcl.h>
#pragma hdrstop
#include "dllMain.h"
//--------------------------------------------------------------------#pragma resource "*.dfm"
TYesNoDialog *YesNoDialog;
//---------------------------------------------------------------------
7-14
Guide du développeur
Création de DLL contenant des composants VCL et CLX
__fastcall TYesNoDialog::TYesNoDialog(TComponent *Owner)
: TForm(Owner)
{
returnValue = false;
}
//--------------------------------------------------------------------void __fastcall TYesNoDialog::YesButtonClick(TObject *Sender)
{
returnValue = true;
Close();
}
//--------------------------------------------------------------------void __fastcall TYesNoDialog::NoButtonClick(TObject *Sender)
{
returnValue = false;
Close();
}
//--------------------------------------------------------------------bool __fastcall TYesNoDialog::GetReturnValue()
{
return returnValue;
}
//--------------------------------------------------------------------// fonction exportée d’interface C++ standard appelant la VCL
bool InvokeYesNoDialog()
{
bool returnValue;
TYesNoDialog *YesNoDialog = new TYesNoDialog(NULL);
YesNoDialog->ShowModal();
returnValue = YesNoDialog->GetReturnValue();
delete YesNoDialog;
return returnValue;
}
//---------------------------------------------------------------------
Le code de cet exemple affiche la boîte de dialogue et enregistre la valeur true
dans la donnée membre privée returnValue si le bouton “Oui” est choisi. Sinon,
returnValue prend la valeur false. La fonction membre publique GetReturnValue()
renvoie la valeur en cours de returnValue.
Pour appeler la boîte de dialogue et déterminer le bouton choisi, l’application
appelante doit utiliser la fonction exportée InvokeYesNoDialog(). Cette fonction est
déclarée dans DLLMAIN.H comme fonction exportée utilisant le lieur C (afin
d’éviter le substantypage C++) et la convention d’appel standard C. La fonction
est définie dans DLLMAIN.CPP.
L’utilisation d’une fonction C standard comme interface de la DLL permet à
toute application appelante, créée ou non avec C++Builder, d’utiliser la DLL. Les
fonctionnalités VCL et CLX nécessaires au fonctionnement de la boîte de
dialogue sont liées dans la DLL elle-même sans que l’application appelante en
sache quoique ce soit.
Toutefois la création d’une DLL utilisant VCL ou CLX nécessite la liaison des
composants VCL ou CLX nécessaires dans la DLL, ce qui en augmente la taille.
Création d’applications, de composants et de bibliothèques
7-15
Liaison de DLL
L’impact de cette augmentation sur la taille totale de l’application peut être
réduite en combinant plusieurs composants dans une seule DLL qui ne nécessite
qu’une seule copie des composants de gestion VCL ou CLX.
Liaison de DLL
Vous pouvez définir les options du lieur pour votre DLL sur la page Lieur de la
boîte de dialogue Options du projet. La case à cocher par défaut de cette page
permet également de créer une bibliothèque d’importation pour votre DLL. Si la
compilation est effectuée à partir de la ligne de commande, appelez le lieur,
ILINK32.EXE, avec le commutateur -Tpd. Par exemple :
ilink32 /c /aa /Tpd c0d32.obj mydll.obj, mydll.dll, mydll.map, import32.lib cw32mt.lib
Si vous avez besoin d’une bibliothèque d’importation, utilisez aussi le
commutateur -Gi, qui permet de la générer.
Vous pouvez, si vous le souhaitez, créer une bibliothèque d’importation avec
l’utilitaire de ligne de commande IMPLIB.EXE. Par exemple :
implib mydll.lib mydll.dll
Pour plus d’informations sur les différentes options de liaison de DLL et leur
utilisation avec d’autres modules statiquement ou dynamiquement liés à la
bibliothèque d’exécution, voir l’aide en ligne.
Ecriture d’applications de bases de données
Un des atouts de C++Builder est sa possibilité de créer des applications de bases
de données sophistiquées. C++Builder fournit des outils intégrés permettant de
vous connecter aux serveurs et bases de données SQL, comme Oracle, Sybase,
InterBase, MySQL, MS-SQL, Informix et DB2, tout en assurant un partage des
données transparent entre les différentes applications.
C++Builder comprend de nombreux composants permettant d’accéder aux bases
de données et de représenter les informations qu’elles contiennent. Sur la palette
de composants, les composants base de données sont regroupés selon le
mécanisme et la fonction d’accès aux données.
Tableau 7.2 Page base de données de la palette des composants
Page
de palette
BDE
7-16
Contenu
Composants qui utilisent le moteur de bases de données Borland (BDE), une
importante API permettant d’interagir avec les bases de données. Le moteur
BDE supporte la plus vaste gamme de fonctions et la plupart des utilitaires
dont Database Desktop, l’explorateur de base de données, le moniteur SQL
et l’administrateur BDE. Pour plus de détails, voir chapitre 24, “Utilisation
du moteur de bases de données Borland”.
Guide du développeur
Ecriture d’applications de bases de données
Tableau 7.2 Page base de données de la palette des composants (suite)
Page
de palette
Contenu
ADO
Composants qui utilisent les objets de données ActiveX (ADO), développés
par Microsoft, pour accéder aux informations des bases de données. De
nombreux pilotes ADO sont disponibles pour la connexion à différents
serveurs de bases de données. Les composants ADO vous permettent
d’intégrer votre application à l’environnement ADO. Pour plus de détails,
voir chapitre 25, “Utilisation des composants ADO”.
dbExpress
Composants multiplates-formes qui utilisent dbExpress pour accéder aux
informations des bases de données. Les pilotes dbExpress fournissent un
accès rapide aux bases de données, mais doivent être utilisés avec
TClientDataSet et TDataSetProvider pour effectuer des mises à jour. Pour plus
de détails, voir chapitre 26, “Utilisation d’ensembles de données
unidirectionnels”.
InterBase
Composants qui accèdent directement aux bases de données InterBase, sans
passer par une couche moteur distincte. Pour plus d’informations sur
l’utilisation des composants InterBase, voir l’aide en ligne.
AccèsBD
Composants qui peuvent être utilisés avec n’importe quel mécanisme d’accès
aux données comme TClientDataSet et TDataSetProvider. Voir chapitre 27,
“Utilisation d’ensembles de données client”, pour avoir des informations sur
les ensembles de données client. Voir chapitre 28, “Utilisation des
composants fournisseur”, pour avoir des informations sur les fournisseurs.
ContrôleBD
Contrôles orientés données qui peuvent accéder aux informations d’une
source de données. Voir chapitre 19, “Utilisation de contrôles de données”,
pour plus de détails.
Lorsque vous concevez une application de base de données, vous devez choisir
le mécanisme d’accès aux données à utiliser. Chaque mécanisme d’accès aux
données diffère par l’éventail des fonctions prises en charge, la facilité de
déploiement et la capacité des pilotes à gérer divers serveurs de bases de
données.
Voir la partie II, “Développement d’applications de bases de données”, dans ce
manuel, pour plus de détails sur la façon d’utiliser C++Builder pour créer des
applications de bases de données client ou serveur. Reportez-vous à
“Déploiement d’applications de bases de données” à la page 17-7 pour avoir des
informations sur le déploiement.
Remarque
Les éditions de C++Builder n’offrent pas toutes le support des bases de données.
Distribution d’applications de bases de données
C++Builder permet de créer des applications de bases de données distribuées en
utilisant un ensemble de composants liés. Il est ainsi possible d’écrire des
applications de bases de données en utilisant divers protocoles de
communication, dont DCOM, TCP/IP et SOAP.
Pour davantage d’informations sur la conception d’applications de bases de
données distribuées, voir chapitre 29, “Création d’applications multiniveaux”.
Création d’applications, de composants et de bibliothèques
7-17
Création d’applications serveur Web
Le déploiement d’applications de bases de données nécessite souvent le
déploiement du moteur de bases de données Borland (BDE) en plus des fichiers
de l’application. Pour des informations sur le déploiement du BDE, voir
“Déploiement d’applications de bases de données” à la page 17-7.
Création d’applications serveur Web
Les applications serveur Web sont des applications s’exécutant sur des serveurs
qui fournissent du contenu web, c’est-à-dire des pages HTML ou des documents
XML, sur Internet. Les applications serveur Web sont par exemple des
applications qui contrôlent l’accès à un site web, génèrent des bons de
commande ou répondent à des demandes d’informations.
Vous pouvez créer différents types d’applications serveur Web en utilisant les
technologies C++Builder suivantes :
•
•
•
•
WebBroker
WebSnap
InternetExpress
Services Web
Utilisation de WebBroker
Vous pouvez utiliser WebBroker (appelé aussi architecture NetCLX) pour créer
des applications serveur Web comme les applications CGI ou les bibliothèques
de liaison dynamiques (DLL). Ces applications serveur web peuvent ne contenir
aucun composant visuel. Les composants de la page Internet de la palette de
composants vous permettent de créer des gestionnaires d’événements, de
construire par programme des documents HTML ou XML et de les transférer
au client.
Pour créer une nouvelle application serveur Web en utilisant l’architecture
WebBroker, choisissez Fichier|Nouveau|Autre et double-cliquez sur Application
serveur Web dans la boîte de dialogue Nouveaux éléments. Choisissez ensuite le
type d’application serveur Web :
Tableau 7.3 Applications serveur Web
Type d’application
serveur Web
DLL ISAPI/NSAPI
7-18
Guide du développeur
Description
Les applications serveur Web ISAPI et NSAPI sont des DLL qui sont
chargées par le serveur Web. Les informations de requête client sont
transmises à la DLL sous forme de structure et sont évaluées par
TISAPIApplication. Chaque message de requête est géré dans un
thread d’exécution distinct.
Sélectionner ce type d’application ajoute l’en-tête bibliothèque des
fichiers du projet et les entrées nécessaires à la liste uses et à la
clause exports du fichier projet.
Création d’applications serveur Web
Tableau 7.3 Applications serveur Web (suite)
Type d’application
serveur Web
Description
Exécutable autonome
CGI
Les applications serveur Web CGI sont des applications console qui
reçoivent les requêtes des clients sur l’entrée standard, traitent ces
requêtes et renvoient le résultat sur la sortie standard afin qu’il soit
renvoyé au client.
Exécutable autonome
Win-CGI
Les applications serveur Web Win-CGI sont des applications
Windows qui reçoivent les requêtes des clients à partir d’un fichier
de paramètres de configuration (INI) écrit par le serveur et qui
écrivent le résultat dans un fichier que le serveur renvoie au client.
Le fichier INI est évalué par TCGIApplication. Chaque message de
requête est géré par une instance distincte de l’application.
Module Apache
partagé (DLL)
La sélection de ce type d’application configure votre projet comme
une DLL. Les applications serveur Web Apache sont des DLL
chargées par le serveur Web. Les informations sont transmises à la
DLL, traitées puis renvoyées au client par le serveur Web.
Exécutable
débogueur
d’application Web
La sélection de ce type d’application configure un environnement
pour développer et tester des applications serveur Web. Les
applications Débogueur d’application Web sont des fichiers
exécutables chargées par le serveur Web. Ce type d’application n’est
pas destiné au déploiement.
Les applications CGI et Win-CGI utilisent davantage de ressources système sur le
serveur, les applications complexes sont mieux gérées si elles sont créées sous la
forme d’applications ISAPI, NSAPI ou Apache DLL. Si vous écrivez des
applications multiplates-formes, vous devez sélectionner autonome CGI ou
Module Apache partagé (DLL) pour le développement de serveur Web. Vous
avez les mêmes options lorsque vous créez des applications WebSnap et service
Web.
Pour plus d’informations sur la construction d’applications serveur Web, voir
chapitre 32, “Création d’applications serveur Internet”.
Création d’applications WebSnap
WebSnap fournit un ensemble de composants et d’experts permettant de
construire des serveurs Web évolués qui interagissent avec les navigateurs web.
Les composants WebSnap génèrent du HTML ou d’autre contenu MIME pour les
pages Web. WebSnap est destiné au développement côté serveur. WebSnap ne
peut pas, pour l’instant, être utilisé dans des applications multiplates-formes.
Pour créer une nouvelle application WebSnap, sélectionnez Fichier|Nouveau|
Autre et sélectionnez l’onglet WebSnap dans la boîte de dialogue Nouveaux
éléments. Choisissez Application WebSnap. Ensuite, sélectionnez le type
d’application serveur Web (ISAPI/NSAPI, CGI, Win-CGI, Apache). Voir le
tableau 7.3, “Applications serveur Web”, pour plus de détails.
Pour plus d’informations sur WebSnap, voir chapitre 34, “Création d’applications
serveur Web avec WebSnap”.
Création d’applications, de composants et de bibliothèques
7-19
Ecriture d’applications en utilisant COM
Utilisation d’InternetExpress
InternetExpress est un ensemble de composants permettant d’étendre
l’architecture d’application serveur Web de base pour qu’elle agisse en tant que
client d’une application serveur. Vous utilisez InternetExpress pour les
applications dans lesquelles les clients dans un navigateur peuvent accéder aux
données d’un fournisseur, résoudre les mises à jour du fournisseur tout en
s’exécutant sur un client.
Les applications InternetExpress génèrent des pages HTML qui associent HTML,
XML et javascript. HTML régit la disposition et l’aspect des pages affichées dans
le navigateur des utilisateurs finals. XML code les paquets de données et les
paquets delta qui représentent les informations base de données. Javascript
permet aux contrôles HTML d’interpréter et de manipuler les données des
paquets XML sur la machine client.
Pour plus d’informations sur InternetExpress, voir chapitre “Construction des
applications Web avec InternetExpress” à la page 29-35.
Création d’applications services Web
Les services Web sont des applications modulaires indépendantes qui peuvent
être publiées ou invoquées sur un réseau (comme le web). Les services Web
fournissent des interfaces bien définies qui décrivent les services fournis. Vous
utilisez les services Web pour fournir ou utiliser des services programmables sur
Internet en faisant appel aux derniers standards comme XML, XML Schema,
SOAP (Simple Object Access Protocol) et WSDL (Web Service Definition
Language).
Les services Web utilisent SOAP, un protocole léger standard permettant
d’échanger des informations dans une environnement distribué. Il utilise HTTP
comme protocole de communication et XML pour coder les appels des
procédures distantes.
Vous pouvez utiliser C++Builder pour construire des serveurs qui implémentent
des services Web et des clients qui font appel à ces services. Vous pouvez écrire
des clients pour que des serveurs quelconques implémentent des services Web
qui répondent aux messages SOAP et des serveurs C++Builder pour publier des
services Web à utiliser par des clients quelconques.
Pour plus d’informations sur services Web, voir chapitre 36, “Utilisation de
services Web”.
Ecriture d’applications en utilisant COM
COM (Component Object Model) propose une architecture d’objet distribué sous
Windows conçue pour assurer une interopérabilité des objets en utilisant des
routines prédéfinies appelées des interfaces. Les applications COM utilisent des
objets implémentés par un processus différent ou, si vous utilisez DCOM, sur
7-20
Guide du développeur
Ecriture d’applications en utilisant COM
une machine différente. Vous pouvez aussi utiliser COM+, ActiveX et les pages
Active Server.
COM est un modèle de composant logiciel indépendant du langage qui permet
l’interaction entre des composants logiciels et des applications s’exécutant sous
Windows. L’aspect fondamental de COM est de permettre la communication
entre composants, entre applications et entre clients et serveurs, par le biais
d’interfaces clairement définies. Les interfaces offrent aux clients un moyen de
demander à un composant COM quelles fonctionnalités il supporte à l’exécution.
Pour fournir d’autres fonctionnalités à votre composant, il suffit d’ajouter une
autre interface pour ces fonctionnalités.
Utilisation de COM et de DCOM
C++Builder contient des classes et des experts qui simplifient la création
d’applications COM, OLE ou ActiveX. Vous pouvez créer des clients ou des
serveurs COM qui implémentent des objets COM, des serveurs d’automatisation
(dont les objets Active Server), des contrôles ActiveX ou des ActiveForms. COM
sert également de base à d’autres technologies comme l’automatisation, les
contrôles ActiveX, les documents Active et les répertoires Active.
L’utilisation de C++Builder pour créer des applications basées sur COM offre de
nombreuses possibilités, allant de l’amélioration de la conception de logiciel en
utilisant des interfaces de manière interne dans une application, à la création
d’objets qui peuvent interagir avec d’autres objets utilisant l’API COM du
système, comme les extensions du shell Win9x ou la gestion multimedia DirectX.
Les applications peuvent accéder aux interfaces des composants COM se
trouvant sur le même ordinateur que l’application ou sur un autre ordinateur du
réseau, en utilisant un mécanisme nommé DCOM (Distributed COM).
Pour davantage d’informations sur COM et les contrôles ActiveX, voir
chapitre 38, “Présentation des technologies COM”, chapitre 43, “Création d’un
contrôle ActiveX”, et “Distribution d’une application client en tant que contrôle
ActiveX” à la page 29-34.
Pour davantage d’informations sur DCOM, voir “Utilisation de connexions
DCOM” à la page 29-10.
Utilisation de MTS et de COM+
Il est possible d’étendre les applications COM en utilisant des services spéciaux
pour gérer les objets dans un environnement distribué important. Ces services
sont, entre autres, des services de transaction, la sécurité et la gestion des
ressources proposées par Microsoft Transaction Server (MTS) (pour les versions
de Windows antérieures à Windows 2000) ou COM+ (pour Windows 2000 ou
plus).
Pour davantage d’informations sur MTS et COM+, voir chapitre 44, “Création
d’objets MTS ou COM+” et “Utilisation des modules de données transactionnels”
à la page 29-7.
Création d’applications, de composants et de bibliothèques
7-21
Utilisation de modules de données
Utilisation de modules de données
Un module de données ressemble à une fiche spéciale qui ne contient que des
composants non-visuels. Tous les composants d’un module de données peuvent
être placés dans des fiches ordinaires avec des contrôles visuels. Les modules de
données constituent un moyen d’organisation utile quand vous envisagez de
réutiliser des groupes d’objets de bases de données ou système ou si vous
souhaitez isoler les parties d’une application qui gèrent la connexion aux bases
de données ou les règles de fonctionnement.
Il y a plusieurs types de modules de données, dont standard, distant, modules
Web, modules applet et services, selon l’édition de C++Builder que vous avez.
Chaque type de module de données a une fonction spéciale.
• Les modules de données standard sont particulièrement utiles aux applications
de bases de données à un ou à deux niveaux, mais peuvent être utilisés pour
organiser les composants non visuels de n’importe quelle application. Pour
plus d’informations, voir “Création et modification de modules de données
standard” à la page 7-23.
• Les modules de données distants constituent la base d’un serveur
d’application dans une application de base de données multiniveau. Ils ne
sont pas disponibles dans toutes les éditions. En plus de contenir les
composants non visuels du serveur d’application, les modules de données
distants exposent l’interface utilisée par les clients pour communiquer avec le
serveur d’application. Pour plus d’informations sur leur utilisation, voir “Ajout
d’un module de données distant à un projet serveur d’application” à la
page 7-26.
• Les modules Web constituent la base des applications serveur Web. En plus
de contenir les composants qui créent le contenu des messages de réponse
HTTP, ils gèrent la répartition des messages HTTP issus des applications
client. Voir chapitre 32, “Création d’applications serveur Internet”, pour plus
d’informations sur l’utilisation des modules Web.
• Les modules applet constituent la base des applets de panneau de
configuration. En plus de contenir les composants non visuels qui
implémentent le panneau de configuration, ils définissent les propriétés qui
déterminent la façon dont l’icône de l’applet apparaît dans le panneau de
configuration et contiennent les événements qui sont appelés quand les
utilisateurs exécutent l’applet. Pour plus d’informations sur les modules
applet, voir l’aide en ligne.
• Les services encapsulent des services individuels dans une application de
service NT. En plus de contenir les composants non visuels qui implémentent
un service, les services contiennent les événements qui sont appelés quand le
service est démarré ou arrêté. Pour davantage d’informations sur les services,
voir “Applications service” à la page 7-4.
7-22
Guide du développeur
Utilisation de modules de données
Création et modification de modules de données standard
Pour créer un module de données standard pour un projet, choisissez Fichier|
Nouveau|Module de données. C++Builder ouvre un conteneur module de
données sur le bureau, affiche le fichier unité du nouveau module dans l’éditeur
de code et ajoute le module au projet en cours.
En conception, un module de données ressemble à une fiche C++Builder
standard, avec un fond blanc et pas de grille d’alignement. Comme avec les
fiches, vous pouvez placer des composants non visuels de la palette dans un
module et modifier leurs propriétés dans l’inspecteur d’objets. Vous pouvez
redimensionner un module de données pour l’adapter aux composants que vous
lui ajoutez.
Vous pouvez aussi cliquer avec le bouton droit sur un module pour afficher son
menu contextuel. Le tableau suivant résume les options du menu contextuel d’un
module de données :
Tableau 7.4 Options du menu contextuel des modules de données
Elément de menu
Utilisation
Edition
Affiche un menu contextuel grâce auquel vous pouvez couper,
copier, coller, supprimer et sélectionner les composants du module
de données.
Position
Aligne les composants s non visuels sur la grille invisible du module
(Aligner sur la grille) ouselon des critères que vous indiquez dans la
boîte de dialogue Alignement (Aligner).
Ordre de tabulation
Vous permet de changer l’ordre dans lequel la focalisation parcourt
les composants quand vous appuyez sur la touche Tab.
Ordre de création
Vous permet de modifier l’ordre dans lequel les composants d’accès
aux données sont créés au démarrage.
Revenir à hérité
Annule les modifications apportées à un module hérité d’un autre
module dans le référentiel d’objets, et revient au module tel qu’il
était à l’origine lors de l’héritage.
Ajouter au référentiel
Stocke un lien vers le module de données dans le référentiel d’objets.
Voir comme texte
Affiche la représentation textuelle des propriétés du module de
données.
DFM texte
Bascule entre les formats (binaire ou texte) dans lesquels est
enregistrée la fiche.
Pour davantage d’informations sur les modules de données, voir l’aide en ligne.
Nom d’un module de données et de son fichier unité
La barre de titre d’un module de données affiche le nom du module. Le nom
par défaut est “DataModuleN”, où N est un nombre représentant le plus petit
numéro d’unité non utilisé dans le projet. Par exemple, si vous commencez un
nouveau projet et si vous lui ajoutez un module avant de faire quoi que ce soit
d’autre, le nom du module de données est par défaut “DataModule2”. Le fichier
unité correspondant à DataModule2 est par défaut “Unit2”.
Création d’applications, de composants et de bibliothèques
7-23
Utilisation de modules de données
Il vaut mieux renommer les modules de données et les fichiers unités
correspondants, pendant la conception, pour qu’ils soient plus descriptifs. En
particulier, il faut renommer les modules de données que vous ajoutez au
référentiel d’objets pour éviter les conflits de nom avec d’autres modules de
données du référentiel ou des applications qui utilisent vos modules.
Pour renommer un module de données :
1 Sélectionnez le module.
2 Modifiez la propriété Name du module dans l’inspecteur d’objets.
Le nouveau nom du module apparaît dans la barre de titre dès que la propriété
Name n’a plus la focalisation dans l’inspecteur d’objets.
Changer le nom d’un module de données en conception change son nom de
variable dans la section interface du code. Cela change également toute
utilisation du nom dans les déclarations de procédure. Vous devez changer
manuellement toutes les références à ce module de données dans le code que
vous avez écrit.
Pour renommer le fichier unité d’un module de données :
1 Sélectionnez le fichier unité.
Placer et nommer les composants
Vous placez des composants non visuels dans un module de données exactement
comme vous les placeriez sur une fiche. Cliquez sur le composant voulu dans la
page appropriée de la palette de composants, puis cliquez dans le module de
données pour placer le composant. Vous ne pouvez pas placer des contrôles
visuels, comme les grilles, dans un module de données. Si vous essayez de le
faire, vous recevez un message d’erreur.
Pour faciliter leur utilisation, les composants d’un module de données sont
affichés avec leur nom. Quand vous placez un composant pour la première fois,
C++Builder lui attribue un nom générique indiquant le type du composant, suivi
de 1. Par exemple, le composant TDataSource prend le nom DataSource1. Cela
facilite la sélection des composants lorsque vous voulez travailler sur leurs
propriétés et méthodes.
Vous pouvez encore donner à un composant un autre nom qui reflète son type
et sa fonction.
Pour changer le nom d’un composant dans un module de données :
1 Sélectionnez le composant.
2 Modifiez la propriété Name du composant dans l’inspecteur d’objets.
Le nouveau nom du composant apparaît sous son icône dans le module de
données dès que la propriété Name n’a plus la focalisation dans l’inspecteur
d’objets.
Par exemple, supposons que votre application de base de données utilise la table
CUSTOMER. Pour accéder à cette table, vous avez besoin d’au moins deux
composants d’accès aux données : un composant source de données (TDataSource)
7-24
Guide du développeur
Utilisation de modules de données
et un composant table (TClientDataSet). Quand vous placez ces composants dans
votre module de données, C++Builder leur attribue les noms DataSource1 et
ClientDataSet1. Pour refléter le type des composants et la base de données à
laquelle ils accèdent, CUSTOMER, vous pourriez changer leurs noms en
CustomerSource et CustomerTable.
Utilisation des propriétés et événements des composants dans un module
de données
Placer des composants dans un module de données centralise leur comportement
pour l’application toute entière. Par exemple, vous pouvez utiliser les propriétés
des composants ensemble de données, comme TClientDataSet, pour contrôler les
données disponibles dans les composants source de données qui utilisent ces
ensembles de données. Définir la propriété ReadOnly d’un ensemble de données
par true empêche les utilisateurs de modifier les données qu’ils voient dans un
contrôle orienté données se trouvant dans une fiche. Vous pouvez aussi appeler
l’éditeur de champs d’un ensemble de données, en double-cliquant sur
ClientDataSet1, pour limiter les champs d’une table ou d’une requête qui seront
disponibles pour la source de données et donc pour les contrôles orientés
données des fiches. Les propriétés que vous définissez pour les composants d’un
module de données s’appliquent à toutes les fiches de votre application qui
utilisent le module.
Outre les propriétés, vous pouvez écrire des gestionnaires d’événements pour les
composants. Par exemple, un composant TDataSource peut avoir trois
événements : OnDataChange, OnStateChange et OnUpdateData. Un composant
TClientDataSet a plus de 20 événements possibles. Vous pouvez utiliser ces
événements pour créer un ensemble cohérent de règles de gestion qui dictent les
manipulation de données dans toute votre application.
Création de règles de gestion dans un module de données
Parallèlement à l’écriture de gestionnaires d’événements pour les composants
d’un module de données, vous pouvez programmer des méthodes directement
dans le fichier unité du module de données. Ces méthodes peuvent être
appliquées en tant que règles de gestion aux fiches qui utilisent le module de
données. Par exemple, vous pouvez écrire une procédure qui établisse les
comptes mensuels, trimestriels et annuels. Vous pouvez appeler cette procédure
depuis un gestionnaire d’événement d’un composant du module de données.
Accès à un module de données depuis une fiche
Pour associer des contrôles visuels d’une fiche à un module de données, vous
devez tout d’abord ajouter le fichier en-tête du module de données au fichier
cpp de la fiche. Pour ce faire, vous pouvez procéder de plusieurs manières :
• Ouvrez le fichier unité de la fiche dans l’éditeur de code et incluez le fichier
en-tête du du module de données en utilisant la directive #include.
Création d’applications, de composants et de bibliothèques
7-25
Utilisation de modules de données
• Cliquez sur le fichier unité de la fiche, choisissez Fichier|Inclure l’en-tête
d’unité, puis entrez le nom d’un module ou choisissez-le dans la boîte liste de
la boîte de dialogue Utiliser l’unité.
• Pour les composants base de données, cliquez dans le module de données sur
un composant ensemble de données ou requête pour ouvrir l’éditeur de
champs et faire glisser dans la fiche des champs de l’éditeur. C++Builder vous
demande alors de confirmer l’ajout de ce module dans la fiche puis crée des
contrôles (par exemple, des boîtes de saisie) pour chaque champ.
Par exemple, si vous avez ajouté le composant TClientDataSet à votre module de
données, double-cliquez dessus pour ouvrir l’éditeur de champs. Sélectionnez un
champ et faites-le glisser dans la fiche. Une boîte de saisie apparaît.
Comme la source de données n’est pas encore définie, C++Builder ajoute un
nouveau composant source de données, DataSource1, à la fiche et définit la
propriété DataSource de la boîte de saisie par DataSource1. La source de données
définit automatiquement sa propriété DataSet par le composant ensemble de
données, ClientDataSet1, dans le module de données.
Vous pouvez définir la source de données avant de faire glisser un champ sur la
fiche, en ajoutant un composant TDataSource au module de données. Définissez
la propriété DataSet de la source de données par ClientDataSet1. Une fois que
vous avez fait glissé un champ dans la fiche, la boîte de saisie apparaît avec sa
propriété TDataSource déjà définie par DataSource1. Grâce à cette méthode, votre
modèle d’accès aux données est plus propre.
Ajout d’un module de données distant à un projet serveur
d’application
Certaines éditions de C++Builder vous permettent d’ajouter des modules de
données distants à des projets de serveur d’applications. Un module de données
distant dispose d’une interface à laquelle les clients d’une application
multiniveau peuvent accéder au travers d’un réseau.
Pour ajouter un module de données distant à un projet :
1 Choisissez Fichier|Nouveau|Autre.
2 Sélectionnez la page Multi-niveaux dans la boîte de dialogue Nouveaux
éléments.
3 Double-cliquez sur l’icône Module de données distant pour ouvrir l’expert
module de données distant.
Une fois le module de données distant ajouté à un projet, vous pouvez l’utiliser
comme un module de données standard.
Pour davantage d’informations sur les applications de base de données
multiniveaux, voir chapitre 29, “Création d’applications multiniveaux”.
7-26
Guide du développeur
Utilisation du référentiel d’objets
Utilisation du référentiel d’objets
Le référentiel d’objets (Outils|Référentiel) vous permet facilement de partager ou
de copier des fiches, des boîtes de dialogue ou des modules de données. Il
propose également des modèles de projet comme point de départ pour de
nouveaux projets et des experts qui guident l’utilisateur dans la création de
fiches ou de projets. Le référentiel est stocké dans BCB.DRO (placé par défaut
dans le répertoire BIN), c’est un fichier texte qui contient des références aux
éléments apparaissant dans le référentiel et dans la boîte de dialogue Nouveaux
éléments.
Partage d’éléments dans un projet
Il est également facile de partager des éléments à l’intérieur d’un projet sans avoir
à les ajouter au référentiel d’objets. Quand vous ouvrez la boîte de dialogue
Nouveaux éléments (Fichier|Nouveau|Autre), l’onglet d’une des pages porte le
nom de votre projet. Cette page énumère toutes les fiches, boîtes de dialogue et
modules de données de votre projet. Vous pouvez alors dériver un nouvel
élément d’un élément existant et le personnaliser si nécessaire.
Ajout d’éléments au Référentiel d’objets
Vous pouvez ajouter vos propres projets, fiches, cadres et modules de données à
ceux qui existent déjà dans le Référentiel d’objets. Pour ajouter un élément au
Référentiel d’objets,
1 Si l’élément est un projet ou dans un projet, ouvrez le projet.
2 Pour un projet, choisissez Projet|Ajouter au référentiel. Pour une fiche ou un
module de données, cliquez sur l’élément avec le bouton droit de la souris
puis choisissez Ajouter au référentiel.
3 Entrez une description, un titre et un auteur.
4 Décidez dans quelle page cet élément doit apparaître dans la boîte de
dialogue Nouveaux éléments, entrez ou sélectionnez la page dans la boîte à
options Page. Si vous entrez un nom de page inexistant, C++Builder crée une
nouvelle page.
5 Choisissez Parcourir pour sélectionner une icône représentant l’objet dans le
Référentiel d’objets.
6 Choisissez OK.
Partage d’objets par une équipe de développement
Vous pouvez partager des objets dans un groupe de travail ou une équipe de
développement en utilisant un référentiel accessible depuis un réseau. Pour
utiliser un référentiel partagé, tous les membres de l’équipe doivent sélectionner
Création d’applications, de composants et de bibliothèques
7-27
Utilisation du référentiel d’objets
le même répertoire de Référentiel partagé dans la boîte de dialogue Options
d’environnement :
1 Choisissez Outils|Options d’environnement.
2 Dans la page Préférences, repérez le volet Référentiel partagé. Dans le volet
boîte de saisie Répertoire, entrez le nom du répertoire où doit se trouver le
référentiel partagé. Assurez-vous que le répertoire spécifié est bien accessible
pour tous les membres de l’équipe.
Lors de l’ajout du premier élément au référentiel, C++Builder crée, s’il n’existe
pas déjà, un fichier BCB.DRO dans le répertoire spécifié par Référentiel partagé.
Utilisation d’un élément du référentiel d’objets dans un projet
Pour accéder aux éléments du Référentiel d’objets, choisissez Fichier|Nouveau|
Autre. La boîte de dialogue Nouveaux éléments apparaît et affiche tous les
éléments disponibles. Selon le type d’élément que vous souhaitez utiliser, il y a
jusqu’à trois options pour ajouter un élément à votre projet :
• Copier
• Hériter
• Utiliser
Copie d’un élément
Choisissez Copier pour obtenir une réplique exacte de l’élément sélectionné et
ajouter la copie à votre projet. Les modifications ultérieures de l’élément du
Référentiel d’objets ne sont pas répercutées sur votre copie. De même, les
modifications apportées à la copie n’affectent pas l’élément original dans le
Référentiel d’objets.
Copier est la seule option possible pour les modèles de projet.
Héritage d’un élément
Choisissez Hériter pour dériver une nouvelle classe de l’élément sélectionné dans
le Référentiel d’objets et ajouter la nouvelle classe à votre projet. Quand vous
recompilez votre projet, toutes les modifications apportées à l’élément du
Référentiel d’objets sont reportées dans votre classe dérivée. Les modifications
faites dans la classe dérivée n’affectent pas l’élément partagé du Référentiel
d’objets.
Hériter est une option proposée pour les fiches, les boîtes de dialogue et les
modules de données, mais pas pour les modèles de projet. C’est la seule option
utilisable pour réutiliser les éléments du projet en cours.
Utilisation d’un élément
Choisissez Utiliser quand vous voulez que l’objet sélectionné fasse lui-même
partie de votre projet. Les modifications faites à l’élément apparaissent dans tous
les projets dans lesquels l’élément a été ajouté en utilisant l’option Hériter ou
Utiliser. Soyez prudent si vous choisissez cette option.
7-28
Guide du développeur
Utilisation du référentiel d’objets
L’option Utiliser est disponible pour les fiches, les boîtes de dialogue et les
modules de données.
Utilisation de modèles de projet
Les modèles de projet sont des projets préfabriqués que vous pouvez utiliser
comme point de départ pour la création de vos projets. Pour créer un nouveau
projet à partir d’un modèle
1 Choisissez Fichier|Nouveau|Autre pour afficher la boîte de dialogue
Nouveaux éléments.
2 Choisissez l’onglet Projets.
3 Sélectionnez le modèle de projet souhaité et choisissez OK.
4 Dans la boîte de dialogue Sélection du répertoire, spécifiez le répertoire des
fichiers du nouveau projet..
C++Builder copie les fichiers du modèle dans le répertoire spécifié, où vous
pouvez ensuite les modifier. Le modèle de projet initial n’est pas affecté par vos
modifications.
Modification d’éléments partagés
Si vous modifiez un élément du référentiel d’objets, vos modifications affectent
tous les projets qui ultérieurement utilisent l’élément mais également tous les
projets existants qui ont ajouté l’élément en utilisant les options Utiliser ou
Hériter. Pour éviter de propager des modifications à d’autres projets, vous avez
plusieurs solutions :
• Copier l’élément et le modifier uniquement dans le projet en cours.
• Copier l’élément dans le projet en cours, le modifier puis l’ajouter au
référentiel sous un autre nom.
• Créer un composant, une DLL, un modèle de composant ou un cadre à partir
de l’élément. Si vous créez un composant ou une DLL, vous pouvez le
partager avec d’autres développeurs.
Spécification d’un projet par défaut, d’une nouvelle fiche et de la
fiche principale
Par défaut, quand vous choisissez Fichier|Nouveau|Application ou Fichier|
Nouveau|Fiche, C++Builder affiche une fiche vide. Vous pouvez changer ce
comportement en reconfigurant le référentiel :
1 Choisissez Outils|Référentiel.
Création d’applications, de composants et de bibliothèques
7-29
Activation de l’aide dans les applications
2 Si vous voulez spécifier un projet par défaut, sélectionnez la page Projets et
choisissez un élément dans Objets. Sélectionnez ensuite la case à cocher
Nouveau projet.
3 Pour spécifier une fiche par défaut, sélectionnez une page Référentiel (comme
Fiches), puis choisissez une fiche sous Objets. Pour spécifier la nouvelle fiche
par défaut, (Fiche|Nouveau|Fiche), sélectionnez la case à cocher Nouvelle
fiche. Pour spécifier la nouvelle fiche principale par défaut des nouveaux
projets, sélectionnez la case à cocher Fiche principale.
4 Cliquez sur OK.
Activation de l’aide dans les applications
La VCL et la CLX supportent toutes deux l’affichage de l’aide à partir
d’applications utilisant un mécanisme à base d’objets pour transmettre les
demandes d’aide à l’un des multiples visualiseurs d’aide externes. Pour
supporter cela, une application doit contenir une classe qui implémente
l’interface ICustomHelpViewer (et, éventuellement, une des nombreuses interfaces
qui en descendent), puis se recense elle-même dans le gestionnaire d’aide global.
La VCL fournit à toutes les applications une instance de TWinHelpViewer, qui
implémente toutes ces interfaces et fournit un lien entre les applications et
WinHelp. CLX impose que vous fournissiez votre propre implémentation. Dans
Windows, les applications CLX peuvent utiliser l’unité WinHelpViewer fournie
avec la VCL en la liant de manière statique, c’est-à-dire en incluant l’unité
comme faisant partie du paquet au lieu de la lier dans le paquet VCL.
Le gestionnaire d’aide maintient la liste des visualiseurs recensés et leur passe les
requêtes dans un processus en deux phases : d’abord, il demande à chaque
visualiseur s’il est capable de fournir du support sur un mot clé ou un contexte
d’aide particulier ; ensuite, il passe la requête d’aide au visualiseur ayant indiqué
qu’il peut fournir un tel support.
Si plusieurs visualiseurs supportent le mot clé (comme c’est le cas des
applications ayant recensé des visualiseurs pour WinHelp et HyperHelp sous
Windows et pour Man et Info sous Linux), le gestionnaire d’aide peut afficher
une boîte de sélection dans laquelle l’utilisateur de l’application choisit le
visualiseur d’aide à invoquer. Sinon, il affiche le premier système d’aide lui
ayant répondu.
Interfaces avec les systèmes d’aide
Le système d’aide permet la communication entre votre application et les
visualiseurs d’aide via une série d’interfaces. Ces interfaces sont toutes définies
dans HelpIntfs, qui contient également l’implémentation du gestionnaire d’aide.
ICustomHelpViewer prend en charge l’affichage de l’aide selon le mot clé fourni et
l’affichage d’un sommaire listant toute l’aide disponible dans un visualiseur
particulier.
7-30
Guide du développeur
Activation de l’aide dans les applications
IExtendedHelpViewer prend en charge l’affichage de l’aide selon le numéro de
contexte fourni et l’affichage des rubriques ; dans la majorité des systèmes d’aide,
les rubriques fonctionnent comme des mots clés de haut niveau (par exemple,
“IntToStr” pourrait être un mot clé dans le système d’aide, tandis que “Routines
de manipulation de chaînes” pourrait être le nom d’une rubrique).
ISpecialWinHelpViewer prend en charge la réponse aux messages WinHelp
spécialisés qu’une application s’exécutant sous Windows risque de recevoir et
qui ne sont pas facilement généralisables. En principe, seules les applications
opérant dans l’environnement Windows ont besoin d’implémenter cette interface
et, même alors, elle n’est nécessaire qu’aux applications faisant une forte
utilisation des messages WinHelp non standard.
IHelpManager fournit le mécanisme permettant au visualiseur d’aide de
communiquer avec le gestionnaire d’aide de l’application et demande un
supplément d’information. Un IHelpManager est obtenu au moment où le
visualiseur d’aide se recense lui-même.
IHelpSystem fournit le mécanisme permettant à TApplication de transmettre les
requêtes d’aide au système d’aide. TApplication obtient une instance d’un objet
qui implémente à la fois IHelpSystem et IHelpManager au chargement de
l’application et exporte cette instance en tant que propriété ; cela autorise
d’autres parties du code de l’application à traiter les requêtes d’aide directement
lorsque c’est possible.
IHelpSelector fournit le mécanisme permettant au système d’aide d’invoquer
l’interface utilisateur pour demander quel visualiseur d’aide doit être utilisé
lorsque plusieurs visualiseurs sont à même de gérer une requête d’aide et
d’afficher un sommaire. Cette capacité d’affichage n’est pas directement intégrée
au gestionnaire d’aide pour permettre l’utilisation du même code du gestionnaire
d’aide, quel que soit l’ensemble de widgets ou la bibliothèque de classes utilisé.
Implémentation de ICustomHelpViewer
L’interface ICustomHelpViewer contient trois types de méthodes : les méthodes
servant à communiquer au gestionnaire d’aide les informations du niveau
système (par exemple, des informations non liées à une requête d’aide
particulière) ; les méthodes servant à afficher l’aide en fonction du mot clé fourni
par le gestionnaire d’aide ; les méthodes servant à afficher le sommaire.
Communication avec le gestionnaire d’aide
ICustomHelpViewer fournit quatre fonctions servant à communiquer les
informations système au gestionnaire d’aide :
•
•
•
•
GetViewerName
NotifyID
ShutDown
SoftShutDown
Création d’applications, de composants et de bibliothèques
7-31
Activation de l’aide dans les applications
Le gestionnaire d’aide appelle ces fonctions dans les circonstances suivantes :
• AnsiString ICustomHelpViewer::GetViewerName() est appelée lorsque le
gestionnaire d’aide veut connaître le nom du visualiseur (par exemple, si
l’application doit afficher la liste des visualiseurs recensés). Cette information
est renvoyée via une chaîne et celle-ci doit être logiquement statique (c’est-àdire qu’elle ne peut pas être modifiée pendant que l’application s’exécute). Les
jeux de caractères multi-octets ne sont pas pris en charge.
• void ICustomHelpViewer::NotifyID(const int ViewerID) est appelée
immédiatement après le recensement pour fournir au visualiseur un cookie
qui l’identifie de manière unique. Cette information doit être conservée pour
une utilisation ultérieure ; si le visualiseur s’interrompt de son propre chef (et
non pas en réponse à une notification du gestionnaire d’aide), il doit fournir le
cookie identificateur au gestionnaire d’aide pour que celui-ci libère toutes les
références au visualiseur. (Ne pas réussir à fournir le cookie, ou en fournir un
mauvais, peut amener le gestionnaire d’aide à libérer les références au
mauvais visualiseur.)
• void ICustomHelpViewer::ShutDown() est appelée par le gestionnaire d’aide
pour signaler au visualiseur d’aide que le gestionnaire va s’interrompre et que
toutes les ressources allouées par le visualiseur doivent être libérées. Il est
conseillé de déléguer à cette méthode la libération de toutes les ressources.
• void ICustomHelpViewer::SoftShutDown() est appelée par le gestionnaire d’aide
pour demander au visualiseur d’aide de fermer toutes les manifestations
externes visibles du système d’aide (par exemple, les fenêtres affichant des
informations d’aide) sans décharger le visualiseur.
Demande d’informations au gestionnaire d’aide
Les visualiseurs d’aide communiquent avec le gestionnaire d’aide via l’interface
IHelpManager, une instance de celle-ci leur est renvoyée lorsqu’ils se recensent
auprès du gestionnaire d’aide. IHelpManager permet au visualiseur de
communiquer quatre choses :
• Une requête pour le handle de fenêtre du contrôle actif.
• Une requête pour le nom du fichier d’aide supposé contenir l’aide sur le
contrôle actif.
• Une requête pour le chemin d’accès à ce fichier.
• La notification que le visualiseur d’aide va s’interrompre lui-même en réponse
à autre chose qu’une demande issue du gestionnaire d’aide.
int IHelpManager::GetHandle() est appelée par le visualiseur d’aide s’il veut
connaître le handle du contrôle actif ; le résultat est un handle de fenêtre.
AnsiString IHelpManager::GetHandle() est appelée par le visualiseur d’aide s’il
veut connaître le nom du fichier d’aide supposé contenir l’aide sur le contrôle
actif.
7-32
Guide du développeur
Activation de l’aide dans les applications
void IHelpManager::Release() est appelée pour signaler au gestionnaire d’aide
qu’un visualiseur d’aide va se déconnecter. Elle ne doit jamais être appelée en
réponse à une requête via IHelpManager::ShutDown() ; elle sert à annoncer au
gestionnaire d’aide uniquement les déconnexions inattendues.
Affichage de l’aide basée sur un mot clé
Les requêtes d’aide adressées au visualiseur d’aide sont soit basées sur un mot
clé, auquel cas le visualiseur est chargé de fournir de l’aide en fonction d’une
chaîne particulière, soit basées sur un contexte, auquel cas le visualiseur est
chargé de fournir de l’aide en fonction d’un identificateur numérique particulier.
CLX
Les contextes d’aide numériques sont la forme par défaut des requêtes d’aide
des applications s’exécutant sous Windows et utilisant le système WinHelp ; bien
que CLX les supporte, il n’est pas conseillé de les utiliser dans les applications
CLX car la majorité des systèmes d’aide sous Linux ne les comprennent pas.
Les implémentations de ICustomHelpViewer sont nécessaires pour prendre en
charge les requêtes basées sur les mots clés, les implémentations de
IExtendedHelpViewer sont nécessaires pour prendre en charge les requêtes basées
sur des contextes.
ICustomHelpViewer fournit trois méthodes pour traiter l’aide par mot clé :
• UnderstandsKeyword
• GetHelpStrings
• ShowHelp
int__fastcall ICustomHelpViewer::UnderstandsKeyword(const AnsiString HelpString)
est la première des trois méthodes appelées par le gestionnaire d’aide, qui
appellera chacun des visualiseurs d’aide recensés avec la même chaîne pour
demander si le visualiseur peut fournir de l’aide pour cette chaîne ; le visualiseur
est supposé répondre par un entier indiquant le nombre de pages d’aide
différentes qu’il peut afficher en réponse à cette demande. Le visualiseur peut
utiliser la méthode qu’il veut pour le déterminer — dans l’EDI, le visualiseur
HyperHelp maintient son propre index et effectue la recherche. Si le visualiseur
ne dispose pas d’aide sur le mot clé, il doit renvoyer zéro. Les nombres négatifs
sont interprétés comme zéro, mais ce comportement n’est pas garanti dans les
versions futures.
Classes::TStringList*__fastcall ICustomHelpViewer::GetHelpStrings(const AnsiString
HelpString)
est appelée par le gestionnaire d’aide si plusieurs visualiseurs peuvent fournir de
l’aide sur une rubrique. Le visualiseur doit renvoyer une TStringList, qui est
libérée par le gestionnaire d’aide. Les chaînes de la liste renvoyée doivent
correspondre aux pages disponibles pour le mot clé, mais les caractéristiques de
correspondance peuvent être déterminées par le visualiseur. Dans le cas du
visualiseur WinHelp sous Windows et d’HyperHelp sous Linux, la liste de
chaînes contient toujours une seule entrée. HyperHelp propose sa propre
indexation, et la dupliquer ailleurs serait inutile. Dans le cas du visualiseur de
Création d’applications, de composants et de bibliothèques
7-33
Activation de l’aide dans les applications
pages Man (Linux), la liste de chaînes contient plusieurs chaînes, une pour
chaque section du manuel contenant une page pour ce mot clé.
void__fastcall ICustomHelpViewer::ShowHelp(const AnsiString HelpString)
est appelée par le gestionnaire d’aide s’il a besoin que le visualiseur d’aide
affiche de l’aide sur un mot clé particulier. C’est le dernier appel de méthode de
l’opération ; elle n’est jamais appelée sauf si UnderstandsKeyword a été invoquée
au préalable.
Affichage des sommaires
ICustomHelpViewer fournit deux méthodes pour afficher les sommaires :
• CanShowTableOfContents
• ShowTableOfContents
Leur mode opératoire ressemble beaucoup à celui des requêtes d’aide par mot
clé : Le gestionnaire d’aide commence par interroger tous les visualiseurs d’aide
en appelant ICustomHelpViewer::CanShowTableOfContents() puis il invoque un
visualiseur donné en appelant ICustomHelpViewer::ShowTableOfContents().
Il est raisonnable pour un visualiseur de refuser de prendre en charge les
demandes de sommaires. C’est ce que fait, par exemple, le visualiseur de pages
Man car le concept de sommaire est trop éloigné de la façon dont fonctionnent
les pages Man ; le visualiseur HyperHelp, en revanche, supporte les sommaires
en passant la requête d’affichage du sommaire directement à HyperHelp sous
Linux et WinHelp sous Windows. Il n’est pas raisonnable, cependant, pour une
implémentation de ICustomHelpViewer de répondre aux requêtes
CanShowTableOfContents avec une valeur true et d’ignorer ensuite les requêtes
ShowTableOfContents.
Implémentation de IExtendedHelpViewer
ICustomHelpViewer est seule à fournir un support direct de l’aide par mot clé.
Certains systèmes d’aide (spécialement WinHelp) opèrent en associant un
nombre (appelé ID de contexte) aux mots clés, de manière interne au système
d’aide et donc de manière invisible pour l’application. De tels systèmes
nécessitent que l’application supporte l’aide par contexte, où l’application
invoque le système d’aide avec un nombre plutôt qu’une chaîne, et que le
système d’aide effectue la traduction du nombre.
Les applications écrites en CLX ou VCL peuvent communiquer avec les systèmes
utilisant l’aide contextuel, en étendant l’objet qui implémente ICustomHelpViewer
afin qu’il implémente également IExtendedHelpViewer. IExtendedHelpViewer prend
aussi en charge la communication avec les systèmes d’aide vous permettant
d’aller directement aux rubriques de haut niveau au lieu d’utiliser les recherches
par mot clé. Le visualiseur intégré WinHelp le fait automatiquement.
IExtendedHelpViewer expose quatre fonctions. Deux d’entre elles,
UnderstandsContext et DisplayHelpByContext, sont utilisées pour supporter l’aide
7-34
Guide du développeur
Activation de l’aide dans les applications
par contexte ; les deux autres, UnderstandsTopic et DisplayTopic, sont utilisées pour
supporter les rubriques.
Lorsque l’utilisateur d’une application appuie sur F1, le gestionnaire d’aide
appelle
int__fastcall IExtendedHelpViewer::UnderstandsContext(const int ContextID, AnsiString
HelpFileName)
et le contrôle actif prend en charge l’aide par contexte et non l’aide par mot clé.
Comme poure ICustomHelpViewer::UnderstandsKeyword(), le gestionnaire d’aide
interroge successivement tous les visualiseurs d’aide recensés. Mais, au contraire
de ICustomHelpViewer::UnderstandsKeyword(), si plusieurs visualiseurs gèrent un
contexte spécifié, c’est le premier visualiseur recensé et gérant le contexte qui est
invoqué.
Le gestionnaire d’aide appelle
void__fastcall IExtendedHelpViewer::DisplayHelpByContext(const int ContextID, AnsiString
HelpFileName)
après avoir consulté les visualiseurs d’aide recensés.
Les fonctions de support des rubriques se comportent de la même façon :
bool__fastcall IExtendedHelpViewer::UnderstandsTopic(const AnsiString Topic)
est utilisée pour demander aux visualiseurs d’aide s’ils supportent une rubrique ;
void__fastcall IExtendedHelpViewer::DisplayTopic(const AnsiString Topic)
est utilisée pour invoquer le premier visualiseur recensé indiquant qu’il peut
fournir de l’aide sur cette rubrique.
Implémentation de IHelpSelector
IHelpSelector est un compagnon de ICustomHelpViewer. Lorsque plusieurs
visualiseurs recensés peuvent assurer le support du mot clé, du contexte ou de la
rubrique spécifié, ou peuvent fournir un sommaire, le gestionnaire d’aide doit
faire un choix entre eux. Dans le cas des contextes ou des rubriques, le
gestionnaire d’aide sélectionne toujours le premier visualiseur d’aide prétendant
assurer le support. Dans le cas des mots clés ou des sommaires, le gestionnaire
d’aide, par défaut, sélectionne le premier visualiseur d’aide. Ce comportement
peut être redéfini par une application.
Pour supplanter la décision du gestionnaire d’aide, une application doit recenser
une classe fournissant une implémentation de l’interface IHelpSelector.
IHelpSelector exporte deux fonctions : SelectKeyword et TableOfContents. Les deux
acceptent comme argument un TStrings contenant, l’un à la suite de l’autre, soit
les correspondances possibles des mots clés, soit les noms des visualiseurs
pouvant fournir un sommaire. L’implémenteur est nécessaire pour renvoyer
l’indice (dans le TStringList) représentant la chaîne sélectionnée, puis le
TStringList est libéré par le gestionnaire d’aide
Remarque
Le gestionnaire d’aide risque de se tromper si les chaînes sont réarrangées ; il est
conseillé que les implémenteurs de IHelpSelector ne le fassent pas. Le système
Création d’applications, de composants et de bibliothèques
7-35
Activation de l’aide dans les applications
d’aide ne supporte qu’un seul HelpSelector ; lorsque de nouveaux sélecteurs sont
recensés, tout sélecteur existant préalablement est déconnecté.
Recensement des objets du système d’aide
Pour que le gestionnaire d’aide communique avec eux, les objets implémentant
ICustomHelpViewer, IExtendedHelpViewer, ISpecialWinHelpViewer et IHelpSelector
doivent se recenser auprès du gestionnaire d’aide.
Pour recenser les objets du système d’aide auprès du gestionnaire d’aide, il
vous faut :
• Recenser le visualiseur d’aide.
• Recenser le sélecteur d’aide.
Recensement des visualiseurs d’aide
L’unité contenant l’implémentation de l’objet doit utiliser HelpIntfs. Une instance
de l’objet doit être déclarée dans le fichier en-tête de l’unité d’implémentation.
L’unité d’implémentation doit contenir une directive pragma startup qui appelle
une méthode affectant la variable d’instance et la transmet à la fonction
RegisterViewer. RegisterViewer est une fonction simple, exportée par HelpIntfs.pas,
qui prend un ICustomHelpViewer en argument et renvoie un IHelpManager. Le
IHelpManager doit être enregistré pour une utilisation ultérieure.
Le fichier .cpp correspondant contient le code qui recense l’interface. Pour
l’interface décrite ci-dessus, le code de recensement à la forme suivante :
void InitServices()
{
THelpImplementor GlobalClass;
Global = dynamic_cast<ICustomHelpViewer*>(GlobalClass);
Global->AddRef;
HelpIntfs::RegisterViewer(Global, GlobalClass->Manager);
}
#pragma startup InitServices
Remarque
Le gestionnaire d’aide doit être libéré dans le destructeur de l’objet GlobalClass
s’il n’a pas déjà été libéré.
Recensement des sélecteurs d’aide
L’unité contenant l’implémentation de l’objet doit utiliser Forms dans la VCL ou
QForms dans la CLX. Une instance de l’objet doit être déclarée dans le fichier
.cpp de l’unité d’implémentation.
L’unité d’implémentation doit recenser le sélecteur d’aide via la propriété
HelpSystem de l’objet global Application :
Application->HelpSystem->AssignHelpSelector(myHelpSelectorInstance)
Cette procédure ne renvoie pas de valeur.
7-36
Guide du développeur
Utilisation de l’aide dans une application VCL
Utilisation de l’aide dans une application VCL
Les sections suivantes expliquent comment utiliser l’aide dans une application
VCL.
•
•
•
•
Comment TApplication traite-il l’aide VCL
Comment les contrôles VCL traitent-ils l’aide
Appel direct à un système d’aide
Utilisation de IHelpSystem
Comment TApplication traite-il l’aide VCL
TApplication dans la VCL fournit quatre méthodes accessibles depuis le code de
l’application :
Tableau 7.5 Méthodes d’aide de TApplication
Méthode
Description
HelpCommand
Transmet HELP_COMMAND, de style aide Windows, à WinHelp. Les
demandes d’aide transmises par le biais de ce mécanisme sont passées
uniquement aux implémentations de IspecialWinHelpViewer.
HelpContext
Invoque le système d’aide en utilisant une requête par contexte.
HelpKeyword
Invoque le système d’aide en utilisant une requête par mot clé.
HelpJump
Demande l’affichage d’une rubrique particulière.
Les quatre fonctions prennent les données qui leurs sont transmises et les font
suivre via une donnée membre de TApplication qui représente le système d’aide.
Cette donnée membre est directement accessible via la propriété HelpSystem.
Comment les contrôles VCL traitent-ils l’aide
Tous les contrôles VCL dérivant de TControl exposent plusieurs propriétés qui
sont utilisées par le système d’aide : HelpType, HelpContext et HelpKeyword.
La propriété HelpType contient une instance d’un type énuméré qui détermine si
le concepteur du contrôle a prévu de fournir l’aide par mot clé ou par contexte.
Si HelpType est définie par htKeyword, le système d’aide s’attend à ce que le
contrôle utilise l’aide par mot clé et il examine uniquement le contenu de la
propriété HelpKeyword. En revanche, si HelpType est définie par htContext, le
système d’aide s’attend à ce que le contrôle utilise l’aide par contexte et il
examine uniquement le contenu de la propriété HelpContext.
En plus des propriétés, les contrôles exposent une seule méthode, InvokeHelp, qui
peut être appelée pour transmettre une requête au système d’aide. Elle ne prend
pas de paramètre et appelle dans l’objet global Application les méthodes qui
correspondent au type d’aide supporté par le contrôle.
Les messages d’aide sont automatiquement invoqués lors de l’appui sur la
touche F1 car la méthode KeyDown de TWinControl appelle InvokeHelp.
Création d’applications, de composants et de bibliothèques
7-37
Utilisation de l’aide dans une application CLX
Utilisation de l’aide dans une application CLX
Les sections suivantes expliquent comment utiliser l’aide dans une application
CLX.
•
•
•
•
Comment TApplication traite-il l’aide CLX
Comment les contrôles CLX traitent-ils l’aide
Appel direct à un système d’aide
Utilisation de IHelpSystem
Comment TApplication traite-il l’aide CLX
TApplication dans CLX fournit deux méthodes accessibles depuis le code de
l’application :
• ContextHelp, qui invoque le système d’aide en utilisant une requête par
contexte
• KeywordHelp, qui invoque le système d’aide en utilisant une requête par
mot clé
Les deux fonctions prennent en argument le contexte ou le mot clé, et fait suivre
la requête à une donnée membre de TApplication, qui représente le système
d’aide. Cette donnée membre est directement accessible via la propriété en
lecture seule HelpSystem.
Comment les contrôles CLX traitent-ils l’aide
Tous les contrôles dérivant de TControl exposent quatre propriétés qui sont
utilisées par le système d’aide : HelpType, HelpFile, HelpContext et HelpKeyword.
HelpFile est censée contenir le nom du fichier où se trouve l’aide du contrôle ; si
l’aide se trouve dans un système d’aide externe ne reconnaissant pas les noms
de fichiers (par exemple, le système des pages Man), la propriété doit être laissée
vierge.
La propriété HelpType contient une instance d’un type énuméré qui détermine si
le concepteur du contrôle a prévu de fournir l’aide par mot clé ou par contexte ;
les deux autres propriétés lui sont liées. Si HelpType est définie par htKeyword, le
système d’aide s’attend à ce que le contrôle utilise l’aide par mot clé et il
examine uniquement le contenu de la propriété HelpKeyword. En revanche, si
HelpType est définie par htContext, le système d’aide s’attend à ce que le contrôle
utilise l’aide par contexte et il examine uniquement le contenu de la propriété
HelpContext.
En plus des propriétés, les contrôles exposent une seule méthode, InvokeHelp, qui
peut être appelée pour transmettre une requête au système d’aide. Elle ne prend
pas de paramètre et appelle dans l’objet global Application les méthodes qui
correspondent au type d’aide supporté par le contrôle.
7-38
Guide du développeur
Appel direct à un système d’aide
Les messages d’aide sont automatiquement invoqués lors de l’appui sur la
touche F1 car la méthode KeyDown de TWidgetControl appelle InvokeHelp.
Appel direct à un système d’aide
Pour les fonctionnalités des systèmes d’aide non fournis par la VCL ou par CLX,
TApplication fournit une propriété, accessible en lecture seulement, qui donne un
accès direct au système d’aide. Cette propriété est une instance d’une
implémentation de l’interface IHelpSystem. IHelpSystem et IHelpManager sont
implémentées par le même objet, mais une interface est utilisée pour permettre à
l’application de communiquer avec le gestionnaire d’aide, et l’autre est utilisée
pour permettre aux visualiseurs d’aide de communiquer avec le gestionnaire
d’aide.
Utilisation de IHelpSystem
IHelpSystem permet à l’application VCL ou CLX de réaliser trois choses :
• Fournit au gestionnaire d’aide les informations de chemin d’accès.
• Fournit un nouveau sélecteur d’aide.
• Demande au gestionnaire d’aide d’afficher l’aide.
Assigner un sélecteur d’aide permet au gestionnaire d’aide de déléguer la prise
de décision au cas où plusieurs systèmes d’aide externes peuvent apporter l’aide
pour le même mot clé. Pour plus d’informations, voir la section “Implémentation
de IHelpSelector” à la page 7-35.
IHelpSystem exporte quatre procédure et une fonction utilisées pour demander au
gestionnaire d’aide d’afficher l’aide :
•
•
•
•
•
ShowHelp
ShowContextHelp
ShowTopicHelp
ShowTableOfContents
Hook
Hook est entièrement destinée à la compatibilité WinHelp et ne doit pas être
utilisée dans une application CLX ; elle permet le traitement des messages
WM_HELP qui ne peuvent pas être directement traduits en requêtes d’aide basée
sur un mot clé, un contexte ou une rubrique. Les autres méthodes prennent
chacune deux arguments : le mot clé, l’ID de contexte ou la rubrique pour lequel
l’aide est demandée, et le fichier d’aide dans lequel on s’attend à la trouver.
En général, sauf si vous demandez une aide par rubrique, il est aussi efficace et
plus clair de transmettre les requêtes au gestionnaire d’aide via la méthode
InvokeHelp de votre contrôle.
Création d’applications, de composants et de bibliothèques
7-39
Personnalisation du système d’aide de l’EDI
Personnalisation du système d’aide de l’EDI
L’EDI de C++Builder supporte les visualiseurs d’aide multiples exactement
comme le fait une application VCL ou CLX : il délègue les demandes d’aide au
gestionnaire d’aide, qui les fait suivre aux visualiseurs d’aide recensés. L’EDI
utilise le même visualiseur d’aide WinHelpViewer que VCL.
Pour installer un nouveau visualiseur d’aide dans l’EDI, faites exactement ce que
vous feriez dans une application VLC ou CLX à une différence près. Vous
écrivez un objet qui implémente ICustomHelpViewer (et, éventuellement,
IExtendedHelpViewer) pour faire suivre les requêtes d’aide au visualiseur externe
de votre choix, et vous recensez le ICustomHelpViewer avec l’EDI.
Pour recenser un visualiseur d’aide personnalisé avec l’EDI
1 Assurez-vous que l’unité implémentant le visualiseur d’aide contient
HelpIntfs.cpp.
2 Construisez l’unité dans un paquet de conception recensé avec l’EDI, et
construisez le paquet en activant l’option Paquets d’exécution. (C’est
nécessaire pour garantir que l’instance du gestionnaire d’aide utilisée par
l’unité est la même que celle utilisée par l’EDI.)
3 Assurez-vous que le visualiseur d’aide existe comme instance globale à
l’intérieur de l’unité.
4 Dans la fonction d’initialisation bloquée par #pragma startup, assurez-vous
que l’instance est transmise à la fonction RegisterHelpViewer.
7-40
Guide du développeur
Chapitre
8
Conception de l’interface utilisateur
des applications
Chapitre 8
Lorsque vous ouvrez C++Builder ou créez un nouveau projet, une fiche vierge
s’affiche à l’écran. Vous concevez l’interface utilisateur de l’application en plaçant
et disposant des composants visuels de la palette des composants (fenêtres,
menus ou boîtes de dialogues) sur la fiche.
Une fois un composant visuel dans la fiche, vous pouvez modifier sa position, sa
taille et d’autres propriétés de conception et coder ses gestionnaires
d’événements. C++Builder prend en charge les détails sous-jacents de la
programmation.
Les sections suivantes décrivent les principales opérations de conception de
l’interface utilisateur : la manipulation des fiches, la création de modèles de
composants, l’ajout de boîtes de dialogue et l’organisation d’actions pour les
menus et les barres d’outils.
Contrôle du comportement de l’application
TApplication, TScreen et TForm sont les classes qui constituent la base de toutes
les applications C++Builder en contrôlant le comportement de votre projet. La
classe TApplication sert de fondation à une application en fournissant les
propriétés et les méthodes qui encapsulent le comportement d’un programme
standard. La classe TScreen est utilisée à l’exécution pour gérer les fiches et les
modules de données chargés, ainsi que pour maintenir des informations
spécifiques au système comme la résolution écran ou les fontes utilisables à
l’affichage. Des instances de la classe TForm servent à construire l’interface
utilisateur de votre application. Les fenêtres et les boîtes de dialogue d’une
application sont basées sur TForm.
Conception de l’interface utilisateur des applications
8-1
Paramétrage des fiches
Manipulation de l’application
La variable globale Application de type TApplication se trouve dans chaque
application utilisant la VCL ou la CLX. Application encapsule l’application et
propose de nombreux services fonctionnant en arrière-plan du programme. Ainsi,
Application gère la manière d’appeler un fichier d’aide depuis les menus de votre
programme. La compréhension du fonctionnement de TApplication est plus
importante pour le concepteur de composants que pour le développeur
d’applications autonomes, mais vous devez définir les options gérées par
Application dans la page Application de la boîte de dialogue Options de projet
(Projet|Options) quand vous créez un projet.
De plus, Application reçoit de nombreux événements qui s’appliquent à
l’application dans son ensemble. Par exemple, l’événement OnActivate vous
permet de réaliser des actions au démarrage de l’application, l’événement OnIdle
vous permet d’exécuter des traitements en arrière-plan lorsque l’application n’est
pas occupée, l’événement OnMessage vous permet d’intercepter les messages
Windows (sous Windows uniquement), l’événement OnEvent vous permet
d’intercepter des événements, etc. Bien que vous ne puissiez pas utiliser l’EDI
pour examiner les propriétés et les événements de la variable globale Application,
un autre composant, TApplicationEvents, intercepte les événements et vous permet
de fournir les gestionnaires d’événements à l’aide de l’EDI.
Gestion de l’écran
Une variable globale de type TScreen, appelée Screen, est créée lors de la création
d’un projet. Screen encapsule l’état de l’écran dans lequel l’application s’exécute.
Parmi les fonctions imparties à Screen, il y a
•
•
•
•
La gestion de l’aspect du curseur.
La taille de la fenêtre dans laquelle s’exécute l’application.
La liste des fontes disponibles pour le périphérique écran.
Divers aspects de l’écran (Windows uniquement).
Si votre application Windows s’exécute sur plusieurs moniteurs, Screen gère une
liste des moniteurs et leurs dimensions afin que vous puissiez effectivement
gérer la disposition de l’interface utilisateur.
Lors de l’utilisation de la CLX pour la programmation multiplate-forme, le
comportement par défaut est le suivant : les applications créent un composant
écran en fonction des informations concernant le périphérique d’écran en cours
et l’assigne à Screen.
Paramétrage des fiches
TForm est la classe essentielle dans la création d’applications disposant d’une
interface utilisateur graphique. Lorsque vous ouvrez C++Builder, ce qui affiche
un projet par défaut, ou lorsque vous créez un nouveau projet, une fiche est
affichée pour vous permettre de démarrer la conception de votre interface
utilisateur.
8-2
Guide du développeur
Paramétrage des fiches
Utilisation de la fiche principale
La première fiche créée et enregistrée dans un projet devient, par défaut, la fiche
principale du projet : c’est la première fiche créée à l’exécution. Quand vous
ajoutez d’autres fiches dans vos projets, vous pouvez en choisir une autre pour
servir de fiche principale à votre application. Par ailleurs, faire d’une fiche la
fiche principale est le moyen le plus simple de la tester à l’exécution : à moins de
changer explicitement l’ordre de création, la fiche principale est la première fiche
affichée lors de l’exécution d’une application.
Pour changer la fiche principale d’un projet :
1 Choisissez Projet|Options et sélectionnez la page Fiche.
2 Dans la boîte liste Fiche principale, sélectionnez la fiche à utiliser comme fiche
principale du projet et choisissez OK.
Désormais, si vous exécutez votre application, la fiche choisie comme fiche
principale s’affiche.
Cacher la fiche principale
Vous pouvez empêcher l’affichage de la fiche principale lors du démarrage de
l’application. Pour ce faire, vous devez utiliser la variable globale Application
(décrite à la section “Manipulation de l’application” à la page 8-2).
Pour masquer la fiche principale au démarrage :
1 Choisissez Projet|Voir le source pour afficher le fichier du projet principal.
2 Ajoutez la ligne suivante après l’appel de Application->CreateForm() et avant
l’appel de Application->Run().
Application->ShowMainForm = false;
3 En utilisant l’inspecteur d’objets, définissez la propriété Visible de votre fiche
principale par false.
Ajout de fiches
Pour ajouter une fiche à votre projet, sélectionnez Fichier|Nouveau|Fiche.
Toutes les fiches d’un projet ainsi que les unités correspondantes sont affichées
dans le gestionnaire de projet (Voir|Gestionnaire de projet) et vous pouvez
afficher la liste des fiches en choisissant Voir|Fiches.
Liaison de fiches
L’ajout d’une fiche au projet ajoute au fichier projet une référence à cette fiche
mais pas aux autres unités du projet. Avant d’écrire du code faisant référence à
la nouvelle fiche, vous devez ajouter une référence à cette fiche dans les fichiers
unité des fiches y faisant référence. Cela s’appelle la liaison de fiche.
Conception de l’interface utilisateur des applications
8-3
Paramétrage des fiches
La liaison de fiche est fréquemment utilisée pour donner accès aux composants
contenus dans une autre fiche. Par exemple, la liaison de fiche est souvent
employée pour permettre à une fiche contenant des composants orientés données
de se connecter aux composants d’accès aux données d’un module de données.
Pour lier une fiche à une autre fiche :
1
2
3
4
Sélectionnez la fiche qui fait référence à une autre.
Choisissez Fichier|Inclure l’en-tête d’unité.
Sélectionnez le nom de l’unité de la fiche qui doit être référencée.
Choisissez OK.
La liaison d’une fiche à une autre se traduit simplement par le fait qu’une fiche
contient l’en-tête de l’unité de l’autre fiche. Par conséquent, la fiche liée et ses
composants se trouvent maintenant dans la portée de l’autre fiche.
Gestion de la disposition
A son niveau le plus élémentaire, vous contrôlez l’organisation de votre interface
utilisateur par la manière de disposer les contrôles dans les fiches. Le choix des
emplacements est reflété par les propriétés Top, Left, Width et Height des
contrôles. Vous pouvez modifier ces valeurs à l’exécution afin de modifier la
position ou la taille des contrôles dans les fiches.
Les contrôles disposent de nombreuses autres propriétés qui leur permettent de
s’adapter automatiquement à leur contenu ou à leur conteneur. Cela vous permet
d’organiser les fiches de telle manière que les différents éléments forment un
tout unifié.
Deux propriétés contrôlent la position et la taille d’un contrôle relativement à
celle de son parent. La propriété Align vous permet d’obliger un contrôle à
s’adapter exactement à un côté spécifié de son parent ou à occuper toute la place
disponible de la zone client du parent une fois les autres contrôles alignés.
Quand le parent est redimensionné, les contrôles alignés sont automatiquement
redimensionnés et restent positionnés le long d’un côté donné du parent.
Si vous voulez qu’un contrôle reste positionné relativement à un côté particulier
de son parent sans toucher ce bord ou être redimensionné pour occuper la
totalité du côté, vous pouvez utiliser la propriété Anchors.
Pour vous assurer qu’un contrôle ne devient ni trop grand ni trop petit, vous
pouvez utiliser la propriété Constraints. Constraints vous permet de spécifier la
hauteur maximum, la hauteur minimum, la largeur maximum et la largeur
minimum du contrôle. Initialisez ces valeurs afin de limiter la taille (en pixels)
de la hauteur et de la largeur du contrôle. Ainsi, en initialisant les contraintes
MinWidth et MinHeight d’un objet conteneur, vous êtes certain que ses objets
enfant sont toujours visibles.
La valeur de Constraints se propage le long de la hiérarchie parent/enfant de
telle manière que la taille d’un objet peut être restreinte car il contient des
enfants alignés qui ont des contraintes de taille. Constraints peut également
empêcher un contrôle d’être mis à l’échelle dans une dimension particulière lors
de l’appel de sa méthode ChangeScale.
8-4
Guide du développeur
Utilisation des fiches
TControl introduit un événement protégé, OnConstrainedResize, de type
TConstrainedResizeEvent :
void __fastcall (__closure *TConstrainedResizeEvent)(System::TObject* Sender, int
&MinWidth, int &MinHeight, int &MaxWidth, int &MaxHeight);
Cet événement vous permet de surcharger les contraintes de taille lors d’une
tentative de redimensionnement du contrôle. Les valeurs des contraintes sont
transmises comme paramètres var, elles peuvent donc être modifiées dans le
gestionnaire d’événement. OnConstrainedResize est publié pour les objets
conteneur (TForm, TScrollBox, TControlBar et TPanel). De plus, les concepteurs de
composants peuvent utiliser ou publier cet événement dans tout descendant de
TControl.
Les contrôles dont le contenu peut changer de taille ont une propriété AutoSize
qui force le contrôle à adapter sa taille à l’objet ou à la fonte qu’il contient.
Utilisation des fiches
Quand vous créez une fiche C++Builder dans l’EDI, C++Builder crée
automatiquement la fiche en mémoire en ajoutant du code au point d’entrée
principal de votre application. C’est généralement le comportement souhaité et
vous n’avez donc rien à y changer. Ainsi, la fiche principale existe pour toute la
durée du programme, il est donc peu probable que vous changiez le
comportement par défaut de C++Builder quand vous créez la fiche de votre
fenêtre principale.
Néanmoins, il n’est pas toujours nécessaire de conserver en mémoire toutes les
fiches de votre application pour toute la durée de l’exécution du programme. Si
vous ne souhaitez pas avoir tout le temps en mémoire toutes les boîtes de
dialogue de votre application, vous pouvez les créer dynamiquement quand vous
voulez les voir apparaître.
Une fiche peut être modale ou non modale. Les fiches modales sont des fiches
avec lesquelles l’utilisateur doit interagir avant de pouvoir passer à une autre
fiche (par exemple une boîte de dialogue impose une saisie de l’utilisateur). Les
fiches non modales sont des fenêtres visibles tant qu’elles ne sont pas masquées
par une autre fenêtre, fermées ou réduites par l’utilisateur.
Contrôle du stockage en mémoire des fiches
Par défaut, C++Builder crée automatiquement en mémoire la fiche principale de
l’application en ajoutant le code suivant au point d’entrée principal de
l’application :
Application ->CreateForm(__classid(TForm1), &Form1);
Cette fonction crée une variable globale portant le même nom que la fiche. Ainsi,
chaque fiche d’une application a une variable globale associée. Cette variable est
un pointeur sur une instance de la classe de la fiche et sert à désigner la fiche
durant l’exécution de l’application. Tout fichier code source (.cpp) qui inclut le
Conception de l’interface utilisateur des applications
8-5
Utilisation des fiches
fichier en-tête (.h) de la fiche peut accéder à la fiche par l’intermédiaire de cette
variable.
Comme la fiche est ajoutée au point d’entrée principal de l’application, elle
apparaît lorsque le programme est exécuté et existe en mémoire pour la durée de
l’application.
Affichage d’une fiche créée automatiquement
Il est possible de créer une fiche au démarrage, mais de ne l’afficher que plus
tard dans l’exécution du programme. Les gestionnaires d’événements de la fiche
utilisent la méthode ShowModal pour afficher une fiche déjà chargée en mémoire :
void __fastcall TMainMForm::FirstButtonClick(TObject *Sender)
{
ResultsForm->ShowModal();
}
Dans ce cas, comme la fiche est déjà en mémoire, il n’est pas nécessaire de créer
une autre instance ou de détruire cette instance.
Création dynamique de fiche
Toutes les fiches de votre application n’ont pas besoin d’être en mémoire
simultanément. Pour réduire la quantité de mémoire nécessaire au chargement
de l’application, vous pouvez créer certaines fiches uniquement quand vous en
avez besoin. Ainsi, une boîte de dialogue n’a besoin d’être en mémoire que
pendant le temps où l’utilisateur interagit avec elle.
Pour spécifier dans l’EDI que la fiche doit être créée à un autre moment pendant
l’exécution, procédez de la manière suivante :
1 Sélectionnez Fichier|Nouveau|Fiche dans le menu principal afin d’afficher la
nouvelle fiche.
2 Retirez la fiche de la liste Fiches créées automatiquement dans la page Fiches
de Projet|Options.
Cela supprime l’appel de la fiche. Vous pouvez également retirer
manuellement la ligne suivante au point d’entrée principal du programme :
Application->CreateForm(__classid(TResultsForm), &ResultsForm);
3 Appelez la fiche au moment souhaité en utilisant la méthode Show de la fiche
si la fiche est non modale ou la méthode ShowModal si la fiche est modale.
Un gestionnaire d’événement de la fiche principale doit créer et détruire une
instance de la fiche résultat. Une manière d’appeler cette fiche consiste à utiliser
la variable globale comme dans le code suivant. Remarquez que ResultsForm
étant une fiche modale, le gestionnaire utilise la méthode ShowModal :
void __fastcall TMainMForm::FirstButtonClick(TObject *Sender)
{
ResultsForm = new TResultsForm(this);
ResultsForm->ShowModal();
delete ResultsForm;
}
8-6
Guide du développeur
Utilisation des fiches
Dans cet exemple, le gestionnaire d’événement supprime la fiche après sa
fermeture, la fiche doit donc être recréée en utilisant new si vous avez besoin de
ResultsForm ailleurs dans l’application. Si la fiche était affichée en utilisant Show,
vous ne pourriez la supprimer dans le gestionnaire d’événement car, après
l’appel de Show, l’exécution du code du gestionnaire se poursuit alors que la
fiche est toujours ouverte.
Remarque
Si vous créez une fiche en utilisant l’opérateur new, assurez-vous que la fiche
n’apparaît pas dans la liste Fiches créées automatiquement de la page Fiches de
la boîte de dialogue Options de projet. En effet, si vous créez une nouvelle fiche
sans avoir détruit la fiche de même nom dans la liste, C++Builder crée la fiche
au démarrage et le gestionnaire d’événement crée une nouvelle instance de la
fiche, ce qui remplace la référence à l’instance auto-créée. L’instance auto-créée
existe toujours mais l’application n’y a plus accès. A la fin du gestionnaire
d’événement, la variable globale ne pointe plus sur une fiche valide. Toute
tentative de déréférencer la variable globale entraînera probablement le blocage
de l’application.
Création de fiches non modales comme fenêtres
Vous devez vous assurer que les variables désignant des fiches non modales
existent tant que la fiche est utilisée. Cela signifie que ces variables doivent avoir
une portée globale. Le plus souvent, vous utiliserez la variable globale
référençant la fiche qui a été créée quand vous avez ajouté la fiche (le nom de
variable qui correspond à la valeur de la propriété Name de la fiche). Si votre
application a besoin d’autres instances de la fiche, déclarez des variables globales
distinctes (de type pointeur sur la classe fiche) pour chaque instance.
Création d’une instance de fiche en utilisant une variable locale
Un moyen fiable de créer une seule instance d’une fiche modale consiste à utiliser
une variable locale du gestionnaire d’événement comme référence à la nouvelle
instance. Si une variable locale est employée, il importe peu que ResultsForm soit
ou non auto-créée. Le code du gestionnaire d’événement ne fait pas référence à
la variable fiche globale. Par exemple :
void __fastcall TMainMForm::FirstButtonClick(TObject *Sender)
{
TResultsForm *rf = new TResultsForm(this);// rf est l’instance locale de la fiche
rf->ShowModal();
// fiche détruite de manière fiable
delete rf;
}
Remarquez que l’instance globale de la fiche n’est jamais utilisée dans cette
version du gestionnaire d’événement.
Habituellement, les applications utilisent les instances globales des fiches.
Cependant, si vous avez besoin d’une nouvelle instance d’une fiche modale alors
que vous utilisez cette fiche dans une portion réduite de votre application (par
exemple dans une seule fonction), une instance locale est normalement le moyen
le plus rapide et le plus fiable de manipuler la fiche.
Conception de l’interface utilisateur des applications
8-7
Utilisation des fiches
Bien entendu, vous ne pouvez pas utiliser de variables locales pour les fiches
non modales dans les gestionnaires d’événements car elles doivent avoir une
portée globale pour garantir que la fiche existe aussi longtemps qu’elle est
utilisée. Show rend la main dès que la fiche est ouverte, donc si vous utilisez une
variable locale, la variable locale sort de portée immédiatement.
Transfert de paramètres supplémentaires aux fiches
Généralement, vous créez les fiches de votre application en utilisant l’EDI.
Quand elle est créée ainsi, la fiche a un constructeur qui prend un argument,
Owner, qui est un pointeur sur le propriétaire de la fiche créée. (Le propriétaire
est l’objet application ou l’objet fiche appelant.) Owner peut avoir la valeur
NULL.
Pour transmettre d’autres arguments à la fiche, créez un constructeur différent et
instanciez la fiche en utilisant l’opérateur new. La classe fiche exemple suivante
utilise un constructeur supplémentaire proposant le paramètre supplémentaire
whichButton. Il faut ajouter manuellement ce nouveau constructeur à la fiche.
class TResultsForm : public TForm
{
// Composants gérés par l’EDI
__published:
TLabel *ResultsLabel;
TButton *OKButton;
void __fastcall OKButtonClick(TObject *Sender);
// Déclarations de l’utilisateur
private:
// Déclarations de l’utilisateur
public:
virtual __fastcall TResultsForm(TComponent* Owner);
virtual __fastcall TResultsForm(int whichButton, TComponent* Owner);
};
Voici le code, créé manuellement, de ce constructeur qui passe le paramètre
supplémentaire whichButton. Ce constructeur utilise le paramètre whichButton
pour initialiser la propriété Caption d’un contrôle Label de la fiche.
void__fastcall TResultsForm::TResultsForm(int whichButton, TComponent* Owner)
: TForm(Owner)
{
switch (whichButton) {
case 1:
ResultsLabel->Caption = "Vous avez choisi le premier bouton!";
break;
case 2:
ResultsLabel->Caption = "Vous avez choisi le second bouton!";
break;
case 3:
ResultsLabel->Caption = "Vous avez choisi le troisième bouton!";
}
}
Quand vous créez une instance d’une fiche disposant de plusieurs constructeurs,
vous pouvez sélectionner le constructeur le mieux adapté à vos besoins.
8-8
Guide du développeur
Utilisation des fiches
Par exemple, le gestionnaire OnClick suivant d’un bouton de la fiche crée une
instance de TResultsForm en utilisant le paramètre supplémentaire :
void __fastcall TMainMForm::SecondButtonClick(TObject *Sender)
{
TResultsForm *rf = new TResultsForm(2, this);
rf->ShowModal();
delete rf;
}
Récupération des données des fiches
La plupart des applications réelles utilisent plusieurs fiches. Bien souvent, il est
nécessaire de transmettre des informations entre ces différentes fiches. Il est
possible de transmettre des informations à une fiche sous la forme des
paramètres du constructeur de la fiche destination ou en affectant des valeurs
aux propriétés de la fiche. La méthode à utiliser pour obtenir des informations
d’une fiche change selon que la fiche est ou non modale.
Récupération de données dans les fiches non modales
Il est facile d’extraire des informations de fiches non modales en appelant des
fonctions membre publiques de la fiche ou en interrogeant ses propriétés. Soit,
par exemple, une application contenant une fiche non modale appelée ColorForm
qui contient une boîte liste appelée ColorListBox contenant une liste de couleurs
(“Rouge”, “Vert”, “Bleu”, etc.). Le nom de couleur sélectionné dans ColorListBox
est automatiquement stocké dans une propriété appelée CurrentColor à chaque
fois que l’utilisateur sélectionne une nouvelle couleur. La déclaration de classe
pour la fiche est la suivante :
class TColorForm : public TForm
{
// Composants gérés par l’EDI
__published:
TListBox *ColorListBox;
void __fastcall ColorListBoxClick(TObject *Sender);
// Déclarations de l’utilisateur
private:
String getColor();
void setColor(String);
String curColor;
// Déclarations de l’utilisateur
public:
virtual __fastcall TColorForm(TComponent* Owner);
__property String CurrentColor = {read=getColor, write=setColor};
};
Le gestionnaire d’événement OnClick de la boîte liste, ColorListBoxClick, initialise
la valeur de la propriété CurrentColor à chaque fois qu’un nouvel élément est
sélectionné. Le gestionnaire d’événement obtient la chaîne dans la boîte liste qui
contient le nom de couleur et l’affecte à CurrentColor. La propriété CurrentColor
utilise la fonction d’affectation, setColor, pour stocker la valeur réelle de la
propriété dans la donnée membre privée curColor :
void __fastcall TColorForm::ColorListBoxClick(TObject *Sender)
{
Conception de l’interface utilisateur des applications
8-9
Utilisation des fiches
int index = ColorListBox->ItemIndex;
if (index >= 0) {// vérifier qu’une couleur est sélectionnée
CurrentColor = ColorListBox->Items->Strings[index];
}
// Pas de couleur sélectionnée
else
CurrentColor = "";
}
//--------------------------------------------------------------------void TColorForm::setColor(String s)
{
curColor = s;
}
Supposons maintenant qu’une autre fiche de l’application, appelée ResultsForm, a
besoin de connaître la couleur actuellement sélectionnée dans ColorForm à chaque
fois qu’un bouton (nommé UpdateButton) de ResultsForm est choisi. Le
gestionnaire d’événement OnClick de UpdateButton doit avoir la forme suivante :
void __fastcall TResultsForm::UpdateButtonClick(TObject *Sender)
{
if (ColorForm) { // Vérifier l’existence de ColorForm
String s = ColorForm->CurrentColor;
// faire quelque chose avec la chaîne nom de la couleur
}
}
Le gestionnaire d’événement commence par vérifier que ColorForm existe en
vérifiant si leur pointeur est NULL. Ensuite, il obtient la valeur de la propriété
CurrentColor de ColorForm. La lecture de la propriété CurrentColor appelle sa
fonction de lecture getColor qui contient le code suivant :
String TColorForm::getColor()
{
return curColor;
}
En procédant autrement, si la fonction getColor de ColorForm est publique, une
autre fiche peut obtenir la couleur en cours sans utiliser la propriété CurrentColor
(par exemple, String s = ColorForm->getColor();). En fait, rien n’empêche l’autre
fiche d’obtenir la couleur sélectionnée dans ColorForm en examinant directement
la valeur sélectionnée dans la boîte liste :
String s = ColorListBox->Items->Strings[ColorListBox->ItemIndex];
Néanmoins, l’utilisation d’une propriété rend l’interface avec ColorForm très claire
et simple. Tout ce qu’une fiche a besoin de savoir sur ColorForm, c’est comment
récupérer la valeur de CurrentColor.
Récupération de données dans les fiches modales
Tout comme les fiches non modales, les fiches modales contiennent souvent des
informations nécessaires à d’autres fiches. Dans le cas de figure la plus classique,
une fiche A lance la fiche modale B. Lors de la fermeture de B, la fiche A a
besoin de savoir ce que l’utilisateur a fait dans la fiche B pour décider comment
poursuivre les traitements de la fiche A. Si la fiche B est toujours en mémoire,
8-10
Guide du développeur
Utilisation des fiches
il est possible de l’interroger via ses propriétés et ses fonctions membres tout
comme les fiches non modales de l’exemple précédent. Mais comment faire si la
fiche B est retirée de la mémoire une fois fermée ? Comme une fiche ne renvoie
pas explicitement de valeur, il est nécessaire de préserver les informations
importantes de la fiche avant de la détruire.
Pour illustrer cette manière de procéder, considérez une version modifiée de la
fiche ColorForm conçue comme une fiche modale. Sa classe est déclarée de la
manière suivante :
class TColorForm : public TForm
{
// Composants gérés par l’EDI
__published:
TListBox *ColorListBox;
TButton *SelectButton;
TButton *CancelButton;
void __fastcall CancelButtonClick(TObject *Sender);
void __fastcall SelectButtonClick(TObject *Sender);
// Déclarations de l’utilisateur
private:
String* curColor;
// Déclarations de l’utilisateur
public:
virtual __fastcall TColorForm(TComponent* Owner);
virtual __fastcall TColorForm(String* s, TComponent* Owner);
};
La fiche contient une boîte liste nommée ColorListBox contenant une liste de
noms de couleur. Quand il est choisi, le bouton nommé SelectButton mémorise le
nom de la couleur sélectionnée dans ColorListBox puis ferme la fiche.
CancelButton est un bouton qui ferme simplement la fiche.
Remarquez l’ajout à la déclaration de la classe d’un constructeur défini par
l’utilisateur qui attend un argument String*. Normalement, ce paramètre String*
pointe sur une chaîne gérée par la fiche qui déclenche ColorForm. Ce constructeur
a l’implémentation suivante :
void__fastcall TColorForm::TColorForm(String* s, TComponent* Owner)
: TForm(Owner)
{
curColor = s;
*curColor = "";
}
Le constructeur enregistre le pointeur dans une donnée membre privée curColor
et initialise la chaîne avec une chaîne vide.
Remarque
Pour utiliser ce constructeur défini par l’utilisateur, la fiche doit être créée
explicitement. Ce ne peut pas être une fiche auto-créée au démarrage de
l’application. Pour davantage d’informations, voir “Contrôle du stockage en
mémoire des fiches” à la page 8-5.
Dans l’application, l’utilisateur sélectionne une couleur dans la boîte liste puis
clique sur le bouton SelectButton pour enregistrer son choix et fermer la fiche.
Conception de l’interface utilisateur des applications
8-11
Utilisation des fiches
Le gestionnaire d’événement OnClick du bouton SelectButton doit avoir la forme
suivante :
void __fastcall TColorForm::SelectButtonClick(TObject *Sender)
{
int index = ColorListBox->ItemIndex;
if (index >= 0)
*curColor = ColorListBox->Items->Strings[index];
Close();
}
Remarquez comment le gestionnaire d’événement stocke le nom de couleur
sélectionné dans l’adresse de chaîne qui a été transmise au constructeur.
Pratiquement, pour utiliser ColorForm, la fiche appelante doit transmettre au
constructeur un pointeur sur une chaîne existante. Supposons par exemple que
ColorForm est instanciée par une fiche appelée ResultsForm en réponse au choix
d’un bouton de ResultsForm nommé UpdateButton. Le gestionnaire d’événement
de ce bouton doit avoir la forme suivante :
void __fastcall TResultsForm::UpdateButtonClick(TObject *Sender)
{
String s;
GetColor(&s);
if (s != "") {
// faire quelque chose avec la chaîne nom de la couleur
}
else {
// faire autre chose car il n’y a pas de couleur sélectionnée
}
}
//--------------------------------------------------------------------void TResultsForm::GetColor(String *s)
{
ColorForm = new TColorForm(s, this);
ColorForm->ShowModal();
delete ColorForm;
ColorForm = 0; // Mettre le pointeur à NULL
}
UpdateButtonClick crée une chaîne nommée s. L’adresse de s est transmise à la
fonction GetColor qui crée ColorForm en transmettant comme argument au
constructeur un pointeur sur s. Dès que ColorForm est fermée, elle est détruite
mais le nom de la couleur sélectionnée, s’il y en a une, est préservé dans s.
Sinon, s contient une chaîne vide, ce qui indique clairement que l’utilisateur est
sorti de ColorForm sans sélectionner une couleur.
Cet exemple utilise une variable chaîne pour stocker des informations provenant
de la fiche modale. Il est possible d’utiliser des objets plus complexes en fonction
de vos besoins. N’oubliez jamais qu’il faut laisser à la fiche appelante un moyen
de savoir que la fiche modale a été fermée sans modification, ni sélection (dans
l’exemple précédent en attribuant par défaut à s une chaîne vide).
8-12
Guide du développeur
Réutilisation des composants et des groupes de composants
Réutilisation des composants et des groupes de composants
C++Builder offre différentes possibilités d’enregistrer et de réutiliser le travail
réalisé avec les composants :
• Les modèles de composants procurent une façon simple et rapide de configurer et
d’enregistrer des groupes de composants. Voir “Création et utilisation des
modèles de composants” à la page 8-13.
• Vous pouvez enregistrer les fiches, les modules de données et les projets dans
le Référentiel. Vous disposez ainsi d’une base de données centrale composée
d’éléments réutilisables et vous pouvez utiliser l’héritage de fiche pour
diffuser les modifications.
• Vous pouvez enregistrer des cadres sur la palette des composants ou dans le
référentiel. Les cadres utilisent l’héritage de fiche et peuvent être incorporés
dans des fiches ou dans d’autres cadres. Voir “Manipulation des cadres” à la
page 8-14.
• Créer un composant personnalisé est la manière la plus complexe de réutiliser
du code, mais c’est celle qui procure le plus de souplesse. Voir chapitre 45,
“Présentation générale de la création d’un composant”.
Création et utilisation des modèles de composants
Vous pouvez créer des modèles composés d’un ou de plusieurs composants.
Après avoir organisé les composants sur une fiche, défini leurs propriétés et écrit
du code pour eux, enregistrez-les sous la forme d’un modèle de composant. Par la
suite, en sélectionnant le modèle dans la palette des composants, vous pouvez
placer les composants préconfigurés sur une fiche en une seule étape ; toutes les
propriétés et tout le code de gestion d’événement associés sont simultanément
ajoutés à votre projet.
Une fois que vous avez placé un modèle sur une fiche, vous pouvez
repositionner les composants indépendamment les uns des autres, redéfinir leurs
propriétés et créer ou modifier Les gestionnaires d’événements qui leur sont
associés comme si vous placiez chaque composant un par un.
Pour créer un modèle de composant,
1 Placez et arrangez des composants sur une fiche. Dans l’inspecteur d’objets,
définissez leurs propriétés et événements.
2 Sélectionnez les composants. La manière la plus simple de sélectionner
plusieurs composants consiste à faire glisser le pointeur de la souris au-dessus
d’eux. Des poignées grises apparaissent dans les coins de chaque composant
sélectionné.
3 Choisissez Composant|Créer un modèle de composant.
4 Spécifiez un nom pour le modèle dans la zone de saisie Information modèle
de composant. Le nom proposé par défaut est le type du premier composant
sélectionné à l’étape 2 suivi du mot “Template”. Par exemple, si vous
sélectionnez un libellé puis une boîte texte, le nom proposé est
Conception de l’interface utilisateur des applications
8-13
Manipulation des cadres
“TLabelTemplate”. Vous pouvez modifier ce nom, en veillant à ne pas utiliser
un nom de composant existant.
5 Dans la boîte de saisie Page de palette, spécifiez la page de la palette des
composants dans laquelle vous souhaitez placer le modèle. Si vous spécifiez
une page qui n’existe pas, une nouvelle page est créée lorsque vous
enregistrez le modèle.
6 A côté de Icône de palette, sélectionnez un bitmap pour représenter le modèle
sur la palette. L’image bitmap proposée par défaut est celle utilisée par le type
du premier composant sélectionné à l’étape 2. Pour rechercher d’autres images
bitmap, cliquez sur Changer. Les dimensions du bitmap que vous choisissez
ne doivent pas dépasser 24 pixels sur 24 pixels.
7 Cliquez sur OK.
Pour supprimer des modèles de la palette des composants, choisissez
Composant|Configurer la palette.
Manipulation des cadres
Un cadre (TFrame), comme une fiche, est un conteneur pour d’autres
composants. Il utilise le même mécanisme de possession que les fiches lorsque
les composants y sont instanciés et détruits automatiquement, et la même
relation parent-enfant pour la synchronisation des propriétés de composants.
A divers égards, un cadre s’apparente davantage à un composant personnalisé
qu’à une fiche. Les cadres peuvent être enregistrés sur la palette des composants
pour en faciliter la réutilisation et imbriqués dans des fiches, dans d’autres
cadres ou autres objets conteneur. Une fois qu’un cadre a été créé et enregistré, il
continue de fonctionner comme une unité et d’hériter des modifications des
composants qu’il contient (y compris d’autres cadres). Lorsqu’un cadre est
incorporé dans un autre cadre ou dans une fiche, il continue d’hériter des
modifications apportées au cadre dont il dérive.
Les cadres servent à organiser les groupes de contrôles utilisés en plusieurs
endroits de votre application. Par exemple, si vous avez un bitmap utilisé par
plusieurs fiches, vous pouvez le placer dans un cadre afin qu’une seule copie de
ce bitmap soit incluse dans les ressources de votre application. Vous pouvez
également décrire un ensemble de champs de saisie servant à modifier une table
avec un cadre et l’utiliser chaque fois que vous souhaitez entrer les données de
la table.
Création de cadres
Pour créer un cadre vide, choisissez Fichier|Nouveau|Cadre, ou Fichier|
Nouveau|Autre et double-cliquez sur Cadre. Vous pouvez alors déposer des
composants (y compris d’autres cadres) sur le nouveau cadre.
8-14
Guide du développeur
Manipulation des cadres
Il est généralement préférable, bien que non nécessaire, d’enregistrer les cadres
en tant que partie d’un projet. Si vous souhaitez créer un projet ne contenant
que des cadres et aucune fiche, choisissez Fichier|Nouveau|Application, fermez
la nouvelle fiche et la nouvelle unité sans les enregistrer, puis choisissez Fichier|
Nouveau|Cadre et enregistrez le projet.
Remarque
Lorsque vous enregistrez des cadres, évitez d’utiliser les noms par défaut Unit1,
Project1 etc., car ils peuvent être à la source de conflits au moment de
l’utilisation ultérieure des cadres.
A la conception, vous pouvez afficher n’importe quel cadre contenu dans le
projet en cours en choisissant Voir|Fiches et en sélectionnant le cadre. Comme
dans le cas des fiches et des modules de données, vous pouvez passer du
concepteur de fiche au fichier fiche du cadre en cliquant avec le bouton droit et
en choisissant Voir comme fiche ou Voir comme texte.
Ajout de cadres à la palette des composants
Les cadres sont ajoutés à la palette des composants comme des modèles de
composants. Pour ajouter un cadre à la palette des composants, ouvrez le cadre
dans le concepteur de fiche (vous ne pouvez pas utiliser un cadre incorporé dans
un autre composant), cliquez avec le bouton droit sur le cadre et choisissez
Ajouter à la palette. Lorsque la boîte de dialogue Information modèle de
composant s’ouvre, sélectionnez un nom, une page de palette et une icône pour
le nouveau modèle.
Utilisation et modification des cadres
Pour utiliser un cadre dans une application, vous devez le placer, directement ou
indirectement, sur une fiche. Vous pouvez ajouter des cadres directement sur des
fiches, sur d’autres cadres ou sur d’autres objets conteneur, comme des volets et
des boîtes de défilement.
Le concepteur de fiche permet d’ajouter un cadre à une application de deux
manières :
• Sélectionnez un cadre à partir de la palette des composants et déposez-le sur
une fiche, un autre cadre ou un autre objet conteneur. Si nécessaire, le
concepteur de fiche demande s’il est possible d’inclure le fichier unité du
cadre dans votre projet.
• Sélectionnez Cadres à partir de la page Standard de la palette des composants
et cliquez sur une fiche ou un autre cadre. Une boîte de dialogue s’ouvre sur
une liste de cadres figurant déjà dans votre projet ; sélectionnez-en un et
cliquez sur OK.
Lorsque vous déposez un cadre sur une fiche ou un autre conteneur, C++Builder
déclare une nouvelle classe qui dérive du cadre que vous avez sélectionné. De
même, lorsque vous ajoutez une nouvelle fiche à un projet, C++Builder déclare
une nouvelle classe qui dérive de TForm. Cela signifie que les modifications
apportées ultérieurement au cadre d’origine (ancêtre) sont répercutées sur le
Conception de l’interface utilisateur des applications
8-15
Manipulation des cadres
cadre incorporé, mais que les modifications apportées au cadre incorporé ne sont
pas répercutées sur le cadre ancêtre.
Supposons que vous souhaitiez regrouper des composants d’accès aux données
et des contrôles orientés données en vue d’une utilisation fréquente,
éventuellement dans plusieurs applications. Pour ce faire, vous pourriez
rassembler les composants dans un modèle de composant ; mais si vous
commencez à utiliser le modèle et changez d’avis ultérieurement sur
l’organisation des contrôles, vous devez faire marche arrière et modifier
manuellement dans chaque projet la partie sur laquelle le modèle a été placé.
Par contre, si vous placez vos composants base de données dans un cadre, les
modifications ultérieures ne doivent être apportées que dans un seul endroit ; les
modifications apportées à un cadre d’origine sont automatiquement répercutées
sur ses descendants incorporés lors de la recompilation des projets.
Parallèlement, vous pouvez modifier n’importe quel cadre incorporé sans affecter
le cadre d’origine ni aucun de ses descendants incorporés. La seule restriction à
la modification des cadres incorporés est que vous ne pouvez pas leur ajouter
des composants.
Figure 8.1
Cadre avec des contrôles orientés données et un composant source de données
Outre une simplification de gestion, les cadres procurent une efficacité
supplémentaire dans l’utilisation des ressources. Par exemple, pour utiliser une
image bitmap ou un autre graphique dans une application, vous pouvez charger
le graphique dans la propriété Picture d’un contrôle TImage. Toutefois, si vous
utilisez fréquemment le même graphique dans une application, chaque objet
Image que vous placez sur une fiche génère une autre copie du graphique ajouté
au fichier ressource de la fiche. Cela est également vrai si vous définissez
TImage::Picture une fois et enregistrez le contrôle Image en tant que modèle de
composant. Une meilleure solution consiste à déposer l’objet Image sur un cadre,
à y charger le graphique puis à utiliser le cadre là où vous souhaitez que le
graphique apparaisse. Cela génère des fichiers fiche moins volumineux et
présente en outre la possibilité de modifier le graphique partout où il figure en
modifiant l’objet Image sur le cadre d’origine.
Partage des cadres
Vous pouvez partager un cadre avec les autres développeurs de deux manières :
• Ajouter le cadre au référentiel d’objet.
• Distribuer l’unité du cadre (.cpp et .h) et les fichiers fiche (.dfm ou .xfm).
8-16
Guide du développeur
Développement de boîtes de dialogue
Pour ajouter un cadre au référentiel, ouvrez n’importe quel projet contenant le
cadre, cliquez avec le bouton droit dans le concepteur de fiche et choisissez
Ajouter au référentiel. Pour davantage d’informations, voir “Utilisation du
référentiel d’objets” à la page 7-27.
Si vous envoyez une unité de cadre et les fichiers fiche à d’autres développeurs,
ils peuvent les ouvrir et les ajouter à la palette des composants. Si le cadre
contient d’autres cadres, ils devront l’ouvrir en tant que partie d’un projet.
Développement de boîtes de dialogue
Les composants boîte de dialogue de la page Dialogues de la palette des
composants permettent d’utiliser dans vos applications diverses boîtes de
dialogue. Ces boîtes de dialogue donnent à vos applications une interface
familière et cohérente dans laquelle l’utilisateur effectue certaines opérations
communes sur les fichiers, comme l’ouverture, l’enregistrement ou l’impression.
Ces boîtes de dialogue affichent et/ou obtiennent des données.
Chaque boîte de dialogue s’ouvre lorsque sa méthode Execute est appelée.
Execute renvoie une valeur booléenne : si l’utilisateur choisit OK pour accepter
les modifications apportées dans la boîte de dialogue, Execute renvoie true ; s’il
choisit Annuler pour quitter la boîte de dialogue sans rien modifier, Execute
renvoie false.
CLX
Si vous écrivez une application multiplate-forme, vous pouvez utiliser les boîtes
de dialogue proposés par CLX dans l’unité QDialogs. Pour les systèmes
d’exploitation disposant d’un type de boîte de dialogue natif pour les opérations
courantes, comme l’ouverture ou l’enregistrement d’un fichier ou la modification
de fonte ou de couleur, vous pouvez utiliser la propriété UseNativeDialog.
Attribuez la valeur true à UseNativeDialog si vous savez que votre application
sera exécutée dans un environnement qui possède ces boîtes de dialogue natives,
et si vous préférez les utiliser au lieu des boîtes de dialogue Qt.
Utilisation des boîtes de dialogue d’ouverture
TOpenDialog est le composant boîte de dialogue le plus couramment utilisé. Il est
généralement employé par une option de menu Nouveau ou Ouvrir dans le
menu Fichier de la barre de menus principale d’une fiche. La boîte de dialogue
contient des contrôles qui vous permettent de sélectionner des groupes de
fichiers en utilisant un caractère joker et de naviguer dans les répertoires.
Le composant TOpenDialog permet d’utiliser une boîte de dialogue Ouvrir dans
votre application. La fonction de cette boîte de dialogue est de permettre à
l’utilisateur de spécifier un fichier à ouvrir. Utilisez la méthode Execute pour
afficher la boîte de dialogue.
Quand l’utilisateur choisit OK dans la boîte de dialogue, le nom du fichier
sélectionné par l’utilisateur est stocké dans la propriété FileName de TOpenDialog.
Vous pouvez ensuite utiliser cette valeur à votre guise.
Conception de l’interface utilisateur des applications
8-17
Organisation des actions pour les barres d’outils et les menus
L’exemple de code suivant peut être placé dans une Action et lié à la propriété
Action du sous-élément d’un menu TMainMenu ou placé dans l’événement
OnClick du sous-élément :
if(OpenDialog1->Execute()){
filename = OpenDialog1->FileName;
};
Ce code affiche la boîte de dialogue et si l’utilisateur choisit le bouton OK, le
nom du fichier sélectionné est copié dans la variable filename de type AnsiString
préalablement déclarée.
Organisation des actions pour les barres d’outils et les menus
C++Builder offre plusieurs fonctionnalités qui simplifieront votre travail de
création, de personnalisation et de maintenance des menus et des barres d’outils.
Ces fonctionnalités vous permettent d’organiser les listes des actions que les
utilisateurs de votre application peuvent déclencher en appuyant sur un bouton
dans une barre d’outils, en choisissant une commande dans un menu ou en
cliquant sur une icône.
Très souvent, un même ensemble d’actions est utilisé pour plusieurs éléments de
l’interface utilisateur. Par exemple, les commandes Couper, Copier et Coller
apparaissent fréquemment à la fois dans un menu Edition et dans une barre
d’outils. Il vous suffit d’ajouter l’action une fois pour l’utiliser dans plusieurs
éléments de l’interface utilisateur de votre application.
Sur la plate-forme Windows, il existe des outils pour vous aider à définir et à
grouper les actions, à créer diverses dispositions et à personnaliser les menus
lors de la conception ou de l’exécution. Ces outils sont appelés outils
ActionBand, les menus et les barres d’outils que vous créez en les utilisant sont
appelés bandes d’actions. En général, vous pouvez créer une interface utilisateur
ActionBand comme suit :
• Construisez une liste d’actions afin de créer un ensemble d’actions disponibles
pour votre application (utilisez le gestionnaire d’actions, TActionManager)
• Ajoutez les éléments de l’interface utilisateur à l’application (utilisez des
composants ActionBand comme TActionMainMenuBar et TActionToolBar)
• “Glissez-déplacez” des actions du gestionnaire d’actions sur les éléments de
l’interface utilisateur
8-18
Guide du développeur
Organisation des actions pour les barres d’outils et les menus
Le tableau suivant définit la terminologie qui s’applique à la définition des
menus et des barres d’outils :
Tableau 8.1 Terminologie de la définition des actions
Terme
Définition
Action
Une réponse à ce que fait l’utilisateur, comme cliquer sur un élément
de menu. De nombreuses actions standard fréquement nécessaires sont
fournies ; vous pouvez les utiliser telles quelles dans vos applications.
Par exemple, sont incluses les opérations sur les fichiers, comme
ouvrir, enregistrer sous, exécuter et quitter, ainsi que de nombreuses
autres pour l’édition, le formatage, la recherche, les dialogues ou les
actions sur les fenêtres. Vous pouvez également programmer des
actions personnalisées et utiliser les listes et le gestrionnaire d’actions
pour y accéder.
Bande d’action
Un conteneur pour un ensemble d’actions associé à un menu ou à une
barre d’outils personnalisés. Les composants ActionBand pour les
menus principaux et les barres d’outils (TActionMainMenuBar et
TActionToolBar) sont des exemples de bandes d’actions.
Catégorie d’action
Vous permet de grouper des actions et de les introduire en tant que
groupe dans un menu ou une barre d’outils. Par exemple, une des
catégories d’actions standard, Search, inclut les actions Find, FindFirst,
FindNext et Replace et les ajoute toutes en même temps.
Classes d’actions
Les classes qui réalisent les actions utilisées dans votre application.
Toutes les actions standard sont définies dans des classes d’actions
comme TEditCopy, TEditCut et TEditUndo. Vous pouvez utiliser ces
classes en les faisant glisser de la boîte de dialogue Personnalisation à
une bande d’action.
Client d’action
Représente la plupart du temps l’élément de menu ou le bouton qui
reçoit une notification pour déclencher une action. Quand le client
reçoit une commande utilisateur (par exemple, un clic de souris), il
initie une action associée.
Liste d’actions
Maintiennent la liste des actions par lesquelles votre application
répond à ce que fait l’utilisateur.
Gestionnaire
d’actions
Groupe et organise des ensembles logiques d’actions pouvant être réutilisés dans les composants ActionBand. Voir TActionManager.
Menu
Présente la liste des commandes que l’utilisateur de l’application peut
exécuter en cliquant dessus. Vous pouvez créer des menus en utilisant
la classe menu ActionBand TActionMainMenuBar, ou en utilisant des
composants multiplates-formes comme TMainMenu ou TPopupMenu.
Cible
Représente l’élément auquel s’applique l’action. La cible est
généralement un contrôle, par exemple un mémo ou un contrôle de
données. Certaines actions n’ont pas de cible. Ainsi, les actions
standard d’aide ne tiennent pas compte de la cible mais démarrent
simplement le système d’aide.
Barre d’outils
Affiche une ligne visible d’icônes de boutons qui, lorsque l’utilisateur
clique dessus, font accomplir au programme une action, comme
imprimer le document en cours. Vous pouvez créer des barres d’outils
en utilisant le composant barre d’outils ActionBand TActionToolBar, ou
en utilisant le composant multiplate-forme TToolBar.
Si vous développez pour plusieurs plates-formes, voir “Utilisation des listes
d’actions” à la page 8-26.
Conception de l’interface utilisateur des applications
8-19
Organisation des actions pour les barres d’outils et les menus
Qu’est-ce qu’une action ?
Pendant que vous développez votre application, vous pouvez créer un ensemble
d’actions utilisables dans divers éléments de l’interface utilisateur. Vous pouvez
les organiser en catégories et les insérer dans un menu en tant qu’ensemble (par
exemple, Couper, Copier et Coller) ou un seul à la fois (par exemple, Outils|
Personnaliser).
Une action correspond à un ou plusieurs éléments de l’interface utilisateur,
comme les commandes de menu ou les boutons des barres d’outils. Les actions
ont deux utilités : (1) elles représentent des propriétés communes à des éléments
de l’interface utilisateur, par exemple l’état d’un contrôle activé ou coché, et (2)
elles répondent lorsqu’un contrôle est déclenché, comme lorsque l’utilisateur de
l’application clique sur un bouton ou choisit un élément de menu. Vous pouvez
créer un catalogue d’actions disponibles dans votre application via des menus,
des boutons, des barres d’outils, des menus contextuels, etc.
Les actions sont associées à d’autres composants :
• Clients :Un ou plusieurs clients utilisent l’action. Le client souvent représente
un élément de menu ou un bouton (par exemple, TToolButton, TSpeedButton,
TMenuItem, TButton, TCheckBox, TRadioButton, etc.). Des actions se trouvent
également dans les composants ActionBand comme TActionMainMenuBar et
TActionToolBar. Quand le client reçoit une commande de l’utilisateur (par
exemple, un clic de souris), il initie une action associée. Généralement,
l’événement OnClick d’un client est associé à l’événement OnExecute de son
action.
• Cible :L’action agit sur la cible. La cible est généralement un contrôle, par
exemple un mémo ou un contrôle de données. Les développeurs de
composants peuvent créer des actions spécifiques aux besoins des contrôles
qu’ils conçoivent et utilisent, puis empaqueter ces unités pour créer des
applications plus modulaires. Certaines actions n’ont pas de cible. Ainsi, les
actions standard d’aide ne tiennent pas compte de la cible mais démarrent
simplement le système d’aide.
Une cible peut également être un composant. Par exemple, les contrôles de
données changent la cible par un ensemble de données associé.
Le client influence l’action — l’action répond lorsqu’un client déclenche l’action.
L’action influence également le client — les propriétés des actions mettent à jour
dynamiquement les propriétés des clients. Par exemple, si, à l’exécution, une
action est désactivée (en initialisant sa propriété Enabled à false), chaque client de
cette action est désactivé et apparaît estompé.
Vous pouvez ajouter, supprimer et réorganiser des actions en utilisant le
gestionnaire d’actions ou l’éditeur de liste d’actions (qui s’affiche lorsque vous
double-cliquez sur un objet liste d’actions, TActionList). Ces actions sont ensuite
connectées aux contrôles client.
8-20
Guide du développeur
Organisation des actions pour les barres d’outils et les menus
Définition des bandes d’actions
Les actions ne conservant aucune information de “disposition” (ni apparence ni
position), C++Builder propose les bandes d’actions capables de stocker ces
données. Les bandes d’actions fournissent un mécanisme permettant de spécifier
des informations de disposition et un jeu de contrôles. Vous pouvez afficher les
actions comme des éléments de l’interface utilisateur tels que barres d’outils ou
menus.
Vous organisez des ensembles d’actions en utilisant le gestionnaire d’actions
(TActionManager). Vous pouvez soit utiliser des actions standard soit créer les
vôtres.
Créez ensuite les bandes d’actions :
• Utilisez TActionMainMenuBar pour créer un menu principal.
• Utilisez TActionToolBar pour créer une barre d’outils.
Les bandes d’actions agissent comme conteneurs qui contiennent et affichent les
ensembles d’actions. Pendant la conception, vous pouvez “glisser-déplacer” des
éléments de l’éditeur du gestionnaire d’actions à la bande d’action. A l’exécution,
les utilisateurs peuvent également personnaliser les menus ou les barres d’outils
de l’application en utilisant une boîte de dialogue semblable à l’éditeur du
gestionnaire d’actions.
Création des barres d’outils et des menus
Remarque
Cette section décrit la méthode recommandée pour créer des menus et des barres
d’outils dans les applications Windows. Pour le développement multiplate-forme,
vous devez utiliser TToolBar et les composants menu, comme TMainMenu, et les
organiser en utilisant les listes d’actions (TActionList). Voir “Définition des listes
d’actions” à la page 8-26.
Vous utilisez le gestionnaire d’actions pour générer automatiquement des barres
d’outils et des menus principaux en fonction des actions contenues dans votre
application. Le gestionnaire d’actions gère les actions standard ainsi que les
actions personnalisées que vous aurez éventuellement écrites. Créez ensuite les
éléments de l’interface utilisateur basés sur ces actions et utilisez les bandes
d’actions pour afficher ces éléments d’action en tant qu’éléments dans un menu
ou en tant que boutons dans une barre d’outils.
La procédure générale de création des menus, barres d’outils et autres bandes
d’actions comporte les étapes suivantes :
• Placer un gestionnaire d’actions sur une fiche.
• Ajouter des actions au gestionnaire d’actions, qui les organise en listes
d’actions adaptées.
• Créer les bandes d’actions (c’est-à-dire, le menu ou la barre d’outils) pour
l’interface utilisateur.
• Placer les actions dans l’interface de l’application par glisser-déplacer.
Conception de l’interface utilisateur des applications
8-21
Organisation des actions pour les barres d’outils et les menus
La procédure suivante décrit ces étapes en détail :
Pour créer des menus et des barres d’outils en utilisant les bandes d’actions :
1 Depuis la page Supplément de la palette des composants, placez un
composant gestionnaire d’actions (TActionManager) sur la fiche dans laquelle
vous voulez créer la barre d’outils ou le menu.
2 Si vous voulez qu’il y ait des images sur le menu ou la barre d’outils, placez
sur la fiche un composant liste d’images (ImageList) en le prenant dans la
page Win32 de la palette des composants. (Vous devez ajouter les images que
vous souhaitez utiliser à cette liste d’images ou utilisez les images fournies.)
3 Depuis la page Supplément de la palette des composants, placez sur la fiche
un ou plusieurs des bandes d’actions suivantes :
• TActionMainMenuBar (pour concevoir des menus principaux)
• TActionToolBar (pour concevoir des barres d’outils)
4 Connectez la liste d’images au gestionnaire d’actions : la focalisation étant sur
le gestionnaire d’actions, sélectionnez dans l’inspecteur d’objets le nom de la
liste d’images dans la propriété Images.
5 Ajoutez des actions au volet action de l’éditeur du gestionnaire d’actions :
• Double-cliquez sur le gestionnaire d’actions afin d’afficher l’éditeur du
gestionnaire d’actions.
• Cliquez sur la flèche déroulante située à côté du bouton Nouvelle action (le
bouton le plus à gauche dans le coin supérieur droit de la page Actions,
comme illustré par la figure 8.2) et sélectionnez Nouvelle action ou
Nouvelle action standard. Une vue arborescente s’affiche. Ajoutez une ou
plusieurs actions, ou catégories d’actions, au volet actions du gestionnaire
d’actions. Le gestionnaire d’actions ajoute les actions à ses listes d’actions.
Figure 8.2
Editeur du gestionnaire d’actions
Bouton Nouvelle action et
bouton déroulant
6 “Glissez-déplacez” des actions indépendantes, ou des catégories d’actions, de
l’éditeur du gestionnaire d’actions au menu ou à la barre d’outils que vous
concevez.
8-22
Guide du développeur
Organisation des actions pour les barres d’outils et les menus
Pour ajouter des actions définies par l’utilisateur, créez une nouvelle TAction en
cliquant sur le bouton Nouvelle action et en écrivant le gestionnaire d’événement
qui définit la réponse qu’elle donnera à son déclenchement. Voir “Que se passet-il lors du déclenchement d’une action ?” à la page 8-27 pour plus de détails.
Lorsque vous avez défini les actions, vous pouvez les placer dans les menus ou
les barres d’outils par glisser-déplacer comme les actions standard.
Ajout de couleurs, de motifs ou d’images aux menus, boutons et barres
d’outils
Vous pouvez utiliser les propriétés Background et BackgroundLayout pour spécifier
la couleur, le motif ou le bitmap à utiliser sur un élément de menu ou un
bouton. Ces propriétés vous permettent également de définir une bannière qui se
place à gauche ou à droite d’un menu.
Assignez des fonds et des dispositions aux sous-éléments à partir de leurs objets
client action. Si vous voulez définir le fond des éléments d’un menu, cliquez
dans le concepteur de fiches sur l’élément de menu qui contient ces éléments.
Par exemple, sélectionner Fichier vous permet de changer le fond des éléments
apparaissant dans le menu Fichier. Vous pouvez attribuer une couleur, un motif,
ou un bitmap à la propriéte Background dans l’inspecteur d’objets.
Utilisez la propriété BackgroundLayout pour décrire comment placer le fond sur
l’élément. Les couleurs ou les images peuvent être placées derrière le libellé
normalement, étirées pour remplir à la zone, ou en mosaïque de petits carrés
recouvrant toute la zone.
Les éléments dont le fond est normal (blNormal), étiré (blStretch), ou en
mosaïque (blTile) sont affichés avec un fond transparent. Si vous créez une
bannière, l’image complète est placée à gauche (blLeftBanner) ou à droite
(blRightBanner) de l’élément. Vous devez vous assurer que la taille convient car
elle ne’st ni réduite ni agrandie.
Pour modifier le fond d’une bande d’action (c’est-à-dire, dans un menu principal
ou une barre d’outils) sélectionnez la bande d’action et choisissez la
TActionClientBar via l’éditeur de collection de bandes d’actions. Vous pouvez
définir les propriétés Background et BackgroundLayout pour spécifier la couleur, le
motif ou le bitmap à utiliser sur toute la barre d’outils ou tout le menu.
Ajout d’icônes aux menus et aux barres d’outils
Vous pouvez ajouter des icônes à côté des éléments de menu ou remplacer les
libellés figurant dans les barres d’outils par des icônes. Vous organiserez les
bitmaps ou les icônes au moyen d’un composant ImageList.
1 A partir de la page Win32 de la palette des composants, placez sur la fiche un
composant ImageList.
2 Ajoutez à la liste d’images les images que vous voulez utiliser. Double-cliquez
sur l’icône ImageList. Cliquez sur Ajouter et naviguez jusqu’aux images que
vous voulez utiliser ; cliquez sur OK lorsque vous en avez terminé. Vous
trouverez des exemples d’images dans Program Files\Common Files\Borland
Shared\Images. (Les images des boutons comprennent deux vues pour les
états actif et inactif des boutons.)
Conception de l’interface utilisateur des applications
8-23
Organisation des actions pour les barres d’outils et les menus
3 Depuis la page Supplément de la palette des composants, placez sur la fiche
une ou plusieurs des bandes d’actions suivantes :
• TActionMainMenuBar (pour concevoir des menus principaux)
• TActionToolBar (pour concevoir des barres d’outils)
4 Connectez la liste d’images au gestionnaire d’actions. Donnez d’abord la
focalisation au gestionnaire d’actions. Ensuite, sélectionnez dans l’inspecteur
d’objets le nom de la liste d’images dans la propriété Images, par exemple
ImageList1.
5 Utilisez l’éditeur du gestionnaire d’actions pour ajouter des actions au
gestionnaire d’actions. Vous pouvez associer une image à une action en
définissant sa propriété ImageIndex par le numéro d’ordre de l’image dans la
liste d’images.
6 “Glissez-déplacez” des actions indépendantes ou des catégories d’actions de
l’éditeur du gestionnaire d’actions au menu ou à la barre d’outils.
7 Pour les barres d’outils, où vous voulez uniquement afficher l’icône et pas le
libellé : sélectionnez la bande d’action Barre d’outils et double-cliquez sur sa
propriété Items. Dans l’éditeur de collections, vous pouvez sélectionner un ou
plusieurs éléments et définir leurs propriétés Caption.
8 Les images apparaissent automatiquement dans le menu ou la barre d’outils.
Création de barres d’outils et de menus personnalisables par l’utilisateur
Vous pouvez utiliser les bandes d’actions avec le gestionnaire d’actions pour
créer des menus et des barres d’outils personnalisables. A l’exécution, les
utilisateurs peuvent personnaliser les barres d’outils et les menus (bandes
d’actions) de l’interface utilisateur de l’application en utilisant une boîte de
dialogue semblable à l’éditeur du gestionnaire d’actions.
Pour permettre aux utilisateurs de personnaliser une bande d’action de
l’application qu’ils exécutent :
1 Placez un composant gestionnaire d’actions sur une fiche.
2 Placez vos composants bande d’action (TActionMainMenuBar, TActionToolBar).
3 Double-cliquez sur le gestionnaire d’actions afin d’afficher l’éditeur du
gestionnaire d’actions.
• Sélectionnez les actions que vous voulez utiliser dans votre application.
Ajoutez également l’action Customize, qui apparaît à la fin de la liste des
actions standard.
• Placez sur la fiche un composant TCustomizeDlg depuis l’onglet Supplément,
connectez-le au gestionnaire d’actions en utilisant sa propriété
ActionManager. Spécifiez un nom de fichier pour enregistrer dans un flux
les personnalisations effectuées par les utilisateurs.
• Placez les actions dans les composants bande d’action par glisser-déplacer.
(Vérifiez que vous avez ajouté l’action Customize à la barre d’outils ou au
menu.)
8-24
Guide du développeur
Organisation des actions pour les barres d’outils et les menus
4 Terminez l’application.
Lorsque vous compilez et exécutez l’application, les utilisateurs ont accès à une
commande Personnaliser qui affiche une boîte de dialogue de personnalisation
semblable à l’éditeur du gestionnaire d’actions. Ils peuvent “glisser-déplacer” des
éléments de menu et créer des barres d’outils en utilisant les actions que vous
avez fournies dans le gestionnaire d’actions.
Cacher les éléments et les catégories inutilisés dans les bandes d’actions
Un des avantages de l’utilisation des ActionBands est que les éléments et les
catégories inutilisés peuvent être cachés à l’utilisateur. Avec le temps, les bandes
d’actions s’adaptent aux utilisateurs de l’application en montrant les éléments
qu’ils utilisent et en cachant les autres. Les éléments cachés peuvent redevenir
visibles : il suffit que l’utilisateur appuie sur un bouton déroulant. De plus,
l’utilisateur peut restaurer la visibilité de tous les éléments d’une bande d’action
en réinitialisant les statistiques d’usage dans la boîte de dialogue de
personnalisation. Le masquage des éléments fait partie du comportement par
défaut des bandes d’actions, mais ce comportement peut être modifié afin
d’inhiber le masquage d’éléments particuliers, de tous les éléments d’une
collection particulière (par exemple, le menu Fichier), ou de tous les éléments
d’une bande d’action particulière.
Le gestionnaire d’actions mémorise le nombre de fois qu’une action a été
invoquée par l’utilisateur en l’enregistrant dans le champ UsageCount du
TActionClientItem correspondant. Le gestionnaire d’actions enregistre également le
nombre de fois que l’application a été exécutée (ce qui correspond au numéro de
la dernière session), ainsi que le numéro de la session où une action a été utilisée
pour la dernière fois. La valeur de UsageCount sert à rechercher le nombre
maximal de sessions pendant lesquelles un élément est inutilisé avant d’être
masqué, il ensuite est comparé avec la différence entre le numéro de session
actuel et le numéro de session de la dernière utilisation de l’élément. Si cette
différence est supérieure au nombre défini dans PrioritySchedule, l’élément est
caché. Les valeurs par défaut de PrioritySchedule sont indiquées dans le tableau
suivant :
Tableau 8.2 Valeurs par défaut de la propriété PrioritySchedule du gestionnaire d’actions
Nombre de sessions dans
lesquelles un élément d’une
bande d’action a été utilisé
Nombre de sessions pendant lesquelles un élément
restera visible après sa dernière utilisation
0, 1
3
2
6
3
9
4, 5
12
6-8
17
9-13
23
14-24
29
25 ou plus
31
Conception de l’interface utilisateur des applications
8-25
Utilisation des listes d’actions
Il est possible de désactiver le masquage d’un élément au moment de la
conception. Pour empêcher le masquage d’une action particulière (et de toutes
les collections qui la contiennent), recherchez son objet TActionClientItem et
définissez sa propriété UsageCount par -1. Pour empêcher le masquage d’une
collection entière d’éléments, comme le menu Fichier ou même la barre de
menus principale, recherchez l’objet TActionClients qui lui est associé et définissez
sa propriété HideUnused par false.
Utilisation des listes d’actions
Remarque
Cette section concerne la définition des barres d’outils et des menus dans le
développement multiplates-formes. Vous pouvez utiliser les méthodes décrites ici
pour le développement Windows. Mais, l’utilisation des bandes d’actions est plus
simple et offre plus d’options. Les listes d’actions seront gérées automatiquement
par le gestionnaire d’actions. Voir “Organisation des actions pour les barres
d’outils et les menus” à la page 8-18 pour davantage d’informations sur
l’utilisation des bandes d’actions et du gestionnaire d’actions.
Les listes d’actions maintiennent la liste des actions par lesquelles votre
application répond à ce que fait l’utilisateur. En utilisant les objets action, vous
centralisez les fonctions accomplies par votre application à partir de l’interface
utilisateur. Cela vous permet de partager le code réalisant les actions (par
exemple, lorsqu’un bouton de barre d’outils et un élément de menu effectuent la
même tâche), et procure un lieu unique et centralisé d’activer et de désactiver
des actions selon l’état de l’application.
Définition des listes d’actions
La définition des listes d’actions est très simple une fois comprises les étapes
qu’elle implique :
•
•
•
•
Créer la liste d’actions.
Ajouter des actions à la liste d’actions.
Définir des propriétés pour les actions.
Attacher des clients à l’action.
Voici le détail de ces étapes :
1 Placez un objet TActionList dans votre fiche ou votre module de données.
ActionList appartient à la page Standard de la palette des composants.
2 Double-cliquez sur l’objet TActionList pour afficher l’éditeur de liste d’actions.
1 Utilisez une des actions prédéfinies énumérées dans l’éditeur : cliquez avec
le bouton droit et choisissez Nouvelle action standard.
2 Les actions prédéfinies sont groupées en catégories (ensemble de données,
édition, aide et fenêtre) dans la boîte de dialogue Classes d’actions
standard. Sélectionnez toutes les actions standard que vous voulez ajouter à
votre liste d’actions et cliquez sur OK.
8-26
Guide du développeur
Utilisation des listes d’actions
ou
3 Créez une nouvelle action qui vous sera propre : cliquez avec le bouton
droit et choisissez Nouvelle action.
3 Définissez les propriétés de chaque action dans l’inspecteur d’objets. Les
propriétés que vous définissez affectent chaque client de l’action.
La propriété Name identifie l’action, les autres propriétés et événements
(Caption, Checked, Enabled, HelpContext, Hint, ImageIndex, ShortCut, Visible et
Execute) correspondent aux propriétés et événements de ses contrôles client.
Elles portent généralement, mais pas obligatoirement, le même nom que la
propriété du client. Par exemple, la propriété Enabled d’une action correspond
à la propriété Enabled d’un TToolButton. Mais, la propriété Checked d’une action
correspond à la propriété Down d’un TToolButton.
4 Si vous utilisez les actions prédéfinies, l’action inclut une réponse standard qui
intervient automatiquement. Si vous créez votre propre action, vous devez
écrire un gestionnaire d’événement définissant comment l’action répond
lorsqu’elle est déclenchée. Voir “Que se passe-t-il lors du déclenchement d’une
action ?” à la page 8-27 pour plus de détails.
5 Attachez les actions de la liste d’actions aux clients qui le nécessitent :
• Cliquez sur le contrôle (par exemple, le bouton ou l’élément de menu) dans
la fiche ou le module de données. Dans l’inspecteur d’objets, la propriété
Action est la liste des actions disponibles.
• Sélectionnez celle que vous souhaitez.
Les actions standard, comme TEditDelete ou TDataSetPost, accomplissent l’action
attendue. L’aide en ligne de référence peut vous donner tous les détails sur le
fonctionnement de chacune des actions standard. Si vous écrivez vos propres
actions, vous aurez besoin de mieux comprendre ce qui se passe lorsqu’une
action est déclenchée.
Que se passe-t-il lors du déclenchement d’une action ?
Lorsqu’un événement est déclenché, il se produit la série d’événements qui
s’applique d’abord aux actions génériques. Ensuite, si l’événement ne gère pas
l’action, une autre séquence d’événements intervient.
Réponse par les événements
Quand un composant ou un contrôle client est cliqué ou subit un autre type
d’opération, se produit une série d’événements auxquels vous pouvez répondre.
Par exemple, le code suivant est le gestionnaire d’événement pour une action qui
inverse la visibilité d’une barre d’outils quand une action est exécutée :
void __fastcall TForm1::Action1Execute(TObject *Sender)
{
// Inverse la visibilité de Toolbar1
ToolBar1->Visible = !ToolBar1->Visible;
}
Conception de l’interface utilisateur des applications
8-27
Utilisation des listes d’actions
Vous pouvez fournir un gestionnaire d’événement qui réponde à l’un des trois
différents niveaux : action, liste d’actions ou application. Cela ne vous concerne
que si vous utilisez une nouvelle action générique et non une action standard
prédéfinie. En effet, les actions standard ont un comportement intégré qui
s’exécute automatiquement lorsque ces événements se produisent.
L’ordre dans lequel les gestionnaires d’événements répondront aux événements
est le suivant :
• Liste d’actions
• Application
• Action
Lorsque l’utilisateur clique sur un contrôle client, C++Builder appelle la méthode
Execute de l’action qui s’adresse d’abord à la liste d’actions, puis à l’objet
Application, enfin à l’action elle-même lorsque ni la liste d’actions ni
l’application ne la gère. C++Builder suit cette séquence de répartition lors de la
recherche d’une façon de répondre à l’action de l’utilisateur :
1 Si vous fournissez un gestionnaire d’événement OnExecute pour la liste
d’actions et qu’il gère l’action, l’application se poursuit.
Le gestionnaire d’événement de la liste d’actions dispose d’un paramètre
Handled qui renvoie false par défaut. Si le gestionnaire est défini et gère
l’événement, il renvoie true et la séquence de traitement s’arrête là. Par
exemple :
void __fastcall TForm1::ActionList1ExecuteAction(TBasicAction *Action, bool &Handled)
{
Handled = true;
}
Si vous ne définissez pas Handled par true dans le gestionnaire d’événement
de la liste d’actions, le traitement se poursuit.
2 Si vous n’avez pas écrit de gestionnaire d’événement OnExecute pour la liste
d’actions ou si le gestionnaire ne gère pas l’action, le gestionnaire d’événement
OnActionExecute de l’application est déclenché. S’il gère l’action, l’application
continue.
L’objet Application global reçoit un événement OnActionExecute si l’une des
listes d’actions de l’application échoue en gérant un événement. Comme le
gestionnaire d’événement OnExecute de la liste d’actions, le gestionnaire
OnActionExecute dispose d’un paramètre Handled qui renvoie false par défaut.
Si un gestionnaire d’événement est défini et gère l’événement, il renvoie true
et la séquence de traitement s’arrête ici. Par exemple :
void __fastcall TForm1::ApplicationExecuteAction(TBasicAction *Action, bool &Handled)
{
// Empêche l’exécution de toutes les actions de Application
Handled = true;
}
3 Si le gestionnaire d’événement OnExecute de l’application ne gère pas l’action,
le gestionnaire d’événement OnExecute de l’action est déclenché.
8-28
Guide du développeur
Utilisation des listes d’actions
Vous pouvez utiliser des actions intégrées ou créer vos propres classes d’actions
qui savent comment opérer sur des classes cibles spécifiques (telles que les
contrôles de saisie). Quand aucun gestionnaire d’événement n’est trouvé à
n’importe quel niveau, l’application essaie ensuite de trouver une cible sur
laquelle exécuter l’action. Quand l’application trouve une cible pour laquelle
l’action sait quoi faire, elle déclenche l’action. Voir la section suivante pour
davantage d’informations sur la manière dont l’application trouve une cible
pouvant correspondre à une classe d’actions prédéfinie.
Comment les actions trouvent leurs cibles
“Que se passe-t-il lors du déclenchement d’une action ?” à la page 8-27 décrit le
cycle d’exécution se produisant lorsqu’un utilisateur déclenche une action. S’il
n’y a pas de gestionnaire d’événement défini pour répondre à l’action, que ce
soit au niveau de la liste d’actions, de l’application ou de l’action, l’application
essaie d’identifier un objet cible auquel l’action peut s’appliquer.
L’application recherche un objet cible selon la séquence suivante :
1 Contrôle actif : l’application recherche d’abord un contrôle actif comme cible
potentielle.
2 Fiche active : si l’application ne trouve pas de contrôle actif ou si le contrôle
actif ne peut servir de cible, elle recherche la fiche active à l’écran
(ActiveForm).
3 Contrôles de la fiche : si la fiche active n’est pas une cible appropriée,
l’application recherche une cible dans les autres contrôles de la fiche active.
Si aucune cible n’est trouvée, rien ne se passe lorsque l’événement est déclenché.
Certains contrôles peuvent étendre la recherche afin de déférer la cible à un
composant qui leur est associé ; par exemple, les contrôles orientés données
défèrent la cible au composant ensemble de données qui leur est associé. D’autre
part, certaines actions prédéfinies n’utilisent pas de cible, par exemple, le
dialogue d’ouverture de fichiers.
Actualisation des actions
Quand l’application est inactive, l’événement OnUpdate se produit pour chaque
action liée à un contrôle ou un élément de menu affiché. Ceci permet aux
applications d’exécuter un code centralisé pour l’activation et la désactivation, la
sélection et la désélection, et ainsi de suite. Par exemple, le code suivant illustre
le gestionnaire d’événement OnUpdate pour une action qui est “cochée” quand la
barre d’outils est visible.
void __fastcall TForm1::Action1Update(TObject *Sender)
{
// Indique si ToolBar1 est actuellement visible
((TAction *)Sender)->Checked = ToolBar1->Visible;
}
Conception de l’interface utilisateur des applications
8-29
Utilisation des listes d’actions
Attention
Ne placez pas de code nécessitant une exécution longue dans le gestionnaire
d’événement OnUpdate. En effet, il est exécuté à chaque fois que l’application est
inactive. Si l’exécution de ce gestionnaire d’événement est trop longue, les
performances de toute l’application s’en trouvent affectées.
Classes d’actions prédéfinies
Vous pouvez ajouter des actions prédéfinies à votre application en cliquant avec
le bouton droit sur le gestionnaire d’actions et en choisissant Nouvelle action
standard. La boîte de dialogue Classes d’actions standard s’affiche et présente la
liste des classes d’actions prédéfinies avec les actions standard qui leur sont
associées. Ce sont des actions qui sont incluses dans C++Builder et il existe des
objets qui accomplissent ces actions automatiquement. Les actions prédéfinies
sont organisées dans les classes suivantes :
Tableau 8.3 Classes d’actions
8-30
Classe
Description
Edition
Actions d’édition standard : Utilisées avec une cible contrôle de saisie.
TEditAction est la classe de base de descendants qui redéfinissent la
méthode ExecuteTarget afin d’implémenter les opérations copier,
couper et coller en utilisant le presse-papiers.
Formatage
Actions de formatage standard : Utilisées avec le texte formaté pour
appliquer des options de formatage du texte telles que gras, italique,
souligné, barré, etc. TRichEditActionest la classe de base de
descendants qui redéfinissent les méthodes ExecuteTarget et
UpdateTarget afin d’implémenter le formatage de la cible.
Aide
Actions d’aide standard : Utilisées avec toute cible. THelpAction est la
classe de base de descendants qui redéfinissent la méthode
ExecuteTarget pour transmettre les commandes à un système d’aide.
Fenêtre
Actions de fenêtre standard : Utilisées avec les fiches d’une
application MDI comme cible. TWindowAction est la classe de base de
descendants qui redéfinissent la méthode ExecuteTarget pour
implémenter la réorganisation, la cascade, la fermeture, la mosaïque
et la réduction de fenêtres enfant MDI.
Fichier
Actions de fichiers : Utilisées avec les opérations sur les fichiers
comme ouvrir, exécuter ou quitter.
Recherche
Actions de recherche : Utilisées avec les options de recherche.
TSearchAction implémente le comportement commun des actions
affichant un dialogue non modal où l’utilisateur peut entrer une
chaîne pour rechercher un contrôle de saisie.
Onglet
Actions des contrôles onglet : Utilisées pour le déplacement entre les
onglets dans un contrôle à onglets, comme les boutons Précédent et
Suivant d’un expert.
Liste
Actions des contrôles liste : Utilisées pour gérer les éléments d’une
vue liste.
Dialogue
Actions de dialogue : Utilisées avec les composants dialogue.
TDialogAction implémente le comportement commun des actions
affichant un dialogue lors de leur exécution. Chaque classe dérivée
représente un dialogue spécifique.
Guide du développeur
Utilisation des listes d’actions
Tableau 8.3 Classes d’actions (suite)
Classe
Description
Internet
Actions Internet : Utilisées pour des fonctions comme la navigation, le
téléchargement et l’envoie de mail sur Internet.
Ensemble de données
Actions d’ensembles de données : Utilisées avec un composant
ensemble de données comme cible. TDataSetAction est la classe de
base de descendants qui redéfinissent les méthodes ExecuteTarget et
UpdateTarget afin d’implémenter les déplacements et les éditions de la
cible.
TDataSetAction introduit une propriété DataSource qui garantit que les
actions sont effectuées sur l’ensemble de données. Si DataSource vaut
NULL, le contrôle orienté données détenant la focalisation est utilisé.
Outils
Outils : Des outils supplémentaires comme TCustomizeActionBars qui
affiche automatiquement la boîte de dialogue permettant de
personnaliser les bandes d’actions.
Tous les objets action sont décrits sous leur nom dans l’aide en ligne de
référence. Reportez-vous à l’aide pour avoir des détails sur leur fonctionnement.
Conception de composants action
Vous pouvez également créer vos propres classes d’actions prédéfinies. Quand
vous écrivez vos propres classes d’actions, vous pouvez leur attribuer la capacité
à s’exécuter sur certaines classes d’objet cible. Vous pouvez ensuite utiliser vos
actions personnalisées de la même façon que les classes d’actions prédéfinies.
Ainsi, si l’action détermine qu’elle peut s’appliquer à une classe cible, il suffit
d’affecter l’action au contrôle client et il agit sur la cible sans avoir besoin
d’écrire un gestionnaire d’événement.
Les concepteurs de composants peuvent utiliser comme exemple les classes
définies dans les unités StdActns et DBActns afin de dériver leurs propres classes
d’actions qui implémentent des comportements spécifiques à leurs contrôles et
leurs composants. Les classes de base de ces actions spécialisées (TEditAction,
TWindowAction) redéfinissent généralement HandlesTarget, UpdateTarget et d’autres
méthodes afin de limiter la cible de l’action à une classe spécifique d’objets.
Les classes dérivées surchargent habituellement ExecuteTarget pour réaliser une
tâche spécifique.
Voici ces méthodes :
Méthode
Description
HandlesTarget
Automatiquement appelée lorsque l’utilisateur invoque un objet (comme
un bouton de barre d’outils ou un élément de menu) lié à l’action. La
méthode HandlesTarget laisse l’objet action indiquer s’il convient de
l’exécuter à ce moment avec comme “cible” l’objet spécifié par le
paramètre Target. Voir “Comment les actions trouvent leurs cibles” à la
page 8-29 pour plus de détails.
Conception de l’interface utilisateur des applications
8-31
Création et gestion de menus
Méthode
Description
UpdateTarget
Automatiquement appelée lorsque l’application est inactive pour que les
actions puissent se mettre à jour elles-mêmes en fonction des conditions
en cours. Utilisez à la place de OnUpdateAction. Voir “Actualisation des
actions” à la page 8-29 pour plus de détails.
ExecuteTarget
Automatiquement appelée lorsque l’action est déclenchée en réponse à
une action de l’utilisateur à la place de OnExecute (par exemple, quand
l’utilisateur sélectionne un élément de menu ou appuie sur un bouton de
barre d’outils qui est lié à cette action). Voir “Que se passe-t-il lors du
déclenchement d’une action ?” à la page 8-27 pour plus de détails.
Recensement d’actions
Si vous écrivez vos propres actions, il est possible de les recenser pour les faire
apparaître dans l’éditeur de liste d’actions. Vous les recensez ou vous en annulez
le recensement en utilisant les routines globales de l’unité ActnList :
extern PACKAGE void __fastcall RegisterActions(const AnsiString CategoryName, TMetaClass*
const * AClasses, const int AClasses_Size, TMetaClass* Resource);
extern PACKAGE void __fastcall UnRegisterActions(TMetaClass* const * AClasses, const int
AClasses_Size);
Quand vous appelez RegisterActions, les actions recensées apparaissent dans
l’éditeur de liste d’actions afin d’être utilisées dans vos applications. Vous
pouvez spécifier un nom de catégorie afin d’organiser vos actions, ainsi qu’un
paramètre Resource permettant de fournir des valeurs de propriété par défaut.
Par exemple, le code suivant recense dans l’EDI les actions de l’unité MyAction :
namespace MyAction
{
void __fastcall PACKAGE Register()
{
// du code vient ici pour recenser des composants et des éditeurs
TMetaClass classes[2] = {__classid(TMyAction1), __classid(TMyAction2)};
RegisterActions("MySpecialActions", classes, 1, NULL);
}
}
Quand vous appelez UnRegisterActions, les actions n’apparaissent plus dans
l’éditeur de liste d’actions.
Création et gestion de menus
Les menus constituent pour les utilisateurs un moyen commode d’exécuter des
commandes regroupées logiquement. Le concepteur de menus vous permet
d’ajouter facilement à une fiche un menu prédéfini ou personnalisé. Ajoutez un
composant menu à la fiche, ouvrez le concepteur de menus et saisissez
directement les éléments de menu dans la fenêtre du concepteur de menus.
8-32
Guide du développeur
Création et gestion de menus
Vous pouvez ajouter ou supprimer des éléments de menu et utiliser le glisserdéplacer pour réorganiser les éléments à la conception.
Vous n’avez même pas besoin d’exécuter votre programme pour voir le résultat :
ce que vous concevez est immédiatement visible dans la fiche et apparaît
exactement comme à l’exécution. En utilisant du code, vous pouvez également
modifier les menus à l’exécution afin de proposer à l’utilisateur davantage
d’informations ou d’options.
Ce chapitre décrit la manière d’utiliser le concepteur de menus pour concevoir
des barres de menus et des menus surgissants (locaux). Les sujets suivants,
concernant la manipulation des menus à la conception et à l’exécution, sont
abordés :
•
•
•
•
•
•
•
Ouverture du concepteur de menus.
Construction des menus.
Edition des éléments de menu dans l’inspecteur d’objets.
Utilisation du menu contextuel du concepteur de menus.
Utilisation des modèles de menu.
Enregistrement d’un menu comme modèle.
Ajout d’images à des éléments de menu.
Figure 8.3
Terminologie des menus
Eléments de menu de la barre de menu
Touche
accélératrice
Eléments de menu d’une liste de menus
Ligne de
séparation
Raccourci clavier
Ouverture du concepteur de menus
Vous concevez les menus de votre application à l’aide du concepteur de menus.
Avant de commencer à utiliser le concepteur de menus, ajoutez à votre fiche un
composant MainMenu ou PopupMenu. Ces deux composants menu se trouvent
dans la page Standard de la palette des composants.
Figure 8.4
Composants menu principal et menu surgissant
Composant MainMenu
Composant PopupMenu
Un composant menu principal (MainMenu) crée un menu attaché à la barre de
titre de la fiche. Un composant menu surgissant (PopupMenu) crée un menu qui
apparaît quand l’utilisateur clique avec le bouton droit de la souris dans la fiche.
Les menus surgissants n’ont pas de barre de menu.
Pour ouvrir le concepteur de menus, sélectionnez un composant menu sur la fiche,
puis :
• Double-cliquez sur le composant menu.
Conception de l’interface utilisateur des applications
8-33
Création et gestion de menus
ou
• Dans la page Propriétés de l’inspecteur d’objets, sélectionnez la propriété Items
puis double-cliquez sur [Menu] dans la colonne des valeurs ou cliquez sur le
bouton points de suspension (...).
Le concepteur de menus apparaît, le premier élément de menu (vide) est
sélectionné dans le concepteur et la propriété Caption est sélectionnée dans
l’inspecteur d’objets.
Figure 8.5
Concepteur pour un menu surgissant
Emplacement pour le
premier élément de menu
Construction des menus
Vous devez ajouter un composant menu à vos fiches pour chaque menu devant
apparaître dans l’application. Vous pouvez créer chaque structure de menu à
partir de zéro ou partir de l’un des modèles de menu prédéfinis.
Cette section décrit les principes de base de la création de menus à la
conception. Pour davantage d’informations sur les modèles de menu, voir
“Utilisation des modèles de menu” à la page 8-42.
Nom des menus
Comme pour tous les composants, C++Builder donne un nom par défaut au
composant menu que vous ajoutez à une fiche, par exemple MainMenu1. Vous
pouvez donner au menu un nom plus significatif.
C++Builder ajoute le nom du menu à la déclaration de type de la fiche et le nom
du menu apparaît dans la liste des composants.
Nom des éléments de menu
A la différence des composants menu, vous devez donner vous-même
explicitement un nom aux éléments de menu quand vous les ajoutez à la fiche.
Vous pouvez le faire de deux manières :
• En saisissant directement la valeur de la propriété Name.
• En saisissant d’abord la valeur de la propriété Caption, puis en laissant
C++Builder en dériver une valeur pour la propriété Name.
8-34
Guide du développeur
Création et gestion de menus
Si, par exemple, vous spécifiez Fichier comme valeur de la propriété Caption,
C++Builder affecte la valeur Fichier1 à la propriété Name de l’élément de
menu. Si vous renseignez d’abord la propriété Name avant de renseigner la
propriété Caption, C++Builder laisse la propriété Caption vide jusqu’à ce que
vous saisissiez une valeur.
Remarque
Si vous saisissez dans la propriété Caption des caractères qui ne sont pas
autorisés dans un identificateur C++, C++Builder modifie la propriété Name en
conséquence. Si par exemple, l’intitulé commence par un chiffre, C++Builder
fait précéder le chiffre d’un caractère pour en dériver la valeur de la propriété
Name.
Le tableau suivant donne des exemples de ce processus en supposant que tous
ces éléments sont placés dans la même barre de menu.
Tableau 8.4 Exemples d’intitulés et des noms dérivés
Intitulé du composant
Nom dérivé
Explication
&Fichier
Fichier1
Retire le &
&Fichier (une
deuxième fois)
Fichier2
Numérote les éléments répétés
1234
N12341
Ajoute une lettre au début et numérote en fin
1234 (une deuxième
fois)
N12342
Ajoute un nombre pour que le nom dérivé ne
soit plus ambigu.
$@@@#
N1
Supprime tous les caractères non standard,
ajoute une lettre au début et un numéro d’ordre
- (signe moins)
N2
Numérote cette deuxième occurrence d’un
intitulé sans aucun caractère standard
Comme pour le composant menu, C++Builder ajoute le nom des éléments de
menu à la déclaration de type de la fiche et leur nom apparaît dans la liste des
composants.
Ajout, insertion et suppression d’éléments de menu
Les procédures suivantes décrivent la manière d’effectuer les opérations de base
intervenant dans la conception d’une structure de menu. Chaque procédure
suppose que la fenêtre du concepteur de menus est déjà ouverte.
Pour ajouter des éléments de menu à la conception :
1 Sélectionnez la position à laquelle vous voulez créer l’élément de menu.
Si vous venez juste d’ouvrir le concepteur de menus, la première position est
déjà sélectionnée dans la barre de menu.
2 Commencez à saisir l’intitulé. Vous pouvez aussi commencer à saisir la
propriété Name en plaçant le curseur dans l’inspecteur d’objets et en saisissant
une valeur. Dans ce cas, vous devez resélectionner la propriété Caption et
entrer une valeur.
3 Appuyez sur Entrée.
L’emplacement suivant d’élément de menu est sélectionné.
Conception de l’interface utilisateur des applications
8-35
Création et gestion de menus
Si vous entrez d’abord la propriété Caption, utilisez les touches de
déplacement pour revenir dans l’élément de menu que vous venez de saisir.
Vous pouvez constater que C++Builder a renseigné la propriété Name en
partant de la valeur saisie dans l’intitulé. (Voir “Nom des éléments de menu”
à la page 8-34.)
4 Continuez à saisir la valeur des propriétés Name et Caption pour chaque
élément de menu à ajouter ou appuyez sur Echap pour revenir à la barre de
menu.
Utilisez les touches de déplacement pour passer de la barre de menu au
menu, puis pour vous déplacer dans les éléments de la liste ; appuyez sur
Entrée pour achever une action. Pour revenir à la barre de menu, appuyez sur
Echap.
Pour insérer un nouvel élément de menu vide :
1 Placez le curseur sur un élément de menu.
2 Appuyez sur Inser.
Dans la barre de menu, les éléments de menu sont insérés à gauche de
l’élément sélectionné et au-dessus de l’élément sélectionné dans la liste de
menus.
Pour supprimer un élément de menu :
1 Placez le curseur sur l’élément de menu à supprimer.
2 Appuyez sur Suppr.
Remarque
Vous ne pouvez pas supprimer l’emplacement par défaut qui apparaît en
dessous du dernier élément ajouté à la liste de menus ou à côté du dernier
élément de la barre de menu. Cet emplacement n’apparaît pas dans le menu à
l’exécution.
Figure 8.6
Ajout d’éléments au menu principal
Le concepteur de menus affiche les éléments de
menu en mode WYSIWYG
au fur et à mesure que vous construisez le menu.
Barre de titre (affiche la propriété Name
du composant menu)
Barre de menu
Un objet TMenuItem est créé et la propriété
Name est affectée avec l’intitulé (Caption)
spécifié pour l’élément de menu (moins les
caractères interdits et plus un suffixe
numérique).
Emplacement
pour les éléments
de menu
8-36
Guide du développeur
Création et gestion de menus
Ajout de lignes de séparation
Les lignes de séparation insèrent une ligne entre deux éléments de menu. Vous
pouvez utiliser les lignes de séparation pour matérialiser des groupes de
commandes dans une liste de menus ou simplement pour réaliser une séparation
visuelle dans une liste.
Pour faire de l’élément de menu une ligne de séparation, entrez un tiret (-)
comme libellé.
Spécification de touches accélératrices et de raccourcis clavier
Les touches accélératrices permettent à l’utilisateur d’accéder à une commande
de menu à partir du clavier en appuyant sur Alt+ la lettre appropriée indiquée
dans le code en la faisant précéder du symbole &. La lettre suivant le symbole &
apparaît soulignée dans le menu.
C++Builder vérifie automatiquement si des touches accélératrices sont dupliquées
et les ajuste à l’exécution. Ainsi, les menus conçus dynamiquement à l’exécution
ne contiennent aucune touche accélératrice dupliquée et tous les éléments de
menu disposent d’une touche accélératrice. Vous pouvez désactiver cette
vérification automatique en attribuant à la propriété AutoHotkeys d’un élément de
menu la valeur maManual.
Pour spécifier une touche accélératrice
• Ajoutez un & devant la lettre appropriée de l’intitulé.
Ainsi, pour ajouter une commande de menu Enregistrer dont la lettre E sert
de touche accélératrice, entrez &Enregistrer.
Les raccourcis clavier permettent à l’utilisateur d’effectuer l’action directement
sans accéder au menu, simplement en appuyant sur la combinaison de touches
du raccourci clavier.
Pour spécifier un raccourci clavier :
• Utilisez l’inspecteur d’objets pour saisir une valeur dans la propriété ShortCut
ou sélectionnez une combinaison de touches dans la liste déroulante.
Cette liste ne propose qu’un sous-ensemble de toutes les combinaisons
utilisables.
Quand vous ajoutez un raccourci, il apparaît à l’exécution à côté de l’intitulé de
l’élément de menu.
Attention
A la différence des touches accélératrices, la présence de raccourcis clavier
dupliqués n’est pas automatiquement vérifiée. Vous devez vous-même en assurer
l’unicité.
Création de sous-menus
La plupart des menus d’application contiennent des listes déroulantes qui
apparaissent à côté d’un élément de menu afin de proposer des commandes
associées supplémentaires. De telles listes sont signalées par un pointeur à droite
Conception de l’interface utilisateur des applications
8-37
Création et gestion de menus
de l’élément de menu. C++Builder gère les niveaux de sous-menus sans aucune
limitation dans l’imbrication.
Une telle organisation de votre structure de menu permet d’économiser de la
place verticalement. Néanmoins, pour optimiser la conception, il est préférable de
ne pas utiliser plus de deux ou trois niveaux de menus dans la conception de
votre interface. Dans le cas des menus surgissants, il est préférable de n’utiliser
au maximum qu’un niveau de sous-menu.
Figure 8.7
Structure de menus imbriqués
Elément de menu de la barre de menu
Elément de menu d’une liste de menus
Elément de menu imbriqué
Pour créer un sous-menu :
1 Sélectionnez l’élément de menu sous lequel vous voulez créer un sous-menu.
2 Appuyez sur Ctrl→ afin de créer le premier emplacement ou cliquez sur le
bouton droit de la souris et choisissez Créer un sous-menu.
3 Entrez l’intitulé de l’élément de sous-menu ou déplacez un élément de menu
existant sur cet emplacement.
4 Appuyez sur Entrée ou ↓ pour créer l’emplacement suivant.
5 Répétez les étapes 3 et 4 pour chaque élément du sous-menu.
6 Appuyez sur Echap pour revenir au niveau de menu précédent.
Création de sous-menus par déplacement de menus existants
Vous pouvez également créer un sous-menu en insérant un élément de la barre
de menu (ou d’un modèle de menu) entre les éléments de menu d’une liste.
Quand vous déplacez un menu dans une structure de menu existante, tous ses
éléments de menu associés se déplacent, ce qui crée directement un sous-menu.
Cela s’applique également aux sous-menus. Le déplacement d’un élément de
menu dans un sous-menu existant peut ainsi créer un niveau supplémentaire
d’imbrication.
Déplacement d’éléments de menu
A la conception, vous pouvez déplacer les éléments de menu en utilisant le
glisser-déplacer. Vous pouvez déplacer des éléments de menu dans la barre de
menu, à un emplacement différent dans la liste de menus ou dans un autre
composant menu.
8-38
Guide du développeur
Création et gestion de menus
La seule limitation à ces déplacements est de nature hiérarchique : vous ne
pouvez pas déplacer un élément de menu de la barre de menu à l’intérieur de
son propre menu ; de même, vous ne pouvez pas déplacer un élément de menu
à l’intérieur de son propre sous-menu. Par contre, vous pouvez toujours déplacer
un élément dans un menu différent quelle que soit sa position d’origine.
Quand vous faites glisser un élément, la forme du curseur change afin
d’indiquer si vous pouvez déposer l’élément de menu à l’emplacement du
pointeur de la souris. Quand vous déplacez un élément de menu, tous les
éléments de menu situés en dessous sont également déplacés.
Pour déplacer un élément de menu à l’intérieur de la barre de menu :
1 Faites glisser l’élément de menu jusqu’à ce que la pointe du curseur de
déplacement désigne la nouvelle position.
2 Relâchez le bouton de la souris pour déposer l’élément de menu à la nouvelle
position.
Pour déplacer un élément de menu dans une liste de menus :
1 Déplacez l’élément de menu le long de la barre de menu jusqu’à ce que le
pointeur du curseur de déplacement désigne le nouveau menu.
Cela force l’ouverture du menu, ce qui vous permet d’amener l’élément à son
nouvel emplacement.
2 Faites glisser l’élément de menu dans la liste et relâchez le bouton de la souris
pour déposer l’élément de menu à sa nouvelle position.
Ajout d’images à des éléments de menu
Des images peuvent aider les utilisateurs à naviguer dans les menus en associant
des glyphes et des images à des actions d’élément de menu, comme les images
des barres d’outils. Vous pouvez ajouter un par un des bitmaps uniques à des
éléments de menu, ou organiser en liste les images de votre application et les
ajouter à un menu au moyen de cette liste. Si, dans votre application, vous
utilisez plusieurs bitmaps de même taille, il est préférable de les placer dans une
liste d’images.
Pour ajouter une image unique à un menu ou à un élément de menu, définissez
la propriété Bitmap de celui-ci pour qu’elle fasse référence au nom du bitmap
que vous voulez utiliser.
Pour ajouter une image à un élément de menu à l’aide d’une liste d’images :
1 Déposez un composant TMainMenu ou TPopupMenu dans une fiche.
2 Déposez un objet TImageList dans la fiche.
3 Ouvrez l’éditeur de liste d’images en double-cliquant sur l’objet TImageList.
4 Choisissez Ajouter pour sélectionner le bitmap ou le groupe de bitmaps que
vous voulez utiliser dans le menu. Cliquez sur OK.
5 Affectez l’objet liste d’images que vous venez de créer à la propriété Images
du composant TMainMenu ou TPopupMenu.
Conception de l’interface utilisateur des applications
8-39
Création et gestion de menus
6 Créez vos éléments de menu et vos sous-menus comme décrit plus haut.
7 Sélectionnez dans l’inspecteur d’objets l’élément de menu pour lequel vous
voulez spécifier une image et affectez à sa propriété ImageIndex le numéro
correspondant dans la liste d’images (la valeur par défaut de la propriété
ImageIndex est -1 : pas d’image affichée).
Remarque
Pour un affichage correct dans les menus, utilisez des images de 16 sur 16 pixels.
Même si vous pouvez utiliser d’autres tailles pour les images placées dans les
menus, il peut y avoir des problèmes d’alignement si vous utilisez des images
plus grandes ou plus petites que 16 x 16 pixels.
Affichage du menu
Vous pouvez voir votre menu dans la fiche à la conception sans exécuter le code
de votre programme. Les composants menu surgissant sont visibles dans la fiche
à la conception, mais pas le menu surgissant lui-même. Utilisez le concepteur de
menus pour visualiser un menu surgissant à la conception.
Pour visualiser le menu :
1 Si la fiche n’est pas visible, cliquez dans la fiche ou dans le menu Voir,
choisissez la fiche que vous voulez voir.
2 Si la fiche contient plusieurs menus, sélectionnez le menu à visualiser dans la
liste déroulante de la propriété Menu de la fiche.
Le menu apparaît dans la fiche exactement tel qu’il apparaîtra à l’exécution du
programme.
Edition des éléments de menu dans l’inspecteur d’objets
Cette section a décrit jusqu’à présent comment initialiser diverses propriétés des
éléments de menu (par exemple, les propriétés Name ou Caption) en utilisant le
concepteur de menus.
Cette section a également décrit comment initialiser certaines propriétés (par
exemple, ShortCut) des éléments de menu directement dans l’inspecteur d’objets
comme vous le faites pour les autres composants sélectionnés dans la fiche.
Quand vous éditez un élément de menu au moyen du concepteur de menus, ses
propriétés sont toujours affichées dans l’inspecteur d’objets. Vous pouvez faire
basculer la focalisation sur l’inspecteur d’objets et y poursuivre l’édition des
propriétés de l’élément de menu. Vous pouvez également sélectionner l’élément
de menu dans la liste des composants de l’inspecteur d’objets et modifier ses
propriétés sans même ouvrir le concepteur de menus.
Pour fermer la fenêtre du concepteur de menus tout en poursuivant la
modification des éléments de menu :
1 Faites passer la focalisation de la fenêtre du concepteur de menus sur
l’inspecteur d’objets en cliquant sur la page Propriétés de l’inspecteur d’objets.
2 Fermez normalement le concepteur de menus.
8-40
Guide du développeur
Création et gestion de menus
La focalisation reste dans l’inspecteur d’objets où vous pouvez continuer à
modifier les propriétés de l’élément de menu sélectionné. Pour éditer un autre
élément de menu, sélectionnez-le dans la liste des composants.
Utilisation du menu contextuel du concepteur de menus
Le menu contextuel du concepteur de menus propose un accès rapide aux
commandes les plus couramment utilisées du concepteur de menus et aux
options des modèles de menu. (Pour davantage d’informations sur les modèles
de menu, voir “Utilisation des modèles de menu” à la page 8-42.)
Pour afficher le menu contextuel, cliquez avec le bouton droit de la souris dans
la fenêtre du concepteur de menus ou appuyez sur Alt+F10 quand le curseur est
dans la fenêtre du concepteur de menus.
Commandes du menu contextuel
Le tableau suivant résume les commandes du menu contextuel du concepteur de
menus.
Tableau 8.5 Commandes du menu contextuel du concepteur de menus
Commande de menu
Action
Insérer
Insère un emplacement au-dessus ou à gauche du curseur.
Supprimer
Supprime l’élément de menu sélectionné (et tous ses éventuels souséléments).
Créer un sous-menu
Crée un emplacement à un niveau imbriqué et ajoute un pointeur à
droite de l’élément de menu sélectionné.
Sélectionner
un menu
Ouvre une liste des menus dans la fiche en cours. Double-cliquez sur
un nom de menu pour l’ouvrir dans le concepteur de menus.
Enregistrer comme
modèle
Ouvre la boîte de dialogue Enregistrement de modèle qui vous permet
d’enregistrer un menu pour une réutilisation ultérieure.
Insérer depuis
un modèle
Ouvre la boîte de dialogue Insertion de modèle qui vous permet de
sélectionner le modèle à réutiliser.
Supprimer
des modèles
Ouvre la boîte de dialogue Suppression de modèles qui vous permet
de supprimer des modèles existants.
Insérer depuis
une ressource
Ouvre la boîte de dialogue Insertion de menu depuis une ressource
qui vous permet de choisir un fichier .rc ou .mnu ouvert dans la fiche
en cours.
Déplacement parmi les menus à la conception
Si vous concevez plusieurs menus pour votre fiche, vous pouvez utiliser le
concepteur de menus ou l’inspecteur d’objets pour sélectionner un menu et
passer d’un menu à l’autre.
Pour utiliser le menu contextuel afin de passer d’un menu de la fiche à un
autre :
1 Cliquez avec le bouton droit de la souriset choisissez Sélectionner un menu.
Conception de l’interface utilisateur des applications
8-41
Création et gestion de menus
La boîte de dialogue Sélection de menus apparaît.
Figure 8.8
Boîte de dialogue Sélection de menu
Cette boîte de dialogue énumère tous les menus associés à la fiche dont les
menus sont ouverts dans le concepteur de menus.
2 Dans la liste de la boîte de dialogue Sélection de menu, choisissez le menu
que vous voulez voir ou modifier.
Pour utiliser l’inspecteur d’objets afin de passer d’un menu de la fiche à un
autre :
1 Attribuez la focalisation à la fiche dans laquelle vous voulez choisir un menu.
2 Dans la liste des composants, sélectionnez le menu à éditer.
3 Dans la page Propriétés de l’inspecteur d’objets, sélectionnez la propriété Items
de ce menu, puis cliquez sur le bouton points de suspension ou doublecliquez sur [Menu].
Utilisation des modèles de menu
C++Builder propose plusieurs menus pré-conçus (des modèles de menu) qui
contiennent des commandes d’utilisation courante. Vous pouvez utiliser ces
menus dans vos applications sans les modifier (sauf pour ajouter le code) ou
vous pouvez les utiliser comme point de départ en les personnalisant comme
vous le feriez avec un menu que vous auriez créé. Les modèles de menu ne
contiennent pas de code de gestionnaire d’événement.
Les modèles de menu livrés avec C++Builder sont stockés dans le sous-répertoire
BIN de l’installation par défaut. Ces fichiers ont l’extension .dmt (extension des
modèles de menu dans C++Builder).
Vous pouvez aussi enregistrer n’importe quel menu que vous avez créé avec le
concepteur de menus en tant que modèle. Une fois un menu enregistré comme
modèle, vous pouvez l’utiliser comme tout menu préconçu. Si vous n’avez plus
besoin d’un modèle de menu, vous pouvez le retirer de la liste.
8-42
Guide du développeur
Création et gestion de menus
Pour ajouter un modèle de menu à l’application,
1 Cliquez avec le bouton droit de la souris dans le concepteur de menus et
choisissez Insérer depuis un modèle.
(S’il n’y a pas de modèle, l’option Insérer depuis un modèle apparaît
estompée dans le menu contextuel.)
La boîte de dialogue Insertion de modèle est ouverte et affiche une liste des
modèles de menu disponibles.
Figure 8.9
Boîte de dialogue Insertion de modèle des menus
2 Sélectionnez le modèle de menu que vous voulez insérer, puis appuyez sur
Entrée ou choisissez OK.
Cela insère le menu dans votre fiche à l’emplacement du curseur. Si, par
exemple, le curseur est positionné sur un élément de menu d’une liste, le
modèle de menu est inséré au-dessus de l’élément sélectionné. Si le curseur
est dans une barre de menus, le modèle de menu est inséré à gauche du
curseur.
Pour supprimer un modèle de menu,
1 Cliquez avec le bouton droit de la souris dans le concepteur de menus et
choisissez Supprimer des modèles.
(S’il n’y a pas de modèle, l’option Supprimer des modèles est estompée dans
le menu contextuel.)
La boîte de dialogue Suppression de modèles s’ouvre en affichant une liste
des modèles disponibles.
2 Sélectionnez le modèle de menu à supprimer et appuyez sur Suppr.
C++Builder retire le modèle de menu de la liste et du disque dur.
Enregistrement d’un menu comme modèle
Il est possible d’enregistrer comme modèle tout menu que vous avez conçu afin
de pouvoir le réutiliser. Vous pouvez utiliser des modèles de menu pour donner
un aspect cohérent à vos applications, mais aussi comme point de départ pour la
création de menus que vous pouvez personnaliser ensuite.
Conception de l’interface utilisateur des applications
8-43
Création et gestion de menus
Les modèles de menu que vous enregistrez sont stockés dans le sous-répertoire
BIN dans des fichiers .dmt.
Pour enregistrer un menu comme modèle,
1 Concevez le menu que vous voulez pouvoir réutiliser.
Ce menu peut contenir de nombreux éléments, commandes et sous-menus :
tout dans le contenu de la fenêtre active du concepteur de menus est
enregistré comme un seul menu réutilisable.
2 Cliquez avec le bouton droit de la souris dans le concepteur de menus et
choisissez Enregistrer comme modèle. La boîte de dialogue Enregistrement de
modèle s’ouvre.
Figure 8.10 Boîte de dialogue Enregistrement de modèle pour les menus
3 Dans la boîte de saisie Description du modèle, entrez une brève description de
ce menu, puis choisissez OK.
La boîte de dialogue Enregistrement de modèle est alors refermée, le modèle
de votre menu est enregistré et vous revenez à la fenêtre du concepteur de
menus.
Remarque
La description que vous saisissez apparaît uniquement dans les boîtes de
dialogue Enregistrement de modèle, Insertion de modèle et Suppression de
modèles. Elle n’a aucun rapport avec les propriétés Name ou Caption du menu.
Conventions de nom pour les éléments et les gestionnaires d’événements
des modèles de menu
Quand vous enregistrez un menu comme modèle, C++Builder n’enregistre pas sa
propriété Name car chaque menu doit disposer d’un nom unique dans la portée
de son propriétaire (la fiche). Cependant, quand vous insérez le menu comme
modèle dans une nouvelle fiche en utilisant le concepteur de menus, C++Builder
génère alors de nouveaux noms pour lui et tous ses éléments.
Par exemple, vous enregistrez un menu Fichier comme modèle Dans le menu
original, vous le nommez MonFichier. Si vous l’insérez comme modèle dans un
nouveau menu, C++Builder le nomme Fichier1. Si vous l’insérez dans un menu
ayant déjà un élément de menu nommé Fichier1, C++Builder le nomme Fichier2.
8-44
Guide du développeur
Création et gestion de menus
C++Builder n’enregistre aucun gestionnaire d’événement OnClick associé au
menu enregistré comme modèle car il n’y a pas moyen de savoir si le code est
applicable dans la nouvelle fiche. Quand vous générez un nouveau gestionnaire
d’événement pour un élément du modèle de menu, C++Builder génère
également le nom du gestionnaire d’événement.
Vous pouvez facilement associer les éléments du modèle de menu à des
gestionnaires d’événements OnClick de la fiche.
Manipulation d’éléments de menu à l’exécution
A l’exécution, il est parfois nécessaire d’ajouter à une structure de menu
existante des éléments de menu ; cela permet de proposer davantage
d’informations ou d’options à l’utilisateur. Il est possible d’insérer un élément de
menu en utilisant les méthodes Add ou Insert de l’élément de menu. Vous
pouvez également masquer ou afficher les éléments d’un menu en jouant sur
leur propriété Visible. La propriété Visible détermine si l’élément de menu est
affiché dans le menu. Pour estomper un élément de menu sans le masquer,
utilisez la propriété Enabled.
Pour des exemples utilisant les propriétés Visible et Enabled des éléments de
menu, voir “Désactivation des éléments de menu” à la page 6-10.
Dans des applications MDI ou OLE, vous pouvez également fusionner des
éléments de menu dans une barre de menu existante. Les sections suivantes
décrivent ce processus plus en détail.
Fusion de menus
Pour les applications MDI, comme l’application exemple éditeur de texte ou dans
les applications client OLE, le menu principal de votre application doit être
capable d’intégrer les éléments de menu d’une autre fiche ou d’un objet serveur
OLE. Ce processus s’appelle la fusion de menus. Notez que la technologie OLE est
limitée aux applications Windows et n’est pas utilisable en programmation
multiplate-forme.
Pour préparer des menus à la fusion, vous devez spécifier les valeurs de deux
propriétés :
• Menu, une propriété de la fiche.
• GroupIndex, une propriété des éléments du menu.
Spécification du menu actif : propriété Menu
La propriété Menu spécifie le menu actif de la fiche. Les opérations de fusion de
menu portent uniquement sur le menu actif. Si la fiche contient plusieurs
composants menu, vous pouvez changer le menu actif à l’exécution en modifiant
la valeur de la propriété Menu dans le code. Par exemple :
Form1->Menu = SecondMenu;
Conception de l’interface utilisateur des applications
8-45
Création et gestion de menus
Ordre des éléments de menu fusionnés : propriété GroupIndex
La propriété GroupIndex détermine l’ordre dans lequel les éléments des menus
fusionnés apparaissent dans la barre de menu partagée. La fusion d’éléments de
menu peut remplacer des éléments existants de la barre de menu principale ou
rajouter des éléments à celle-ci.
La valeur par défaut de GroupIndex est 0. Plusieurs règles s’appliquent à la
spécification d’une valeur pour la propriété GroupIndex :
• Les nombres les plus bas apparaissent en premier (plus à gauche) dans le
menu.
Par exemple, affectez la valeur 0 (zéro) à la propriété GroupIndex d’un menu
qui doit apparaître tout à gauche, comme le menu Fichier. De même, spécifiez
une valeur élevée (elle n’a pas besoin d’être consécutive) à un menu qui doit
apparaître tout à droite (comme le menu Aide).
• Pour remplacer des éléments du menu principal, attribuez la même valeur à la
propriété GroupIndex des éléments du menu enfant.
Cela peut s’appliquer à des groupes d’éléments ou à un seul élément. Si, par
exemple, votre fiche principale contient un élément de menu Edition dont la
propriété GroupIndex vaut 1, vous pouvez le remplacer par un ou plusieurs
éléments du menu de la fiche enfant en attribuant également la valeur 1 à leur
propriété GroupIndex.
Si plusieurs éléments du menu enfant ont la même valeur pour GroupIndex,
leur ordre n’est pas modifié quand ils sont fusionnés au menu principal.
• Pour insérer des éléments sans remplacer des éléments du menu principal,
laissez des intervalles numériques entre les éléments du menu principal et
intercalez les numéros de la fiche enfant.
Vous pouvez par exemple, numéroter les éléments du menu principal 0 et 5,
et insérer les éléments du menu enfant en les numérotant 1, 2, 3 et 4.
Importation de fichiers ressource
C++Builder peut utiliser des menus conçus avec d’autres applications dans la
mesure où ils utilisent le format standard de fichier ressource Windows (.RC).
Vous pouvez importer ces menus directement dans votre projet C++Builder, ce
qui vous évite d’avoir à redéfinir des menus que vous avez conçu par ailleurs.
Pour charger un fichier menu .RC existant :
1 Dans le concepteur de menus, placez le curseur à l’endroit où le menu doit
apparaître.
Le menu importé peut faire partie du menu que vous concevez ou constituer
la totalité d’un menu par lui-même.
2 Cliquez avec le bouton droit de la souris et choisissez Insérer depuis
ressource.
La boîte de dialogue Insertion de menu depuis une ressource s’ouvre.
8-46
Guide du développeur
Conception de barres d’outils et de barres multiples
3 Dans la boîte de dialogue, sélectionnez le fichier ressource à charger, puis
choisissez OK.
Le menu apparaît dans la fenêtre du concepteur de menus.
Remarque
Si votre fichier ressource contient plusieurs menus, vous devez enregistrer
chaque menu dans un fichier ressource séparé avant de les importer.
Conception de barres d’outils et de barres multiples
Une barre d’outils est un volet, généralement placé en haut d’une fiche (sous la
barre de menu) qui contient des boutons et d’autres contrôles. Une barre multiple
est une sorte de barre d’outils qui affiche des contrôles dans des bandes
déplaçables et redimensionnables. Si plusieurs volets sont alignés sur le haut de
la fiche, ils s’empilent verticalement dans l’ordre de leur ajout.
Remarque
Les barres multiples ne peuvent pas être utilisées dans CLX pour les applications
multiplates-formes.
Vous pouvez placer toutes sortes de contrôles dans une barre d’outils. Outre les
boutons, vous pouvez y placer des grilles de couleur, des barres de défilement,
des libellés, etc.
Vous pouvez ajouter une barre d’outils à une fiche de plusieurs façons :
• Placez un composant volet (TPanel) dans la fiche et ajoutez-y des contrôles, en
général des turboboutons.
• Utilisez un composant barre d’outils (TToolBar) à la place de TPanel et ajoutezlui des contrôles. TToolBar gère les boutons et les autres contrôles en les
disposant en lignes et en ajustant automatiquement leur taille et leur position.
Si vous utilisez des contrôles bouton outil (TToolButton) dans la barre d’outils,
TToolBar permet simplement de grouper les boutons de manière fonctionnelle
et propose d’autres options d’affichage.
• Utilisez un composant barre multiple (TCoolBar) et ajoutez-lui des contrôles.
La barre multiple affiche des contrôles dans des bandes qui peuvent être
déplacées et redimensionnées de manière indépendante.
La méthode à employer pour implémenter une barre d’outils dépend de votre
application. Le composant volet présente l’avantage de vous donner une maîtrise
totale de l’aspect de la barre d’outils.
Si vous utilisez des composants barre d’outils ou bande multiple, vous êtes
certain que votre application a bien le style d’une application Windows, car vous
utilisez dans ce cas des contrôles natifs de Windows. Si ces contrôles du système
d’exploitation changent à l’avenir, votre application changera également. Par
ailleurs, comme les composants barre d’outils et barre multiple sont fondés sur
des composants standard Windows, votre application nécessite la présence du
fichier COMCTL32.DLL. Les barres d’outils et les barres multiples ne sont pas
autorisées dans les applications WinNT 3.51.
Conception de l’interface utilisateur des applications
8-47
Conception de barres d’outils et de barres multiples
Les sections suivantes expliquent comment :
• Ajouter une barre d’outils et les contrôles turbobouton correspondants en
utilisant le composant volet.
• Ajouter une barre d’outils et les contrôles bouton outil correspondants en
utilisant le composant barre d’outils.
• Ajouter un composant barre multiple.
• Répondre aux clics.
• Ajouter des barres d’outils et des barres multiples masquées.
• Afficher et masquer des barres d’outils et des barres multiples.
Ajout d’une barre d’outils en utilisant un composant volet
Pour ajouter à une fiche une barre d’outils en utilisant un composant volet :
1 Ajoutez à la fiche un composant volet (à partir de la page Standard de la
palette des composants).
2 Affectez la valeur alTop à la propriété Align du volet. Quand il est aligné sur
le haut de la fiche, le volet conserve sa hauteur mais ajuste sa largeur pour
occuper toute la largeur de la zone client de la fiche, et ce même si la fenêtre
change de taille.
3 Ajoutez des turboboutons ou d’autres contrôles dans le volet.
Les turboboutons sont conçus pour fonctionner dans des volets barre d’outils.
Généralement, un turbobouton n’a pas d’intitulé mais seulement une petite
image (appelée un glyphe) qui représente la fonction du bouton.
Les turboboutons ont trois modes de fonctionnement. Ils peuvent :
• Se comporter comme des boutons poussoirs normaux.
• Se comporter comme des bascules.
• Se comporter comme un ensemble de boutons radio.
Pour implémenter des turboboutons dans des barres d’outils, vous pouvez :
•
•
•
•
•
Ajouter un turbobouton dans un volet barre d’outils.
Affecter un glyphe au turbobouton.
Définir l’état initial du turbobouton.
Créer un groupe de turboboutons.
Utiliser des boutons bascule.
Ajout d’un turbobouton à un volet
Pour ajouter un turbobouton à un volet barre d’outils, placez dans le volet un
composant turbobouton (à partir de la page Supplément de la palette des
composants).
C’est alors le volet et non la fiche qui est le “propriétaire” du turbobouton, donc
déplacer ou masquer le volet déplace ou masque également le turbobouton.
8-48
Guide du développeur
Conception de barres d’outils et de barres multiples
La hauteur par défaut d’un volet est 41 et la hauteur par défaut d’un
turbobouton est 25. Si vous affectez la valeur 8 à la propriété Top de chaque
bouton, ils sont centrés verticalement. Le paramétrage par défaut de la grille
aligne verticalement le turbobouton sur cette position.
Spécification du glyphe d’un turbobouton
Chaque turbobouton a besoin d’une image appelée un glyphe afin d’indiquer à
l’utilisateur la fonction du bouton. Si vous ne spécifiez qu’une seule image pour
le bouton, le bouton manipule l’image afin d’indiquer si le bouton est enfoncé,
relâché, sélectionné ou désactivé. Vous pouvez également spécifier des images
distinctes spécifiques à chaque état.
Normalement, les glyphes sont affectés à un turbobouton à la conception mais il
est possible d’affecter d’autres glyphes à l’exécution.
Pour affecter un glyphe à un turbobouton à la conception :
1 Sélectionnez le turbobouton.
2 Dans l’inspecteur d’objets, sélectionnez la propriété Glyph.
3 Double-cliquez dans la colonne des valeurs à côté de Glyph pour afficher
l’éditeur d’images et sélectionner le bitmap souhaité.
Définition de l’état initial d’un turbobouton
C’est l’aspect visuel d’un turbobouton qui donne à l’utilisateur des indications
sur sa fonction et son état. N’ayant pas d’intitulé, il est indispensable d’utiliser
des indications visuelles pour aider l’utilisateur.
Le tableau suivant décrit comment modifier l’aspect d’un turbobouton :
Tableau 8.6 Paramétrage de l’aspect d’un turbobouton
Pour que le turbobouton :
Initialisez :
Apparaisse enfoncé
Sa propriété GroupIndex à une valeur non nulle et sa
propriété Down à true.
Apparaisse désactivé
Sa propriété Enabled à false.
Dispose d’une marge gauche
Sa propriété Indent à une valeur supérieure à 0.
Si, par exemple, votre application propose un outil de dessin activé par défaut,
vérifiez que le bouton correspondant de la barre d’outils est enfoncé au
démarrage de l’application. Pour ce faire, affectez une valeur non nulle à sa
propriété GroupIndex et la valeur true à sa propriété Down.
Création d’un groupe de turboboutons
Une série de turboboutons représente souvent un ensemble de choix
mutuellement exclusifs. Dans ce cas, vous devez associer les boutons dans un
groupe, afin que l’enfoncement d’un bouton fasse remonter le bouton
précédemment enfoncé du groupe.
Conception de l’interface utilisateur des applications
8-49
Conception de barres d’outils et de barres multiples
Pour associer un nombre quelconque de turboboutons dans un groupe, affectez
la même valeur à la propriété GroupIndex de chacun de ces turboboutons.
Le moyen le plus simple de procéder consiste à sélectionner tous les boutons à
grouper puis à spécifier une même valeur pour leur propriété GroupIndex.
Utilisation de boutons bascule
Dans certains cas, vous voulez pouvoir cliquer sur un bouton déjà enfoncé d’un
groupe afin de le faire remonter, ce qui laisse le groupe sans aucun bouton
enfoncé. Un tel bouton est appelé une bascule. Utilisez la propriété AllowAllUp
pour créer un groupe de boutons qui se comporte ainsi : cliquez une fois; il est
enfoncé, cliquez à nouveau, il remonte.
Pour qu’un groupe de boutons radio se comporte comme une bascule, affectez à
sa propriété AllowAllUp la valeur true.
L’affectation de la valeur true à la propriété AllowAllUp d’un des turboboutons
du groupe l’affecte à tous ceux du groupe. Cela permet au groupe de se
comporter comme un groupe normal, un seul bouton étant sélectionné à la fois
mais, au même moment, tous les boutons peuvent être à l’état relâché.
Ajout d’une barre d’outils en utilisant le composant barre d’outils
Le composant barre d’outils (TToolBar) propose des caractéristiques de gestion
des boutons et de l’affichage dont ne dispose pas le composant volet. Pour
ajouter une barre d’outils à une fiche en utilisant le composant barre d’outils :
1 Ajoutez à la fiche un composant barre d’outils (à partir de la page Win32 de
la palette des composants). La barre d’outils s’aligne automatiquement en haut
de la fiche.
2 Ajoutez des boutons outil ou d’autres contrôles à la barre.
Les boutons outil sont conçus pour fonctionner dans des composants barre
d’outils. Comme les turboboutons, les boutons outil peuvent :
• Se comporter comme des boutons poussoirs normaux.
• Se comporter comme des bascules.
• Se comporter comme un ensemble de boutons radio.
Pour implémenter des boutons outil dans une barre d’outils, vous pouvez :
•
•
•
•
•
Ajouter un bouton outil
Affecter des images à un bouton outil
Définir l’aspect et les conditions initiales du bouton outil
Créer un groupe de boutons outil
Utiliser des boutons outil bascule
Ajout d’un bouton outil
Pour ajouter un bouton outil dans une barre d’outils, cliquez avec le bouton
droit de la souris dans la barre d’outils et choisissez Nouveau bouton.
8-50
Guide du développeur
Conception de barres d’outils et de barres multiples
La barre d’outils est le “propriétaire” du bouton outil : déplacer ou masquer la
barre d’outils déplace et masque également le bouton. De plus, tous les boutons
outil de la barre d’outils ont automatiquement la même largeur et la même
hauteur. Vous pouvez déposer dans la barre d’outils d’autres contrôles de la
palette des composants, ils ont automatiquement une hauteur homogène. De
plus, les contrôles passent à la ligne et commencent une nouvelle ligne quand ils
ne tiennent pas horizontalement sur une seule ligne de la barre d’outils.
Affectation d’images à des boutons outil
Chaque bouton outil dispose de la propriété ImageIndex qui détermine l’image
apparaissant dedans à l’exécution. Si vous ne fournissez qu’une seule image au
bouton outil, le bouton manipule cette image pour indiquer si le bouton est
désactivé. Pour affecter une image à un bouton outil à la conception :
1 Sélectionnez la barre d’outils dans laquelle le bouton apparaît.
2 Dans l’inspecteur d’objet, attribuez un objet TImageList à la propriété Images de
la barre d’outils. Une liste d’images est une collection d’icônes ou de bitmaps
de même taille.
3 Sélectionnez un bouton outil.
4 Dans l’inspecteur d’objets, affectez à la propriété ImageIndex du bouton outil
une valeur entière correspondant à l’image de la liste d’images qui doit être
affectée au bouton.
Vous pouvez également spécifier des images distinctes apparaissant dans les
boutons outil quand ils sont désactivés ou sous le pointeur de la souris. Pour ce
faire, affectez des listes d’images distinctes aux propriétés DisabledImages et
HotImages de la barre d’outils.
Définition de l’aspect et des conditions initiales d’un bouton outil
Le tableau suivant indique comment modifier l’aspect des boutons d’une barre
multiple :
Tableau 8.7 Paramétrage de l’aspect des boutons d’une barre d’outils
Remarque
Pour que le bouton outil :
Initialisez :
Apparaisse enfoncé
(Sur le bouton outil) sa propriété Style à
tbsCheck et sa propriété Down à true.
Apparaisse désactivé
Sa propriété Enabled à false.
Dispose d’une marge gauche
Sa propriété Indent à une valeur supérieure à 0.
Semble avoir une bordure “pop-up”, ce
qui donne à la barre d’outils un aspect
transparent
Sa propriété Flat à true.
L’utilisation de la propriété Flat de TToolBar nécessite une version 4.70 ou
ultérieure de COMCTL32.DLL.
Pour forcer le passage à la ligne après un bouton outil spécifique, sélectionnez le
bouton outil devant apparaître en dernier sur la ligne et affectez la valeur true à
sa propriété Wrap.
Conception de l’interface utilisateur des applications
8-51
Conception de barres d’outils et de barres multiples
Pour désactiver le passage à la ligne automatique dans la barre d’outils, affectez
la valeur false à la propriété Wrapable de la barre d’outils.
Création de groupes de boutons outil
Pour créer un groupe de boutons outils, sélectionnez les boutons à associer et
affectez la valeur tbsCheck à leur propriété Style et la valeur true à leur propriété
Grouped. La sélection d’un bouton outil du groupe désélectionne le choix
précédent dans le groupe de boutons, ce qui permet de représenter un ensemble
de choix mutuellement exclusifs.
Toute séquence non interrompue de boutons outils adjacents dont la propriété
Style a la valeur tbsCheck et la propriété Grouped la valeur true forme un même
groupe. Pour interrompre un groupe de boutons outils, séparez les boutons
avec :
• Un bouton outil dont la propriété Grouped a la valeur false.
• Un bouton outil dont la propriété Style n’a pas la valeur tbsCheck. Pour créer
des espaces ou des séparateurs dans la barre d’outils, ajoutez un bouton outil
de Style tbsSeparator ou tbsDivider.
• Un contrôle d’un type autre que bouton outil.
Utilisation de boutons outil bascule
Utilisez la propriété AllowAllUp pour créer un groupe de boutons outils se
comportant comme une bascule : cliquez une fois pour enfoncer le bouton et une
seconde fois pour le faire remonter. Pour qu’un groupe de boutons outils se
comporte comme une bascule, affectez à sa propriété AllowAllUp la valeur true.
Comme pour les turboboutons, l’affectation de la valeur true à la propriété
AllowAllUp d’un des boutons du groupe affecte automatiquement la même
valeur à tous les boutons du groupe.
Ajout d’un composant barre multiple
Remarque
Le composant TCoolBar nécessite une version 4.70 ou ultérieure de
COMCTL32.DLL et n’est pas disponible dans CLX.
Le composant barre multiple (TCoolBar) — également appelé multibarre — affiche
des contrôles fenêtrés dans des bandes redimensionnables qui peuvent se
déplacer indépendamment les unes des autres. L’utilisateur peut positionner les
bandes faisant glisser des poignées de redimensionnement placées sur le côté de
chaque bande.
Pour ajouter une barre multiple à une fiche dans une application Windows :
1 Ajoutez à la fiche un composant barre multiple (à partir de la page Win32 de
la palette des composants). La barre multiple s’aligne automatiquement en
haut de la fiche.
2 Ajoutez à la barre des contrôles fenêtrés de la palette des composants.
Seuls les composants de la VCL dérivant de TWinControl sont des contrôles
fenêtrés. Vous pouvez ajouter à une barre multiple des contrôles graphiques
8-52
Guide du développeur
Conception de barres d’outils et de barres multiples
(comme les libellés ou les turboboutons), mais ils n’apparaissent pas dans des
bandes distinctes.
Définition de l’aspect de la barre multiple
Le composant barre multiple dispose de plusieurs options de configuration.
Le tableau suivant indique comment modifier l’aspect des boutons d’une barre
multiple :
Tableau 8.8 Paramétrage de l’aspect des boutons d’une barre multiple
Pour que la barre multiple :
Initialisez :
Se redimensionne automatiquement pour
s’adapter aux bandes qu’elle contient.
Sa propriété AutoSize à true.
Dispose de bandes d’une hauteur
uniforme.
Sa propriété FixedSize à true.
Soit orientée à la verticale et pas à
l’horizontale.
Sa propriété Vertical à true. Cela change l’effet
de la propriété FixedSize.
Empêche l’affichage à l’exécution de la
propriété Text des bandes.
Sa propriété ShowText à false. Chaque bande
d’une barre multiple a sa propre propriété Text.
Retire la bordure autour de la barre.
Sa propriété BandBorderStyle à bsNone.
Empêche les utilisateurs de modifier l’ordre
des bandes à l’exécution. L’utilisateur peut
toujours déplacer et redimensionner les
bandes.
Sa propriété FixedOrder à true.
Crée une image de fond pour la barre
multiple.
Sa propriété Bitmap avec un objet TBitmap.
Choisisse une liste d’images apparaissant à
gauche des bandes
Sa propriété Images avec un objet TImageList.
Pour affecter individuellement des images aux bandes, sélectionnez la barre
multiple et, dans l’inspecteur d’objets, double-cliquez sur sa propriété Bands.
Sélectionnez une bande et affectez une valeur à sa propriété ImageIndex.
Réponse aux clics
Quand l’utilisateur clique sur un contrôle, par exemple un bouton d’une barre
d’outils, l’application génère un événement OnClick auquel vous pouvez
répondre avec un gestionnaire d’événement. Etant donné que OnClick est
l’événement par défaut des boutons, vous pouvez générer un gestionnaire
squelette pour l’événement en double-cliquant sur le bouton à la conception.
Affectation d’un menu à un bouton outil
Si vous utilisez une barre d’outils (TToolBar) contenant des boutons outils
(TToolButton), vous pouvez associer un menu à un bouton spécifique :
1 Sélectionnez le bouton outil.
2 Dans l’inspecteur d’objets, affectez un menu surgissant (TPopupMenu) à la
propriété DropDownMenu du bouton outil.
Conception de l’interface utilisateur des applications
8-53
Conception de barres d’outils et de barres multiples
Si la propriété AutoPopup du menu a la valeur true, le menu apparaît
automatiquement quand le bouton est enfoncé.
Ajout de barres d’outils cachées
Les barres d’outils n’ont pas besoin d’être visibles tout le temps. En fait, il est
souvent commode de disposer de plusieurs barres d’outils, mais de n’afficher
que celles dont l’utilisateur veut disposer. Il arrive très souvent qu’une fiche
comporte plusieurs barres d’outils, certaines étant cachées.
Pour créer une barre d’outils cachée :
1 Ajoutez à la fiche un composant barre d’outils, barre multiple ou volet.
2 Affectez la valeur false à la propriété Visible du composant.
Bien que la barre d’outils reste visible à la conception afin que vous puissiez la
modifier, elle reste cachée à l’exécution tant que l’application ne la rend pas
explicitement visible.
Masquage et affichage d’une barre d’outils
Fréquemment, une application dispose de plusieurs barres d’outils mais vous ne
voulez pas encombrer l’écran en les affichant toutes à la fois. Vous pouvez
laisser l’utilisateur décider s’il veut afficher les barres d’outils. Comme tous les
composants, les barres d’outils peuvent être masquées et affichées quand c’est
nécessaire à l’exécution.
Pour masquer ou afficher une barre d’outils à l’exécution, affectez à sa propriété
Visible, respectivement, la valeur false ou true. Généralement vous faites ceci en
réponse à un événement utilisateur particulier ou à un changement du mode de
fonctionnement de l’application. Pour ce faire, chaque barre d’outils dispose
généralement d’un bouton de fermeture. Quand l’utilisateur clique sur ce bouton,
l’application masque la barre d’outils correspondante.
Vous pouvez également proposer un système pour inverser l’état de la barre
d’outils. Dans l’exemple suivant, la visibilité d’une barre d’outils de crayons est
inversée par un bouton de la barre d’outils principale. Comme chaque clic de la
souris enfonce ou libère le bouton, un gestionnaire d’événement OnClick peut
afficher ou masquer la barre d’outils des crayons selon que le bouton est relâché
ou enfoncé.
void __fastcall TForm1::PenButtonClick(TObject *Sender)
{
PenBar->Visible = PenButton->Down;
}
8-54
Guide du développeur
Chapitre
9
Types de contrôles
Chapitre 9
Les contrôles sont des composants visuels qui vous aident à concevoir votre
interface utilisateur.
Ce chapitre décrit les différents contrôles que vous pouvez utiliser, dont les
contrôles texte, les contrôles de saisie, les boutons, les contrôles liste, les
contrôles de regroupement, les contrôles d’affichage, les grilles, les éditeurs de
listes et les contrôles graphiques.
Pour créer un contrôle graphique, voir chapitre 54, “Création d’un contrôle
graphique”. Pour apprendre à implémenter ces contrôles, voir chapitre 6,
“Manipulation des contrôles”.
Contrôles texte
La plupart des applications utilisent des contrôles texte pour afficher du texte à
l’utilisateur. Vous pouvez utiliser :
• Les contrôles de saisie, qui permettent à l’utilisateur de saisir du texte.
Utilisez
ce composant :
Quand l’utilisateur doit :
TEdit
Modifier une seule ligne de texte.
TMemo
Modifier plusieurs lignes de texte.
TMaskEdit
Utiliser un format particulier, par exemple celui d’un code postal
ou d’un numéro de téléphone.
TRichEdit
Modifier plusieurs lignes de texte en utilisant du texte mis en
forme (VCL seulement).
Types de contrôles
9-1
Contrôles texte
• Les contrôles de visualisation de texte et les libellés, qui ne permettent pas à
l’utilisateur de saisir du texte.
Utilisez ce
composant :
Quand l’utilisateur doit :
TTextBrowser
Afficher un fichier texte ou une page HTML simple que
l’utilisateur peut faire défiler.
TTextViewer
Afficher un fichier texte ou une page HTML simple. Les
utilisateurs peuvent faire défiler la page ou cliquer sur des liens
pour voir d’autres pages et d’autres images.
TLCDNumber
Afficher des informations numériques dans un format d’affichage
digital.
TLabel
Afficher du texte dans un contrôle non fenêtré..
TStaticText
Afficher du texte dans un contrôle fenêtré..
Contrôles de saisie
Les contrôles de saisie présentent du texte à l’utilisateur et lui permettent d’en
saisir. Le type de contrôle à employer pour contenir les informations dépend de
la taille et du format des informations.
TEdit et TMaskEdit sont de simples contrôles texte comprenant une boîte texte
d’une ligne dans laquelle vous pouvez entrer des informations. Quand la boîte
texte détient la focalisation, un point d’insertion clignotant apparaît.
Vous pouvez inclure du texte dans la boîte en donnant une valeur chaîne à sa
propriété Text. Vous contrôlez l’apparence du texte dans la boîte en donnant des
valeurs à sa propriété Font. Vous pouvez spécifier la police, la taille, la couleur et
des attributs de fonte. Ces attributs affectent tout le texte de la boîte et ne
peuvent s’appliquer individuellement à chacun des caractères.
Une boîte texte peut être conçue pour changer de taille en fonction de la taille de
la police qu’elle contient. Vous faites cela en définissant la propriété AutoSize par
true. Vous pouvez limiter le nombre de caractères que peut contenir une boîte de
texte en attribuant une valeur à la propriété MaxLength.
TMaskEdit est un contrôle d’édition spécial qui valide le texte entré par le biais
d’un masque indiquant les formats corrects du texte. Le masque peut également
formater le texte affiché à l’utilisateur.
TMemo et TRichEdit permettent d’ajouter plusieurs lignes de texte.
9-2
Guide du développeur
Contrôles texte
Propriétés des contrôles de saisie
Voici quelques propriétés importantes des contrôles de saisie :
Tableau 9.1 Propriétés des contrôles de saisie
Propriété
Description
Text
Détermine le texte qui apparaît dans la boîte de saisie ou le contrôle
mémo.
Font
Contrôle les attributs du texte écrit dans le contrôle boîte texte ou mémo.
AutoSize
Permet à la hauteur de la boîte texte de changer de façon dynamique
selon la font sélectionnée.
ReadOnly
Spécifie si l’utilisateur est autorisé à modifier le texte.
MaxLength
Limite le nombre de caractères d’un contrôle de saisie.
SelText
Contient la partie du texte actuellement sélectionnée (mise en évidence).
SelStart,
SelLength
Indiquent la position des premier et dernier caractères de la partie
sélectionnée du texte.
Contrôles de saisie mémo et texte formaté
Les contrôles TMemo et TRichEdit gèrent plusieurs lignes de texte.
VCL
Le contrôle texte formaté existe uniquement dans la VCL.
TMemo est un autre type de boîte texte, contenant plusieurs lignes de texte.
Les lignes d’un contrôle mémo peuvent s’étendre au-delà de la marge droite de
la boîte texte ou aller à la ligne automatiquement. Vous décidez du retour à la
ligne à l’aide de la propriété WordWrap.
TRichEdit est un composant mémo qui gère le texte mis en forme, l’impression,
la recherche et le glisser-déplacer du texte. Il vous permet de spécifier les
propriétés de police, d’alignement, de tabulation, d’indentation et de
numérotation.
Outre les propriétés de tous les contrôles de saisie, les contrôles mémo et éditeur
de texte formaté proposent d’autres propriétés, dont :
• Alignment spécifie comment le texte est aligné (gauche, droite ou centré) à
l’intérieur du composant.
• La propriété Text contient le texte du contrôle. Votre application peut
déterminer si le texte a été modifié en examinant la propriété Modified.
• Lines contient le texte sous la forme d’une liste de chaînes.
• OEMConvert détermine si le texte du contrôle est converti en caractères OEM.
Cela s’avère utile pour valider les noms de fichiers (VCL seulement).
• WordWrap détermine si le texte revient à la ligne après la marge droite.
• WantReturns détermine si l’utilisateur peut insérer des passages à la ligne dans
le texte.
• WantTabs détermine si l’utilisateur peut insérer des tabulations dans le texte.
Types de contrôles
9-3
Contrôles texte
• AutoSelect détermine si le texte est automatiquement sélectionné (mis en
évidence) quand le contrôle devient actif.
A l’exécution, vous pouvez sélectionner tout le texte d’un mémo en utilisant la
méthode SelectAll.
Contrôles de visualisation de texte (CLX seulement)
Les contrôles de visualisation de texte affiche du texte en lecture seulement.
TTextViewer permet juste de visualiser un texte que l’utilisateur peut lire et faire
défiler. Avec TTextBrowser, les utilisateurs également peuvent cliquer sur les liens
pour naviguer vers d’autres documents et d’autres parties du même document.
Les documents visités sont stockés dans une liste d’historique, dans laquelle on
peut naviguer en utilisant les méthodes Backward, Forward et Home. TTextViewer
et TTextBrowser sont généralement utilisés pour afficher du texte HTML ou pour
afficher un système d’aide au format HTML.
TTextBrowser a les même propriétés que TTextViewer plus Factory. Factory
détermine l’objet fabrique MIME utilisé pour déterminer les types de fichiers
pour les images incorporées. Vous pouvez, par exemple, associer des extensions
de fichier (comme .txt, .html ou .xml) avec des types MIME et faire charger ces
données dans le contrôle par le fabriquant.
Utilisez la propriété FileName pour faire apparaître un fichier texte, par exemple
.html, dans le contrôle à l’exécution.
Libellés
Les libellés (TLabel et TStaticText (VCL uniquement)) servent à afficher du texte,
généralement ils sont placés à côté d’autres contrôles. Vous placez un libellé sur
une fiche lorsque vous avez besoin d’identifier ou d’annoter un autre composant,
comme une boîte de saisie, ou lorsque vous voulez inclure du texte dans la fiche.
Le composant libellé standard, TLabel, est un contrôle non-fenêtré (dans CLX,
non basé sur un widget), qui ne peut donc pas recevoir la focalisation ; si vous
avez besoin d’un libellé disposant d’un handle de fenêtre, utilisez à la place
TStaticText.
Les propriétés des libellés sont les suivantes :
• Caption contient la chaîne de texte du libellé.
• Font, Color et d’autres propriétés déterminent l’apparence du libellé. Chaque
libellé ne peut utiliser qu’une seule police, taille et couleur.
• FocusControl relie le contrôle libellé à un autre contrôle de la fiche. Si Caption
comporte une touche accélératrice, le contrôle spécifié dans la propriété
FocusControl obtient la focalisation quand l’utilisateur appuie sur la touche de
raccourci.
• ShowAccelChar détermine si le libellé peut afficher un caractère de raccourci
souligné. Si ShowAccelChar a la valeur true, tout caractère précédé d’un &
apparaît souligné et active une touche de raccourci.
9-4
Guide du développeur
Contrôles de saisies spécialisées
• Transparent détermine si les éléments sur lesquels le libellé est placé (par
exemple des images) sont visibles.
Les libellés contiennent généralement du texte statique en lecture seule, que
l’utilisateur de l’application ne peut pas modifier. Vous pouvez modifier le texte
lorsque l’application est exécutée en attribuant une nouvelle valeur à la propriété
Caption. Pour ajouter à une fiche un objet texte que l’utilisateur peut faire défiler
ou modifier, utilisez TEdit.
Contrôles de saisies spécialisées
Les composants suivants proposent d’autres méthodes pour recevoir des saisies.
Utilisez ce
composant :
Quand l’utilisateur doit :
TScrollBar
Sélectionner des valeurs dans un intervalle continu.
TTrackBar
Sélectionner des valeurs dans un intervalle continu (visuellement plus
parlant qu’une barre de défilement).
TUpDown
Sélectionner une valeur à l’aide d’un incrémenteur associé à un
composant de saisie (VCL seulement)
THotKey
Entrer des séquences clavier Ctrl/Maj/Alt (VCL seulement).
TSpinEdit
Sélectionner une valeur à l’aide d’un widget incrémenteur (CLX
seulement).
Barres de défilement
Le composant barre de défilement crée une barre de défilement utilisée pour
faire défiler le contenu d’une fenêtre, d’une fiche ou d’un autre contrôle. Le code
écrit dans le gestionnaire d’événement OnScroll détermine comment le contrôle
se comporte quand l’utilisateur fait défiler la barre de défilement.
Le composant barre de défilement est rarement utilisé car la plupart des
composants visuels disposent de leurs propres barres de défilement sans
nécessiter de programmation. Par exemple, TForm propose les propriétés
VertScrollBar et HorzScrollBar qui configurent automatiquement des barres de
défilement pour la fiche. Pour créer une région défilante dans une fiche, utilisez
TScrollBox.
Barres graduées
Une barre graduée peut définir des valeurs entières dans un intervalle continu.
Elle sert à ajuster des propriétés telle qu’une coleur, un volume ou une
luminosité. L’utilisateur déplace la glissière en la faisant glisser à une position
donnée ou en cliquant dans la barre.
• Utilisez les propriétés Max et Min pour définir les bornes supérieure et
inférieure de l’intervalle de la barre graduée.
Types de contrôles
9-5
Contrôles de saisies spécialisées
• Utilisez SelEnd et SelStart pour mettre en évidence un intervalle sélectionné.
Voir figure 9.1.
• La propriété Orientation détermine si la barre graduée est verticale ou
horizontale.
• Par défaut, une barre graduée dispose d’une ligne de graduations en bas.
Utilisez la propriété TickMarks pour modifier leur emplacement. Pour contrôler
l’espacement des graduations, utilisez la propriété TickStyle et la méthode
SetTick.
Figure 9.1
Trois vues du composant barre graduée
• Position définit une position par défaut dans la barre graduée et indique à
l’exécution la valeur sélectionnée par l’utilisateur.
• Par défaut, l’utilisateur peut se déplacer d’une graduation vers le haut ou vers
le bas en utilisant les touches de déplacement correspondantes. Affectez
LineSize pour changer cet incrément.
• Affectez PageSize pour déterminer le nombre de graduations du déplacement
quand l’utilisateur appuie sur les touches Pg. Haut et Pg. Bas.
Contrôles flèches haut-bas (VCL seulement)
Un contrôle flèches haut-bas (TUpDown) est constitué d’une paire de boutons
fléchés qui permettent à l’utilisateur de modifier une valeur entière d’un
incrément fixe. La valeur en cours est donnée par la propriété Position ;
l’incrément, qui vaut 1 par défaut, est spécifié par la propriété Increment. Utilisez
la propriété Associate pour associer un autre composant (comme un contrôle
d’édition) au contrôle haut-bas.
Contrôles incrémenteur (CLX seulement)
Un contrôle incrémenteur (TSpinEdit) est également appelé widget haut-bas,
comme le widget flèches ou le bouton incrémenteur. Ce contrôle permet à
l’utilisateur de l’application de changer une valeur entière par incréments fixes,
soit en cliquant sur les boutons fléchés haut ou bas pour augmenter ou diminuer
la valeur affichée, soit en tapant directement la valeur dans la boîte de
l’incrémenteur.
La valeur en cours est donnée par la propriété Value ; l’incrément, qui vaut 1 par
défaut, est spécifié par la propriété Increment.
9-6
Guide du développeur
Boutons et contrôles similaires
Contrôles touche d’accès rapide (VCL seulement)
Utilisez le composant touche d’accès rapide (THotKey) pour affecter une séquence
de touches qui transfère la focalisation à un composant. La propriété HotKey
contient la combinaison de touches en cours et la propriété Modifiers détermine
les touches disponibles pour HotKey.
Le composant raccourci clavier peut être affecté à la propriété ShortCut d’un
élément de menu. Ensuite, lorsqu’un utilisateur saisit la combinaison de touches
spécifiée par les propriétés HotKey et Modifiers, Windows active l’élément
de menu.
Contrôles séparateur
Un séparateur (TSplitter) placé entre deux contrôles alignés permet aux
utilisateurs de redimensionner les contrôles. Utilisés avec des composants comme
les volets ou les boîtes de groupe, les séparateurs vous permettent de
décomposer une fiche en plusieurs volets contenant chacun plusieurs contrôles.
Après avoir placé un volet ou un autre contrôle dans une fiche, ajoutez un
séparateur ayant le même alignement que le contrôle. Le dernier contrôle doit
être aligné sur le client afin qu’il remplisse tout l’espace restant quand les autres
sont redimensionnés. Vous pouvez, par exemple, placer un volet sur le bord
gauche d’une fiche, initialiser sa propriété Alignment par alLeft, puis placer un
séparateur (ayant également l’alignement alLeft) à droite du volet, et enfin placer
un autre volet (avec l’alignement alLeft ou alClient) à droite du séparateur.
Initialisez MinSize afin de spécifier la taille minimum que le séparateur doit
laisser quand il redimensionne le contrôle adjacent. Initialisez Beveled à true pour
donner au séparateur un aspect 3D.
Boutons et contrôles similaires
En dehors des menus, les boutons constituent le moyen le plus simple de
déclencher une commande dans une application. C++Builder propose plusieurs
contrôles de type bouton :
Utilisez ce
composant :
Pour :
TButton
Présenter des choix de commandes avec du texte dans des boutons
TBitBtn
Présenter des choix de commandes dans des boutons contenant du
texte et des glyphes
TSpeedButton
Créer des groupes de boutons dans les barres d’outils
TCheckBox
Présenter des options de type Oui/Non
TRadioButton
Présenter un ensemble de choix mutuellement exclusifs
Types de contrôles
9-7
Boutons et contrôles similaires
Utilisez ce
composant :
Pour :
TToolBar
Disposer des boutons et d’autres contrôles en ligne et ajuster
automatiquement leur taille et leur position
TCoolBar
Afficher une collection de contrôles fenêtrés dans des bandes
déplaçables et redimensionnables (VCL seulement)
Contrôles bouton
Les utilisateurs cliquent sur les contrôles bouton pour initier des actions. Les
boutons sont libellés par du texte qui représente l’action. Vous spécifiez le texte
en attribuant une valeur chaîne à la propriété Caption. Vous pouvez aussi
sélectionner la plupart des boutons en appuyant sur une touche du clavier,
appelée raccourci clavier. Le raccourci est indiqué sur le bouton par une lettre
soulignée.
Les utilisateurs cliquent sur les contrôles bouton pour initier des actions. Vous
pouvez associer une action à un composant TButton en créant un gestionnaire
d’événement OnClick correspondant. En double-cliquant sur un bouton à la
conception, vous affichez le gestionnaire d’événement OnClick du bouton dans
l’éditeur de code.
• Affectez la valeur true à la propriété Cancel pour que le bouton déclenche son
événement OnClick quand l’utilisateur appuie sur Echap.
• Affectez la valeur true à la propriété Default pour que la touche Entrée
déclenche l’événement OnClick du bouton.
Boutons bitmap
Un bouton bitmap (BitBtn) est un contrôle bouton qui contient une image
bitmap.
• Pour attribuer un bitmap personnalisé à votre bouton, affectez la propriété
Glyph.
• Utilisez la propriété Kind pour configurer automatiquement un bouton avec
un glyphe et un comportement par défaut.
• Par défaut, le glyphe est à gauche du texte. Pour le déplacer, utilisez la
propriété Layout.
• Le glyphe et le texte sont automatiquement centrés dans le bouton. Pour
changer leur position, utilisez la propriété Margin. Margin détermine le
nombre de pixels entre le bord de l’image et le bord du bouton.
• Par défaut, l’image et le texte sont séparés par 4 pixels. Utilisez Spacing pour
augmenter ou réduire cette distance.
• Les boutons bitmap peuvent avoir 3 états : haut, bas et enfoncé. Affectez la
valeur 3 à la propriété NumGlyphs pour attribuer un bitmap différent à
chaque état.
9-8
Guide du développeur
Boutons et contrôles similaires
Turboboutons
Les turboboutons, qui affichent généralement une image, peuvent fonctionner en
groupe. Ils sont souvent utilisés avec des volets pour créer des barres d’outils.
• Pour faire fonctionner des turboboutons en groupe, affectez à la propriété
GroupIndex de tous les boutons la même valeur non-nulle.
• Par défaut, des turboboutons apparaissent à l’état haut (non sélectionné). Pour
afficher un turbobouton à l’état sélectionné, affectez la valeur true à la
propriété Down.
• Si AllowAllUp a la valeur true, tous les turboboutons d’un groupe peuvent être
non sélectionnés. Affectez la valeur false à AllowAllUp pour qu’un groupe de
boutons se comporte comme un groupe de boutons radio.
Pour davantage d’informations sur les turboboutons, voir “Ajout d’une barre
d’outils en utilisant un composant volet” à la page 8-48 et “Organisation des
actions pour les barres d’outils et les menus” à la page 8-18.
Cases à cocher
Une case à cocher est une bascule qui permet à l’utilisateur de sélectionner un
état activé ou désactivé. Quand l’option est activée, la case est cochée. Sinon, la
case à cocher est vide. Vous créez des cases à cocher à l’aide de TCheckBox.
• Affectez true à Checked pour que la case soit cochée par défaut.
• Affectez true à AllowGrayed pour que la case à cocher puisse prendre trois
états : activée, désactivée et grisée.
• La propriété State indique si la case est cochée (cbChecked), non cochée
(cbUnchecked) ou grisée (cbGrayed).
Remarque
Les contrôles case à cocher affichent un des deux états binaires. L’état
indéterminé est utilisé quand les autres choix rendent impossible de déterminer
la valeur en cours de la case à cocher.
Boutons radio
Les boutons radio proposent un ensemble de choix mutuellement exclusifs. Vous
pouvez créer des boutons radio individuels à l’aide de TRadioButton ou utiliser le
composant groupe de boutons radio (TRadioGroup) qui regroupe automatiquement
des boutons radio. Cela permet à l’utilisateur de sélectionner une option dans un
ensemble de choix limité. Pour davantage d’informations, voir “Regroupement
de contrôles” à la page 9-14.
Un bouton radio sélectionné s’affiche sous forme d’un cercle dont le centre est
rempli. S’il n’est pas sélectionné, le bouton radio affiche un cercle vide. Donnez
la valeur true ou false à la propriété Checked pour changer l’état visuel du
bouton radio.
Types de contrôles
9-9
Boutons et contrôles similaires
Barres d’outils
Les barres d’outils permettent aisément d’organiser et de gérer des contrôles
visuels. Vous pouvez créer une barre d’outils à partir d’un composant volet et de
turboboutons, ou utiliser le composant ToolBar puis choisir Nouveau bouton dans
son menu contextuel pour chaque bouton à ajouter.
Le composant TToolBar présente plusieurs avantages : les boutons d’une barre
d’outils ont automatiquement des dimensions et un espacement homogènes, les
autres contrôles conservent leur position et hauteur relatives ; les contrôles
peuvent automatiquement passer à la ligne s’il n’y a pas assez de place
horizontalement. Le composant TToolBar propose également des options comme
la transparence, les bordures en relief et les espaces et des séparations pour
regrouper des contrôles.
Vous pouvez utiliser un ensemble d’actions regroupées sur des barres d’outils et
des menus, en utilisant des listes d’actions ou des bandes d’actions. Reportez-vous à
“Utilisation des listes d’actions” à la page 8-26 pour savoir comment utiliser les
listes d’actions avec boutons et barres d’outils.
Les barres d’outils peuvent aussi être parents d’autres contrôles, comme les
boîtes de saisie, les boîtes à options, etc.
Barres multiples (VCL seulement)
Une barre multiple contient des contrôles enfant pouvant être déplacés et
redimensionnés de manière indépendante. Chaque contrôle se trouve dans une
bande indépendante. L’utilisateur positionne les contrôles en utilisant la poignée
de redimensionnement à gauche de chaque bande.
A la conception et à l’exécution, la barre multiple exige une version 4.70, ou
ultérieure, de COMCTL32.DLL (qui se trouve généralement dans le répertoire
Windows\System ou Windows\System32). Les barres multiples ne peuvent pas
être utilisées dans les applications multiplates-formes.
• La propriété Bands contient une collection d’objets TCoolBand. A la conception,
vous pouvez ajouter, retirer ou modifier les bandes en utilisant l’éditeur de
bandes. Pour l’ouvrir, sélectionnez la propriété Bands dans l’inspecteur d’objets
puis double-cliquez dans la colonne des valeurs à droite ou cliquez sur le
bouton Points de suspension (...). Vous pouvez également créer des bandes en
ajoutant de nouveaux contrôles fenêtrés de la palette.
• La propriété FixedOrder détermine si les utilisateurs peuvent réorganiser les
bandes.
• La propriété FixedSize détermine si les bandes ont une hauteur uniforme.
9-10
Guide du développeur
Contrôles liste
Contrôles liste
Les listes proposent à l’utilisateur une collection d’éléments dans laquelle il peut
choisir. Plusieurs composants affichent des listes :
Utilisez ce
composant :
Pour afficher :
TListBox
Une liste de chaînes de texte
TCheckListBox
Une liste avec une case à cocher devant chaque élément
TComboBox
Une boîte de saisie avec une liste surgissante déroulante
TTreeView
Une liste hiérarchique
TListView
Une liste d’éléments (déplaçables) avec éventuellement des icônes,
des en-têtes et des colonnes
TIconView
(CLX seulement)
Une liste d’éléments ou de données, en lignes et en colonnes, sous
forme de petites ou grandes icônes.
TDateTimePicker
Une boîte liste permettant de saisir des dates ou des heures
(VCL seulement)
TMonthCalendar
Un calendrier permettant de sélectionner des dates (VCL seulement)
Utilisez les composants non-visuels TStringList et TImageList pour gérer des
ensembles de chaînes ou d’images. Pour davantage d’informations sur les listes
de chaînes, voir “Utilisation des listes de chaînes” à la page 4-17.
Boîtes liste et boîtes liste de cases à cocher
Les boîtes liste (TListBox) et les boîtes liste de cases à cocher affichent une liste
dans laquelle l’utilisateur peut sélectionner des éléments.
• Items utilise un objet TStrings pour remplir le contrôle avec des valeurs.
• ItemIndex indique l’élément sélectionné dans la liste.
• MultiSelect spécifie si l’utilisateur peut sélectionner plusieurs éléments à la fois.
• Sorted détermine si la liste est triée alphabétiquement.
• Columns spécifie le nombre de colonnes dans le contrôle liste.
• IntegralHeight spécifie si la boîte liste n’affiche que des entrées affichées en
entier verticalement (VCL seulement).
• ItemHeight spécifie la hauteur, exprimée en pixels, de chaque élément de la
liste. La propriété Style peut neutraliser l’effet de ItemHeight.
• La propriété Style détermine comment une boîte liste affiche ses éléments. Par
défaut, les éléments sont affichés sous la forme d’une chaîne. En modifiant la
valeur de Style, vous pouvez créer des boîtes liste dessinées par le propriétaire,
dans ce cas les éléments peuvent être graphiques et de hauteur fixe ou de
hauteur variable. Pour plus d’informations sur les contrôles dessinés par le
propriétaire, voir “Ajout de graphiques à des contrôles” à la page 6-12.
Types de contrôles
9-11
Contrôles liste
Pour créer une boîte liste simple,
1 Dans le projet, faites glisser un composant boîte liste sur une fiche depuis la
palette des composants.
2 Redimensionnez la boîte liste et définissez son alignement, si nécessaire.
3 Double-cliquez sur la partie droite de la propriété Items ou choisissez le
bouton Points de suspension pour afficher l’éditeur de liste de chaînes.
4 Utilisez l’éditeur pour entrer des lignes de texte libre comme contenu de la
boîte liste.
5 Puis, choisissez OK.
Pour permettre aux utilisateurs de sélectionner plusieurs éléments de la liste,
utilisez les propriétés ExtendedSelect et MultiSelect.
Boîtes à options
Une boîte à options (TComboBox) combine une boîte de saisie et une liste
déroulante. Quand les utilisateurs saisissent des données, en entrant du texte
dans la boîte de saisie ou en sélectionnant un élément de la liste, la valeur de la
propriété Text change. Si AutoComplete est activée, l’application recherche et
affiche la correspondance la plus proche dans la liste au fur et à mesure que
l’utilisateur tape des données.
Les trois types de boîtes à options sont : standard, déroulante (par défaut) et liste
déroulante.
• Utilisez la propriété Style pour spécifier le type de boîte à options que vous
souhaitez.
• Utilisez csDropDown si vous voulez une boîte de saisie avec une liste
déroulante. Utilisez csDropDownList pour que la boîte de saisie soit en lecture
seule (ce qui oblige les utilisateurs à sélectionner dans la liste). Initialisez la
propriété DropDownCount pour changer le nombre d’éléments affichés dans la
liste.
• Utilisez csSimple pour créer une boîte à options avec une liste fixe qui reste
toujours ouverte. Prenez soin de redimensionner la boîte à options pour que
les éléments de la liste soient affichés.
• Utilisez csOwnerDrawFixed ou csOwnerDrawVariable pour créer des boîtes à
options dessinées par le propriétaire qui affichent des éléments graphiques ou de
hauteur variable. Pour plus d’informations sur les contrôles dessinés par le
propriétaire, voir “Ajout de graphiques à des contrôles” à la page 6-12.
Pendant l’exécution, les boîtes à options CLX fonctionnent différemment des
boîtes à options VCL. Dans CLX (mais pas dans la boîte à options VCL), vous
pouvez ajouter un élément à une liste déroulante en entrant du texte et en
appuyant sur Entrée dans le champ d’édition d’une boîte à options. Vous pouvez
désactiver cette fonctionnalité en définissant InsertMode par ciNone. Il est
également possible d’ajouter des éléments vides (sans chaîne) à la liste de la
boîte à options. De plus, si vous maintenez la flèche bas enfoncée, vous ne vous
arrêtez pas au dernier élément de la liste. Vous refaites un tour en
recommençant au début.
9-12
Guide du développeur
Contrôles liste
Vues arborescentes
Une vue arborescente (TTreeView) affiche des éléments dans une table des
matières indentée. Le contrôle propose des boutons qui permettent de
développer ou de réduire les nœuds. Vous pouvez inclure des icônes en plus du
libellé des éléments et afficher différentes icônes pour indiquer si un nœud est
développé ou réduit. Vous pouvez également inclure des éléments graphiques,
par exemple des cases à cocher, afin de refléter des informations sur l’état des
éléments.
• Indent définit le nombre de pixels séparant horizontalement les éléments de
leurs parents.
• ShowButtons active l’affichage des boutons ’+’ et ’–’ pour indiquer si un
élément peut être développé.
• ShowLines active l’affichage de lignes de connexion qui montrent les relations
hiérarchiques (VCL seulement).
• ShowRoot détermine si des lignes connectent les éléments racine (VCL
seulement).
Pour lui ajouter des éléments au cours de la conception, double-cliquez sur le
contrôle vue arborescente afin d’afficher l’éditeur d’éléments TreeView. Les
éléments ajoutés deviennent la valeur de la propriété Items. Vous pouvez
modifier les éléments pendant l’exécution en utilisant les méthodes de la
propriété Items, qui est un objet de type TTreeNodes. TTreeNodes possède des
méthodes pour ajouter des éléments, supprimer des éléments et naviguer entre
les éléments dans la vue arborescente.
Les vues arborescentes peuvent afficher des colonnes et des sous-éléments
semblables aux vues liste en mode vsReport.
Vues liste
Les vues liste, créées à l’aide de TListView, affichent des listes dans divers
formats. Utilisez la propriété ViewStyle pour choisir le type de liste utilisé :
• vsIcon et vsSmallIcon affichent chaque élément sous la forme d’une icône avec
un libellé. Les utilisateurs peuvent faire glisser les éléments dans la fenêtre de
la vue liste (VCL seulement).
• vsList affiche les éléments comme icônes libellées qui ne peuvent pas être
déplacées.
• vsReport affichent les éléments à raison d’un par ligne avec des informations
organisées en colonnes. La colonne de gauche contient une petite icône et un
libellé et les autres colonnes contiennent des sous-éléments spécifiés par
l’application. Utilisez la propriété ShowColumnHeaders afin d’afficher des entêtes de colonne.
Types de contrôles
9-13
Regroupement de contrôles
Sélecteurs Date/Heure et calendriers mensuels (VCL seulement)
Le composant sélecteur date/heure affiche une boîte liste permettant de saisir
des dates ou des heures. Le composant calendrier mensuel propose un calendrier
permettant de saisir des dates ou des plages de dates. Pour utiliser ces
composants, que ce soit à la conception ou à l’exécution, vous devez avoir la
version 4.70, ou une version ultérieure, de COMCTL32.DLL (normalement dans
le répertoire Windows\System ou Windows\System32). Ils ne peuvent pas être
utilisés dans les applications multiplates-formes.
Regroupement de contrôles
Une interface utilisateur graphique est plus facile à utiliser quand des contrôles
et les contrôles associés sont présentés dans des groupes. C++Builder propose
plusieurs composants permettant de regrouper des composants :
Utilisez ce
composant :
Pour :
TGroupBox
Une boîte groupe standard avec un titre
TRadioGroup
Un groupe simple de boutons radio
TPanel
Un groupe de contrôles plus flexible visuellement
TScrollBox
Une zone défilante contenant des contrôles
TTabControl
Un ensemble d’onglets (du type classeur) mutuellement exclusifs
TPageControl
Un ensemble d’onglets (du type classeur) mutuellement exclusifs avec
les pages correspondantes, chacune pouvant contenir d’autres
contrôles
THeaderControl
Des en-têtes de colonne redimensionnables
Boîtes groupe et groupes de boutons radio
Une boîte groupe (TGroupBox) associe des contrôles d’une fiche. Les contrôles les
plus fréquemment regroupés sont les boutons radio. Après avoir placé une boîte
groupe dans une fiche, sélectionnez les composants dans la palette des
composants et placez-les dans la boîte groupe. La propriété Caption contient le
texte qui sert à libeller la boîte groupe à l’exécution.
Le composant groupe de boutons radio (TRadioGroup) simplifie le regroupement
de boutons radio et gère leur fonctionnement en commun. Pour ajouter des
boutons radio à un groupe, modifiez la propriété Items dans l’inspecteur
d’objets ; chaque chaîne de Items constitue un bouton radio qui apparaît dans le
groupe en utilisant la chaîne spécifiée comme libellé. La valeur de la propriété
ItemIndex détermine le bouton radio sélectionné. Affichez les boutons radio sur
une ou plusieurs colonnes en définissant la valeur de la propriété Columns. Pour
espacer les boutons, redimensionnez le composant groupe de boutons radio.
9-14
Guide du développeur
Regroupement de contrôles
Volets
Le composant TPanel constitue un conteneur générique pour d’autres contrôles.
Les volets sont généralement utilisés pour regrouper visuellement des
composants sur une fiche. Il est possible d’aligner des volets dans la fiche pour
conserver la même position relative quand la fiche est redimensionnée.
La propriété BorderWidth détermine la largeur, en pixels, de la bordure entourant
un volet.
Vous pouvez aussi placer d’autres contrôles sur un volet et utiliser la propriété
Align pour positionner correctement tous les contrôles du groupe ou de la fiche.
Vous pouvez choisir pour un volet un alignement alTop, pour que sa position
soit maintenue même si la fiche est redimensionnée.
S vous voulez que le volet paraisse élevé ou enfoncé, utilisez les propriétés
BevelOuter et BevelInner. Vous pouvez varier les valeurs de ces propriétés pour
créer différents effets visuels 3D. Remarquez que si vous voulez seulement un
biseau élevé ou enfoncé, il vaut mieux utiliser le contrôle TBevel, moins
gourmand en ressources.
Vous pouvez aussi utiliser les volets pour construire des barres d’état ou des
zones d’affichage d’information.
Boîtes de défilement
Les boîtes de défilement (TScrollBox) permettent de créer des zones défilantes à
l’intérieur d’une fiche. Souvent, les applications ont besoin d’afficher plus
d’informations qu’il ne peut apparaître dans une zone particulière. Certains
contrôles, comme les boîtes liste, les mémos ou les fiches mêmes, peuvent
automatiquement faire défiler leur contenu.
Les boîtes de défilement s’utilisent aussi pour créer des zones de défilement
(vues) multiples dans une fenêtre. Les vues sont fréquentes dans les traitements
de texte, les tableurs et les applications de gestion. Les boîtes de défilement vous
offrent davantage de souplesse en vous permettant de définir arbitrairement une
zone défilante dans une fiche.
Comme les volets et les boîtes groupe, les boîtes de défilement contiennent
d’autres contrôles, comme les objets TButton et TCheckBox. Mais, normalement
une boîte de défilement est invisible. Si les contrôles qu’elle contient ne peuvent
rentrer dans sa partie visible, la boîte de défilement affiche automatiquement des
barres de défilement.
A l’aide d’une boîte de défilement, vous pouvez aussi empêcher le défilement
dans certaines zones d’une fenêtre, par exemple dans une barre d’outils ou dans
une barre d’état (composants TPanel). Pour empêcher le défilement dans une
barre d’outils ou dans une barre d’état, cachez les barres de défilement, puis
placez une boîte de défilement dans la zone client de la fenêtre, entre la barre
d’outils et la barre d’état. Les barres de défilement associées à la boîte de
défilement sembleront appartenir à la fenêtre, mais vous pourrez seulement faire
défiler la zone se trouvant à l’intérieur de la boîte de défilement.
Types de contrôles
9-15
Contrôles d’affichage
Contrôles onglets
Le composant contrôle onglets (TTabControl) crée un ensemble d’onglets
semblables aux séparateurs d’un classeur. Vous pouvez créer des onglets en
modifiant la propriété Tabs à l’aide de l’inspecteur d’objets ; chaque chaîne de
Tabs représente un onglet. Le contrôle onglets est un simple volet avec un seul
ensemble de composants dedans. Pour changer l’aspect du contrôle quand les
onglets sont sélectionnés, écrivez un gestionnaire d’événement OnChange.
Pour créer une boîte de dialogue multipage, utilisez plutôt un contrôle pages.
Contrôles pages
Le composant contrôle pages (TPageControl) est un ensemble de pages utilisé
pour constituer une boîte de dialogue multipage. Un contrôle pages affiche
plusieurs pages les unes sur les autres, et ce sont des objets TTabSheet. Vous
sélectionnez une page dans l’interface utilisateur en cliquant sur son onglet, en
haut du contrôle.
Pour créer une nouvelle page dans un contrôle pages lors de la conception,
cliquez avec le bouton droit de la souris sur le contrôle pages et choisissez
Nouvelle page. A l’exécution, vous ajoutez de nouvelles pages en créant l’objet
correspondant à la page et en définissant sa propriété PageControl :
TTabSheet *pTabSheet = new TTabSheet(PageControl1);
pTabSheet->PageControl = PageControl1;
Pour accéder à la page active, utilisez la propriété ActivePage. Pour changer de
page active, définissez la propriété ActivePage ou la propriété ActivePageIndex.
Contrôles en-têtes
Un contrôle en-têtes (THeaderControl) est un ensemble d’en-têtes de colonnes que
l’utilisateur peut sélectionner ou redimensionner à l’exécution. Modifiez la
propriété Sections du contrôle pour ajouter ou modifier les en-têtes. Vous pouvez
placer les sections d’en-tête au-dessus des colonnes ou des champs. Par exemple,
les sections d’en-tête peuvent être placées sur une boîte liste (TListBox).
Contrôles d’affichage
Il existe plusieurs moyens de donner à l’utilisateur des informations sur l’état
d’une application. Par exemple, certains composants, dont TForm, disposent de la
propriété Caption qui peut être définie à l’exécution. Vous pouvez également
créer des boîtes de dialogue pour afficher des messages. De plus, les composants
suivants sont particulièrement utiles pour fournir des indications visuelles à
l’exécution.
9-16
Guide du développeur
Contrôles d’affichage
Utilisez ce composant
ou cette propriété :
Pour :
TStatusBar
Afficher une zone d’état (généralement en bas d’une fenêtre)
TProgressBar
Afficher le pourcentage effectué d’une tâche donnée
Hint et ShowHint
Activer les conseils d’aide (appelés aussi bulles d’aide)
HelpContext et HelpFile
Effectuer la liaison avec le système d’aide en ligne
Barres d’état
Même si vous pouvez utiliser un volet pour créer une barre d’état, il est plus
simple d’utiliser le composant barre d’état. Par défaut, la propriété Align d’une
barre d’état a la valeur alBottom, ce qui gère à la fois la position et la taille.
Si vous voulez afficher une seule chaîne de texte à la fois dans la barre d’état,
définissez sa propriété SimplePanel par true et utilisez la propriété SimpleText
pour contrôler le texte affiché dans la barre d’état.
Vous pouvez aussi diviser une barre d’état en plusieurs zones de texte, appelées
volets. Pour créer des volets, modifiez la propriété Panels avec l’inspecteur
d’objets et spécifiez les propriétés Width, Alignment et Text de chaque volet à
l’aide de l’éditeur de volets. La propriété Text de chaque volet contient le texte
affiché dans le volet.
Barres de progression
Quand une application effectue une opération longue, vous pouvez utiliser une
barre de progression pour indiquer le pourcentage réalisé de l’opération. Une
barre de progression affiche une ligne pointillée qui progresse de gauche à
droite.
Figure 9.2
Une barre de progression
La propriété Position indique la longueur de la ligne pointillée. Max et Min
déterminent l’étendue des valeurs prises par Position. Pour allonger la ligne,
augmentez Position en appelant la méthode StepBy ou StepIt. La propriété Step
détermine l’incrément utilisé par StepIt.
Propriétés d’aide ou de conseil d’aide
La plupart des contrôles visuels peuvent, à l’exécution, afficher de l’aide
contextuelle ou des conseils d’aide. Les propriétés HelpContext et HelpFile
spécifient un numéro de contexte d’aide le nom du fichier d’aide pour un
contrôle.
Types de contrôles
9-17
Grilles
La propriété Hint spécifie la chaîne de texte qui apparaît quand l’utilisateur
déplace le pointeur de la souris au-dessus d’un contrôle ou d’un élément de
menu. Pour activer les conseils, définissez ShowHint par true ; l’initialisation de
ParentShowHint à true force la propriété ShowHint du contrôle à prendre la même
valeur que celle de son parent.
Grilles
Les grilles affichent des informations disposées en lignes et en colonnes. Si vous
concevez une application de base de données, utilisez les composants TDBGrid et
TDBCtrlGrid décrits au chapitre 19, “Utilisation de contrôles de données”. Sinon,
utilisez une grille de dessin ou une grille de chaînes standard.
Grilles de dessin
Une grille de dessin (TDrawGrid) affiche des données quelconques dans un
format tabulaire. Ecrivez un gestionnaire d’événement OnDrawCell pour remplir
les cellules de la grille.
• La méthode CellRect renvoie les coordonnées écran de la cellule spécifiée alors
que la méthode MouseToCell renvoie la colonne et la ligne de la cellule se
trouvant aux coordonnées écran spécifiées. La propriété Selection indique les
limites de la sélection de cellules en cours.
• La propriété TopRow détermine la ligne qui apparaît en haut de la grille. La
propriété LeftCol détermine la première colonne visible sur la gauche de la
grille. VisibleColCount et VisibleRowCount indiquent, respectivement, le nombre
de colonnes et de lignes visibles dans la grille.
• Vous pouvez modifier la largeur et la hauteur d’une colonne ou d’une ligne
en utilisant les propriétés ColWidths et RowHeights. Définissez l’épaisseur des
lignes du quadrillage de la grille avec la propriété GridLineWidth. Ajoutez des
barres de défilement à la grille en utilisant la propriété ScrollBars.
• Vous pouvez spécifier les colonnes ou les lignes fixes (qui ne défilent pas) à
l’aide des propriétés FixedCols et FixedRows. Attribuez une couleur aux
colonnes et aux lignes fixes en utilisant la propriété FixedColor.
• Les propriétés Options, DefaultColWidth et DefaultRowHeight affectent également
l’aspect et le comportement de la grille.
Grilles de chaînes
Le composant grille de chaînes est un descendant de TDrawGrid spécialisé afin
de simplifier l’affichage de chaînes. La propriété Cells énumère les chaînes pour
chaque cellule de la grille ; la propriété Objects énumère les objets associés à
chaque chaîne. Il est possible d’accéder à toutes les chaînes et objets associés
d’une colonne ou d’une ligne donnée en utilisant les propriétés Cols et Rows.
9-18
Guide du développeur
Editeur de liste de valeurs (VCL seulement)
Editeur de liste de valeurs (VCL seulement)
TValueListEditor est une grille spécialisée pour la modification des listes de
chaînes contenant des paires nom/valeur sous la forme Nom=Valeur. Les noms
et les valeurs sont stockés dans un descendant de TStrings qui est la valeur de la
propriété Strings. Vous pouvez rechercher la valeur d’un nom à l’aide de la
propriété Values. TValueListEditor ne peut pas être utilisé en programmation
multiplate-forme.
La grille contient deux colonnes, une pour les noms et une pour les valeurs. Par
défaut, la colonne des noms s’appelle “Key” et la colonne des valeurs “Value”.
Vous pouvez modifier ces titres en définissant la propriété TitleCaptions. Vous
pouvez omettre ces titres en utilisant la propriété DisplayOptions (qui contrôle
également la façon dont se redimensionne le contrôle.)
Vous pouvez autoriser ou empêcher l’utilisateur de modifier la colonne des noms
en utilisant la propriété KeyOptions. KeyOptions contient des options séparées
pour autoriser la modification des noms, l’ajout de nouveaux noms, la
suppression de noms, ainsi que pour déterminer si les nouveaux noms doivent
être uniques.
Vous pouvez autoriser ou empêcher l’utilisateur de modifier les entrées de la
colonne des valeurs en utilisant la propriété ItemProps. Chaque élément a un
objet TItemProp séparé qui vous permet de :
• Fournir un masque afin d’imposer la validité de la saisie.
• Spécifier une longueur maximale pour les valeurs.
• Marquer les valeurs comme valeurs en lecture seule.
• Demander que l’éditeur de liste de valeurs affiche une flèche déroulante
ouvrant la liste des valeurs parmi lesquelles l’utilisateur pourra choisir, ou un
bouton Points de suspension déclenchant un événement que vous utiliserez
pour afficher un dialogue dans lequel l’utilisateur entrera des données.
Si vous spécifiez une flèche déroulante, vous devez fournir la liste des valeurs
parmi lesquelles l’utilisateur peut choisir. Il peut s’agir d’une liste statique (la
propriété PickList de l’objet TItemProp) ou les valeurs peuvent être ajoutées de
manière dynamique à l’exécution en utilisant l’événement OnGetPickList de
l’éditeur de liste de valeurs. Vous pouvez aussi combiner ces approches et
avoir une liste statique modifiée par le gestionnaire de l’événement
OnGetPickList.
Si vous spécifiez un bouton Points de suspension, vous devez fournir la
réponse qui est faite lorsque l’utilisateur clique sur ce bouton (y compris la
définition d’une valeur, si approprié). Vous fournirez cette réponse en écrivant
un gestionnaire pour l’événement OnEditButtonClick.
Types de contrôles
9-19
Contrôles graphiques
Contrôles graphiques
Les composants suivants facilitent l’incorporation d’éléments graphiques dans
une application.
Utilisez ce
composant :
Pour afficher :
TImage
des fichiers graphiques
TShape
des formes géométriques
TBevel
des lignes et des cadres en 3D
TPaintBox
des graphiques dessinés par l’application à l’exécution
TAnimate
des fichiers AVI (VCL seulement)
Ces classes contiennent les routines de dessin courantes (Repaint, Invalidate, etc.)
qui ne peuvent pas recevoir la focalisation.
Images
Le composant image affiche une image graphique : bitmap, icône ou métafichier.
La propriété Picture spécifie l’image à afficher. Utilisez les propriétés Center,
AutoSize, Stretch et Transparent pour spécifier les options d’affichage. Pour
davantage d’informations, voir “Présentation de la programmation relative aux
graphiques” à la page 10-1.
Formes
Le composant forme affiche une forme géométrique. C’est un contrôle non
fenêtré (dans CLX, non basé sur un widget), il ne peut donc pas recevoir la
saisie de l’utilisateur. La propriété Shape spécifie la forme du contrôle. Pour
modifier la couleur de la forme ou lui ajouter un motif, utilisez la propriété
Brush qui contient un objet TBrush. Les propriétés Color et Style de TBrush
contrôlent la manière dont la forme est dessinée.
Biseaux
Le composant biseau (TBevel) est une ligne qui peut apparaître en relief ou en
creux. Certains composants, comme TPanel, disposent de propriétés intégrées
pour créer des contours biseautés. Quand ces propriétés ne sont pas disponibles,
utilisez un composant TBevel pour créer des contours, des boîtes ou des cadres
biseautés.
9-20
Guide du développeur
Contrôles graphiques
Boîtes à peindre
Le composant boîte à peindre (TPaintBox) permet à une application de dessiner
dans une fiche. Ecrivez un gestionnaire d’événement OnPaint pour restituer
directement l’image dans le canevas (Canvas) de la boîte à peindre. Il n’est pas
possible de dessiner hors des limites d’une boîte à peindre. Pour davantage
d’informations, voir “Présentation de la programmation relative aux graphiques”
à la page 10-1.
Contrôles animation (VCL seulement)
Le composant animation est une fenêtre qui affiche silencieusement une séquence
vidéo AVI (Audio Video Interleaved). Une séquence AVI est composée d’une
série de plans bitmap, comme un film. Les séquences AVI peuvent être
sonorisées, mais les contrôles animation ne fonctionnent qu’avec les séquences
AVI silencieuses. Les fichiers utilisés doivent être des fichiers AVI non
compressés ou des séquences AVI compressées en utilisant l’algorithme RLE. Les
contrôles animation ne peuvent pas être utilisés en programmation multiplateforme.
Le composant animation comporte, entre autres, les propriétés suivantes :
• ResHandle est le handle Windows du module contenant la séquence AVI sous
la forme d’une ressource. Initialisez ResHandle à l’exécution avec le handle
d’instance ou le handle du module contenant la ressource animation. Après
avoir initialisé ResHandle, affectez la propriété ResID ou ResName pour spécifier
la ressource du module spécifié qui contient la séquence AVI à afficher dans
le contrôle animation.
• Initialisez AutoSize à true pour que le contrôle animation ajuste sa taille à la
taille des plans de la séquence AVI.
• StartFrame et StopFrame spécifient les plans où la séquence doit commencer et
s’arrêter.
• Définissez CommonAVI pour afficher l’une des séquences AVI standard de
Windows contenues dans Shell32.DLL.
• Spécifiez quand commencer ou arrêter l’animation en initialisant la propriété
Active à true et false, respectivement, et le nombre de répétitions à effectuer
en initialisant la propriété Repetitions.
• La propriété Timers permet d’afficher les plans en utilisant un timer. Cela
permet de synchroniser la séquence animation avec d’autres actions, par
exemple la restitution d’une piste sonore.
Types de contrôles
9-21
9-22
Guide du développeur
Chapitre
10
Utilisation des graphiques
et du multimédia
Chapitre 10
Les éléments graphiques et multimédia permettent d’améliorer la présentation
de vos applications. C++Builder propose divers moyens d’introduire ces
caractéristiques dans votre application. Pour ajouter des éléments graphiques,
vous pouvez insérer des images pré-dessinées à la conception, les créer en
utilisant des contrôles graphiques à la conception ou les dessiner
dynamiquement à l’exécution. Pour ajouter des fonctions multimédia, C++Builder
propose des composants spéciaux qui peuvent jouer des séquences audio
et vidéo.
CLX
Les composants multimédia sont disponibles uniquement dans la VCL.
Présentation de la programmation relative aux graphiques
Les composants graphiques de la VCL définis dans l’unité Graphics encapsulent
la GDI (Graphics Device Interface) de Windows pour faciliter l’ajout de
graphiques aux applications Windows. Les composants graphiques CLX définis
dans l’unité QGraphics encapsulent les widgets graphiques Qt pour permettre
d’ajouter des graphiques aux applications multiplates-formes.
Lorsque vous dessinez des graphiques dans une application C++Builder, vous
travaillez sur le canevas, de l’objet, plutôt que directement sur l’objet. Le mot Canvas
désigne une propriété de l’objet, mais c’est aussi un objet. Le principal avantage de
l’objet canevas est qu’il gère efficacement des ressources et prend en compte le
contexte de périphérique. Que vous dessiniez sur des bitmaps, sur l’écran, sur
l’imprimante ou sur des métafichiers (dessins dans CLX), vos programmes
peuvent utiliser les mêmes méthodes. Les canevas sont uniquement disponibles en
phase d’exécution. Tout le travail relatif aux canevas se fait donc en écrivant du
code. Les sections suivantes expliquent comment utiliser les composants
graphiques de la VCL pour simplifier les opérations de programmation.
Utilisation des graphiques et du multimédia
10-1
Présentation de la programmation relative aux graphiques
VCL
Puisque TCanvas est un gestionnaire de ressources qui enveloppe le contexte de
périphérique Windows, vous pouvez aussi utiliser toutes les fonctions GDI de
Windows sur le canevas. La propriété Handle du canevas est le handle du
contexte de périphérique.
CLX
TCanvas est un gestionnaire de ressources qui enveloppe un dispositif de dessin
Qt. La propriété Handle du canevas est un pointeur typé sur l’instance d’un objet
dispositif de dessin Qt. Le fait que ce pointeur d’instance soit exposé vous
permet d’utiliser les fonctions de bas niveau de la bibliothèque graphique Qt qui
nécessitent QPainterH.
La façon dont les images graphiques apparaissent dans votre application dépend
de la façon dont elles sont dessinées. Si vous dessinez directement dans le
canevas d’un contrôle, l’image est affichée directement. Toutefois, si vous
dessinez sur une image hors écran comme un canevas Tbitmap, l’image
n’apparaît que lorsqu’un contrôle effectue la copie d’un bitmap sur le canevas du
contrôle. Ce qui signifie que lorsqu’on dessine des bitmaps et qu’on les affecte à
un contrôle image, l’image n’apparaît que si le contrôle a la possibilité de traiter
son message (VCL) ou son événement (CLX) OnPaint.
Lorsqu’on travaille avec des graphiques, on rencontre fréquemment les termes
dessin et peinture :
• Lorsque vous dessinez, vous créez avec du code un seul élément graphique
spécifique tel qu’une ligne ou une forme. Dans votre code, vous indiquez à un
objet de dessiner un graphique particulier à un emplacement particulier sur son
canevas en appelant une méthode de dessin du canevas.
• Lorsque vous peignez, vous créez l’apparence entière d’un objet. La peinture
implique généralement le dessin. En effet, en réponse à des événements
OnPaint, un objet dessinera des graphiques. Une boîte de saisie, par exemple, se
peint elle-même en dessinant un rectangle, puis en dessinant du texte à
l’intérieur. Un contrôle forme, en revanche, se peint lui-même en dessinant un
seul graphique.
Les exemples donnés au début de ce chapitre démontrent comment dessiner divers
graphiques en réponse à des événements OnPaint. Les sections ultérieures
montrent comment faire le même type de dessin en réponse à d’autres événements.
Rafraîchissement de l’écran
A certains moments, le système d’exploitation détermine que l’apparence des
objets affichés à l’écran doit être rafraîchie. Windows génère des messages
WM_PAINT que la VCL redirige vers des gestionnaires d’événements OnPaint.
(Si vous utilisez CLX dans le cadre du développement multiplate-forme, un
événement de dessin est généré que CLX redirige vers des gestionnaires
d’événements OnPaint.) Si vous avez écrit un gestionnaire de l’événement
OnPaint pour cet objet, il est appelé lorsque vous utilisez la méthode Refresh.
Le nom généré par défaut pour le gestionnaire d’événement OnPaint dans un
fiche est FormPaint. La méthode Refresh est parfois utilisée pour rafraîchir un
composant ou une fiche. Par exemple, la méthode Refresh peut être appelée dans
10-2
Guide du développeur
Présentation de la programmation relative aux graphiques
le gestionnaire d’événement OnResize de la fiche afin de réafficher des
graphiques ou, si vous utilisez la VCL, pour dessiner un fond sur la fiche.
Bien que certains systèmes d’exploitation gèrent automatiquement l’affichage des
zones clientes d’une fenêtre qui ne sont plus valides, Windows ne le fait pas.
Pour Windows, tout dessin est considéré comme permanent. Lorsqu’une fiche ou
un contrôle est temporairement masqué, par exemple lors d’un glisser-déplacer,
la fiche ou le contrôle doivent repeindre la zone masquée lorsqu’elle ne l’est
plus. Pour plus d’informations sur le message WM_PAINT, voir l’aide en ligne
de Windows.
Lors de l’utilisation du contrôle TImage pour afficher une image graphique sur
une fiche, le dessin et le rafraîchissement du graphique contenu dans le TImage
sont gérés automtiquement. La propriété Picture spécifie le bitmap, le dessin ou
tout autre objet raphique affiché par TImage. Vous pouvez aussi définir la
propriété Proportional pour que l’image l’image soir affichée sans distorsion dans
le contrôle image. Dessiner sur un TImage crée une image persistante. Par
conséquent, il n’est pas nécessaire de redessiner l’image contenue. Au contraire,
le canevas d’un TPaintBox écrit directement sur le pilote de l’écran (VCL) ou le
dispositif de dessin (CLX), et de ce fait, tout ce qui est dessiné sur le canevas du
PaintBox est transitoire. Cela est vrai pour les contrôles similaires, y compris la
fiche elle-même. De plus, si vous dessinez ou peignez à partir du constructeur
d’un TPaintBox, vous devrez ajouter ce code dans le gestionnaire OnPaint afin
que l’image soit repeinte à chaque fois que la zone cliente est invalidée.
Types des objets graphiques
La VCL et CLX proposent les objets graphiques énumérés dans le tableau
suivant. Ces objets disposent de méthodes pour dessiner dans le canevas,
décrites dans la section “Utilisation des méthodes du canevas pour dessiner des
objets graphiques” à la page 10-10, et pour charger et enregistrer des fichiers
graphiques (voir “Chargement et enregistrement de fichiers graphiques” à la
page 10-20).
Tableau 10.1 Types d’objets graphiques
Objet
Description
Picture
Utilisé pour contenir une image graphique. Pour ajouter d’autres formats de
fichiers graphiques, utilisez la méthode Register de l’objet Picture. Elle permet
de gérer des fichiers arbitraires comme l’affichage d’images dans un contrôle
image.
Bitmap
Objet graphique utilisé pour créer des images, les manipuler (mise à l’échelle,
défilement, rotation et peinture) et les stocker sur disque sous forme de
fichiers. Il est très facile de créer la copie d’un bitmap, puisque c’est le handle
qui est copié et non l’image.
Clipboard
Représente le conteneur d’un texte ou d’un graphique qui est coupé, copié ou
collé depuis ou vers une application. Grâce au presse-papiers, vous pouvez
extraire des données en fonction d’un format donné ; comptage des références
d’handles, et l’ouverture et la fermeture du presse-papiers ; gérer et
manipuler des formats pour les objets du presse-papiers.
Utilisation des graphiques et du multimédia
10-3
Présentation de la programmation relative aux graphiques
Tableau 10.1 Types d’objets graphiques (suite)
Objet
Description
Icon
Représente la valeur chargée depuis un fichier icône (fichier .ICO).
Metafile
(VCL
seulement)
Drawing
(CLX
seulement)
Contient un fichier, qui enregistre les opérations nécessaires à la construction
d’une image, au lieu de contenir les pixels du bitmap de l’image. Les
métafichiers ou les dessins sont extrêmement réductibles sans perte de détail
de l’image et nécessitent souvent moins de mémoire que les bitmaps,
particulièrement pour les pilotes haute résolution comme les imprimantes.
Mais les métafichiers et les dessins ne sont pas aussi rapides que les bitmaps.
Utilisez les métafichiers ou les dessins lorsque vous recherchez souplesse et
précison plutôt que les performances.
Propriétés et méthodes communes du canevas
Le tableau suivant énumère les principales propriétés de l’objet canevas. Pour
une liste complète des propriétés et des méthodes, voir la rubrique traitant du
composant TCanvas dans l’aide en ligne.
Tableau 10.2 Propriétés communes de l’objet canevas
Propriété
Description
Font
Spécifie la police devant être utilisée pour écrire du texte sur l’image.
Définit les propriétés de l’objet TFont afin de spécifier le type de police, sa
couleur, sa taille, et son style.
Brush
Détermine la couleur et le modèle utilisés par le canevas pour remplir les
fonds et les formes graphiques. Définissez les propriétés de l’objet TBrush
pour spécifier la couleur et le modèle ou le bitmap à utiliser lors du
remplissage des espaces sur le canevas.
Pen
Spécifie le type de crayon utilisé par le canevas pour dessiner des lignes et
des formes. Définissez les propriétés de l’objet TPen de façon à spécifier la
couleur, le style, la largeur et le mode du crayon.
PenPos
Spécifie la position de dessin en cours du crayon.
Pixels
Spécifie la couleur des pixels à l’intérieur du ClipRect en cours.
Pour davantage d’informations sur ces propriétés, voir “Utilisation des propriétés
de l’objet canevas” à la page 10-5.
Le tableau suivant liste les différentes méthodes pouvant être utilisées :
Tableau 10.3 Méthodes communes de l’objet canevas
10-4
Méthode
Description
Arc
Dessine un arc sur l’image ainsi que le périmètre de l’ellipse délimitée par
le rectangle spécifié.
Chord
Dessine une figure fermée représentée par l’intersection d’une ligne et d’une
ellipse.
CopyRect
Copie une partie de l’image d’un autre canevas sur le canevas.
Draw
Dessine sur le canevas à l’emplacement donné par les coordonnées (X, Y)
l’objet graphique spécifié par le paramètre Graphic.
Guide du développeur
Présentation de la programmation relative aux graphiques
Tableau 10.3 Méthodes communes de l’objet canevas (suite)
Méthode
Description
Ellipse
Dessine sur le canevas l’ellipse définie par un rectangle délimité.
FillRect
Remplit sur le canevas le rectangle spécifié en utilisant le pinceau en cours.
FloodFill (VCL
seulement)
Remplit une zone du canevas en utilisant le pinceau en cours.
FrameRect
Dessine un rectangle en utilisant le pinceau du canevas pour dessiner sa
bordure.
LineTo
Dessine une ligne sur le canevas en partant de la position de PenPos au
point spécifié par X et Y, et définit la position du crayon à (X, Y).
MoveTo
Change la position de dessin en cours par le point (X,Y).
Pie
Dessine sur le canevas un secteur de l’ellipse délimitée par le rectangle (X1,
Y1) et (X2, Y2).
Polygon
Dessine sur le canevas une série de lignes connectant les points transmis et
fermant la forme par une ligne allant du dernier point au premier point.
Polyline
Dessine sur le canevas une série de lignes à la position en cours du crayon
et connectant chacun des points transmis dans Points.
Rectangle
Dessine sur le canevas un rectangle dont le coin supérieur gauche apparaît
au point (X1, Y1) et le coin inférieur droit au point (X2, Y2). Utilisez
Rectangle pour dessiner un cadre utilisant Pen et remplissez-le avec Brush.
RoundRect
Dessine sur le canevas un rectangle à coins arrondis.
StretchDraw
Dessine sur le canevas un graphique afin que l’image tienne dans le
rectangle spécifié. Le facteur d’amplification de l’image devra sans doute
être modifié pour que l’image tienne dans le rectangle.
TextHeight,
TextWidth
Renvoie respectivement la hauteur et la largeur d’une chaîne dans la police
en cours. La hauteur inclut l’intervalle entre les lignes.
TextOut
Ecrit sur le canevas une chaîne commençant au point (X,Y), puis modifie
PenPos par rapport à la fin de la chaîne.
TextRect
Ecrit une chaîne à l’intérieur d’une région ; toute partie de la chaîne se
trouvant à l’extérieur de la région ne sera pas visible.
Pour davantage d’informations sur ces méthodes, voir “Utilisation des méthodes
du canevas pour dessiner des objets graphiques” à la page 10-10.
Utilisation des propriétés de l’objet canevas
A l’objet canevas, il est possible de définir les propriétés d’un crayon afin qu’il
dessine des lignes, celles d’un pinceau pour qu’il remplisse des formes, celles
d’une fonte pour écrire du texte et celles d’un tableau de pixels pour représenter
une image.
Cette section traite des sujets suivants :
• Utilisation des crayons
• Utilisation des pinceaux
• Lecture et définition des pixels
Utilisation des graphiques et du multimédia
10-5
Présentation de la programmation relative aux graphiques
Utilisation des crayons
La propriété Pen d’un canevas contrôle la façon dont les lignes apparaissent, y
compris les lignes dessinées pour définir le pourtour d’une forme. Dessiner une
ligne droite revient à changer un groupe de pixels alignés entre deux points.
Le crayon lui-même possède quatre propriétés qu’il est possible de changer : Color,
Width, Style, et Mode.
•
•
•
•
Propriété
Propriété
Propriété
Propriété
Color: modifie la couleur du crayon
Width: modifie la largeur du crayon
Style: modifie le style du crayon
Mode: modifie le mode du crayon
Les valeurs de ces propriétés déterminent la façon dont le crayon change les pixels
de la ligne. Par défaut, chaque crayon est noir, a une largeur de 1 pixel, est de style
uni, et a un mode appelé copie qui écrase tout ce qui se trouve déjà sur le canevas.
Vous pouvez utiliser TPenRecall pour enregistrer et restaurer rapidement les
propriétés des crayons.
Changement de la couleur du crayon
La couleur du crayon est définie en mode exécution comme toute autre propriété
Color. La couleur du crayon détermine la couleur des lignes qu’il dessine : lignes,
polylignes et contour des formes, ainsi que d’autres types de lignes et polylignes.
Pour modifier la couleur du crayon, donnez une valeur à la propriété Color du
crayon.
Pour permettre à l’utilisateur de choisir une nouvelle couleur de crayon, vous
devez placer une grille de couleurs dans la barre d’outils du crayon. Une grille
de couleurs permet de spécifier une couleur de premier plan et une couleur
d’arrière-plan. Si vous n’utilisez pas de grille, vous devez penser à fournir une
couleur d’arrière-plan pour dessiner les intervalles entre les segments de lignes.
La couleur d’arrière-plan provient de la propriété Color du pinceau.
Quand l’utilisateur choisit une nouvelle couleur en cliquant dans la grille, ce code
modifie la couleur du crayon en réponse à l’événement OnClick :
void __fastcall TForm1::PenColorClick(TObject *Sender)
{
Canvas->Pen->Color = PenColor->ForegroundColor;
}
Changement de l’épaisseur du crayon
L’épaisseur du crayon détermine la taille, exprimée en pixels, de la ligne qu’il
dessine.
Remarque
N’oubliez pas que lorsque l’épaisseur est supérieure à un pixel, Windows dessine
toujours une ligne continue, sans tenir compte de la valeur de la propriété Style du
crayon.
Pour modifier l’épaisseur du crayon, affectez une valeur numérique à la propriété
Width du crayon.
10-6
Guide du développeur
Présentation de la programmation relative aux graphiques
Supposons que la barre d’outils du crayon contienne une barre de défilement
permettant de définir la largeur de celui-ci, et que vous vouliez mettre à jour le
libellé attenant à la barre de défilement pour que l’utilisateur voit ce qu’il fait.
Pour utiliser la position de la barre de défilement afin de déterminer l’épaisseur du
crayon, il est nécessaire de changer l’épaisseur du crayon chaque fois que la
position change.
Voici comment traiter l’événement OnChange de la barre de défilement :
void __fastcall TForm1::PenWidthChange(TObject *Sender)
{
Canvas->Pen->Width = PenWidth->Position;
// Définir directement l’épaisseur
// du crayon.
PenSize->Caption = IntToStr(PenWidth->Position); // convertir en chaîne
}
Changement du style du crayon
La propriété Style d’un crayon permet de créer des lignes continues, pointillées
ou à tirets.
Remarque
VCL
Pour une application multiplate-forme devant être déployée sous Windows,
Windows ne permet pas de dessiner des lignes pointillées ou à tirets lorsque le
crayon a une largeur supérieure à un pixel. Il dessine à la place une ligne
continue, quel que soit le style spécifié.
La définition des propriétés d’un crayon est une opération qui se prête
parfaitement au partage d’événements entre plusieurs contrôles. Pour déterminer
le contrôle qui reçoit l’événement, il suffit de tester le paramètre Sender.
Pour créer un gestionnaire pour l’événement clic de chacun des six boutons de
style de la barre d’outils d’un crayon, procédez comme suit :
1 Sélectionnez les six boutons de style de crayon et choisissez Inspecteur d’objets|
Evénements|événement OnClick et tapez SetPenStyle dans la colonne des
gestionnaires.
C++Builder génère un gestionnaire vide appelé SetPenStyle et l’attache à
l’événement OnClick de chacun des six boutons.
2 Remplissez le gestionnaire de l’événement Onclick en définissant le style du
crayon selon la valeur du paramètre Sender qui désigne le contrôle ayant envoyé
l’événement clic :
void __fastcall TForm1::SetPenStyle(TObject *Sender)
{
if (Sender == SolidPen)
Canvas->Pen->Style = psSolid;
else if (Sender == DashPen)
Canvas->Pen->Style = psDash;
else if (Sender == DotPen)
Canvas->Pen->Style = psDot;
else if (Sender == DashDotPen)
Canvas->Pen->Style = psDashDot;
else if (Sender == DashDotDotPen)
Canvas->Pen->Style = psDashDotDot;
Utilisation des graphiques et du multimédia
10-7
Présentation de la programmation relative aux graphiques
‘ else if (Sender == ClearPen)
Canvas->Pen->Style = psClear;
}
Ce gestionnaire d’événement peut être condensé en plaçant les constantes de
style de crayon dans les propriétés Tag des boutons de style de crayon. Dans ce
cas, le code événementiel a la forme suivante :
void __fastcall TForm1::SetPenStyle(TObject *Sender)
{
if (Sender->InheritsFrom (__classid(TSpeedButton))
Canvas->Pen->Style = (TPenStyle) ((TSpeedButton *)Sender)->Tag;
}
Changement du mode du crayon
La propriété Mode d’un crayon vous permet de spécifier les différentes façons de
combiner la couleur du crayon à celle du canevas. Par exemple, le crayon peut
toujours être noir, être de la couleur inverse à l’arrière-plan du canevas, etc. Pour
plus de détails, voir la rubrique traitant de TPen dans l’aide en ligne.
Renvoi de la position du crayon
La position de dessin en cours, position à partir de laquelle le crayon va dessiner
la prochaine ligne, est appelée la position du crayon. Le canevas stocke la
position du crayon dans sa propriété PenPos. Cette position n’affecte que le
dessin des lignes ; pour les formes et le texte, vous devez spécifier toutes les
coordonnées dont vous avez besoin.
Pour définir la position du crayon, appelez la méthode MoveTo du canevas. Par
exemple, le code suivant déplace la position du crayon sur le coin supérieur
gauche du canevas :
Canvas->MoveTo(0, 0);
Remarque
Lorsqu’une ligne est dessinée avec la méthode LineTo, la position en cours est
déplacée sur le point d’arrivée de la ligne.
Utilisation des pinceaux
La propriété Brush d’un canevas détermine la façon dont les zones sont remplies, y
compris l’intérieur des formes. Remplir une zone avec le pinceau revient à changer
d’une certaine façon un grand nombre de pixels adjacents.
Le pinceau a trois propriétés que vous pouvez manipuler :
• Propriété Color : modifie la couleur de remplissage
• Propriété Style : modifie le style du pinceau
• Propriété Bitmap : utilise un bitmap comme modèle de pinceau
Les valeurs de ces propriétés déterminent la façon dont le canevas remplit les
formes et d’autres zones. Par défaut, chaque pinceau est blanc, a un style uni et n’a
pas de motif de remplissage.
Vous pouvez utiliser TBrushRecall pour enregistrer et restaurer rapidement les
propriétés des pinceaux.
10-8
Guide du développeur
Présentation de la programmation relative aux graphiques
Changement de la couleur du pinceau
La couleur du pinceau détermine la couleur utilisée par le canevas pour remplir les
formes. Pour modifier la couleur de remplissage, affectez une valeur à la propriété
Color du pinceau. Le pinceau est utilisé pour la couleur d’arrière-plan dans le
dessin de lignes et de texte.
Il est possible de définir la couleur du pinceau de la même manière que celle du
crayon, en réponse à un clic dans la grille de couleurs présentée dans la barre
d’outils du pinceau (voir la section “Changement de la couleur du crayon” à la
page 10-6) :
void __fastcall TForm1::BrushColorClick(TObject *Sender)
{
Canvas->Brush->Color = BrushColor->BackgroundColor;
}
Changement du style du pinceau
Le style d’un pinceau détermine le motif utilisé pour remplir les formes. Il vous
permet de spécifier différentes façons de combiner la couleur du pinceau à des
couleurs déjà présentes sur le canevas. Les styles prédéfinis comprennent des
couleurs unies, pas de couleur et divers motifs de lignes et de hachurages.
Pour modifier le style d’un pinceau, définissez sa propriété Style par l’une des
valeurs prédéfinies suivantes : bsSolid, bsClear, bsHorizontal, bsVertical, bsFDiagonal,
bsBDiagonal, bsCross ou bsDiagCross.
Cet exemple définit le style du pinceau en faisant partager le même gestionnaire
d’événement OnClick aux huit boutons de style de pinceau. Tous les boutons sont
sélectionnés, OnClick est sélectionné dans Inspecteur d’objets|Evénements et le
gestionnaire OnClick porte le nom SetBrushStyle.
void __fastcall TForm1::SetBrushStyle(TObject *Sender)
{
if (Sender == SolidBrush)
Canvas->Brush->Style = bsSolid;
else if (Sender == ClearBrush)
Canvas->Brush->Style = bsClear;
else if (Sender == HorizontalBrush)
Canvas->Brush->Style = bsHorizontal;
else if (Sender == VerticalBrush)
Canvas->Brush->Style = bsVertical;
else if (Sender == FDiagonalBrush)
Canvas->Brush->Style = bsFDiagonal;
else if (Sender == BDiagonalBrush)
Canvas->Brush->Style = bsBDiagonal;
else if (Sender == CrossBrush)
Canvas->Brush->Style = bsCross;
else if (Sender == DiagCrossBrush)
Canvas->Brush->Style = bsDiagCross;
}
Le code de ce gestionnaire d’événement peut être condensé en plaçant les
constantes de style de pinceau dans les propriétés Tag des boutons de style de
pinceau.
Utilisation des graphiques et du multimédia
10-9
Présentation de la programmation relative aux graphiques
Dans ce cas, le code événementiel a la forme suivante :
void __fastcall TForm1::SetBrushStyle(TObject *Sender)
{
if (Sender->InheritsFrom (__classid(TSpeedButton))
Canvas->Brush->Style = (TBrushStyle) ((TSpeedButton *)Sender)->Tag;
}
Définition de la propriété Bitmap du pinceau
La propriété Bitmap du pinceau vous permet de spécifier une image bitmap qui
sera utilisée comme motif de remplissage des formes et des autres zones.
L’exemple suivant charge un bitmap d’un fichier et l’affecte au pinceau du
canevas de la fiche Form1 :
BrushBmp->LoadFromFile("MyBitmap.bmp");
Form1->Canvas->Brush->Bitmap = BrushBmp;
Form1->Canvas->FillRect(Rect(0,0,100,100));
Remarque
Le pinceau n’assume pas la possession d’un objet bitmap affecté à sa propriété
Bitmap. Vous devez vous assurer que l’objet Bitmap reste valide pendant la
durée de vie du pinceau, après quoi vous devez vous-même libérer l’objet
Bitmap.
Lecture et définition de pixels
Chaque canevas a une propriété Pixels indexée qui représente les points de couleur
constituant l’image sur le canevas. Vous devrez rarement accéder directement à la
propriété Pixels, sauf si vous voulez connaître ou modifier la couleur d’un pixel
particulier.
Remarque
La définition de pixels individuels prend beaucoup plus de temps que les
opérations graphiques sur des zones particulières. N’utilisez pas la propriété
tableau Pixel pour accéder aux pixels d’une image dans un tableau général. Pour
un accès performant aux pixels d’une image, voir la propriété TBitmap::ScanLine.
Utilisation des méthodes du canevas pour dessiner des objets
graphiques
Cette section montre comment utiliser certaines méthodes pour dessiner des
objets graphiques. Elle traite des sujets suivants :
•
•
•
•
Dessin
Dessin
Dessin
Dessin
de
de
de
de
lignes et de polylignes
formes
rectangles arrondis
polygones
Dessin de lignes et de polylignes
Un canevas peut dessiner des lignes droites et des polylignes (ou lignes brisées).
Une ligne droite est une ligne de pixels reliant deux points. Une polyligne est une
10-10
Guide du développeur
Présentation de la programmation relative aux graphiques
chaîne de lignes droites, reliées bout à bout. Le canevas dessine toutes les lignes en
utilisant son crayon.
Dessin de lignes
Pour dessiner une ligne droite sur un canevas, utilisez la méthode LineTo du
canevas.
La méthode LineTo dessine une ligne partant de la position en cours du crayon et
allant au point spécifié, et fait du point d’arrivée de la ligne la position en cours. Le
canevas dessine la ligne en utilisant son crayon.
Par exemple, la méthode suivante dessine des lignes diagonales qui se croisent sur
une fiche, chaque fois que la fiche est peinte :
void __fastcall TForm1::FormPaint(TObject *Sender)
{
Canvas->MoveTo(0,0);
Canvas->LineTo(ClientWidth, ClientHeight);
Canvas->MoveTo(0, ClientHeight);
Canvas->LineTo(ClientWidth, 0);
}
Dessin de polylignes
En plus des lignes individuelles, le canevas peut dessiner des polylignes, qui
sont des groupes composés d’un nombre quelconque de segments de ligne reliés
entre eux.
Pour dessiner une polyligne sur un canevas, appelez la méthode Polyline du
canevas.
Le paramètre passé à la méthode Polyline est un tableau de points. Imaginez
qu’une polyligne réalise une méthode MoveTo sur le premier point et une méthode
LineTo sur chaque point successif. Si vous voulez dessiner plusieurs lignes, vous
devez savoir que Polyline est plus rapide que la méthode MoveTo et que la
méthode LineTo, car elle élimine un certain nombre d’appels supplémentaires.
La méthode suivante, par exemple, dessine un losange dans une fiche :
void __fastcall TForm1::FormPaint(TObject *Sender)
{
TPoint vertices[5];
vertices[0] = Point(0, 0);
vertices[1] = Point(50, 0);
vertices[2] = Point(75, 50);
vertices[3] = Point(25, 50);
vertices[4] = Point(0, 0);
Canvas->Polyline(vertices, 4);
}
Remarquez que le dernier paramètre de Polyline est l’indice du dernier point et
pas le nombre de points.
Utilisation des graphiques et du multimédia
10-11
Présentation de la programmation relative aux graphiques
Dessin de formes
Les canevas disposent de méthodes vous permettant de dessiner différents types
de formes. Le canevas dessine le pourtour d’une forme avec son crayon, puis
remplit l’intérieur avec son pinceau. La ligne qui définit la bordure de la forme
est déterminée par l’objet Pen en cours.
Cette section couvre :
• Dessin de rectangles et d’ellipses
• Dessin de rectangles à coins arrondis
• Dessin de polygones
Dessin de rectangles et d’ellipses
Pour dessiner un rectangle ou une ellipse sur un canevas, appelez la méthode
Rectangle ou la méthode Ellipse du canevas, en transmettant les coordonnées des
limites d’un rectangle.
La méthode Rectangle dessine le rectangle ; Ellipse dessine une ellipse qui touche
tous les côtés du rectangle.
La méthode suivante dessine un rectangle remplissant le quart supérieur gauche
d’une fiche, puis dessine une ellipse sur la même zone :
void __fastcall TForm1::FormPaint(TObject *Sender)
{
Canvas->Rectangle(0, 0, ClientWidth/2, ClientHeight/2);
Canvas->Ellipse(0, 0, ClientWidth/2, ClientHeight/2);
}
Dessin de rectangles à coins arrondis
Pour dessiner un rectangle à coins arrondis sur un canevas, appelez la méthode
RoundRect du canevas.
Les quatre premiers paramètres transmis à RoundRect sont les limites d’un
rectangle, comme pour la méthode Rectangle ou la méthode Ellipse. RoundRect
prend deux paramètres supplémentaires qui indiquent comment dessiner les coins
arrondis.
La méthode suivante, par exemple, dessine un rectangle à coins arrondis dans le
quart supérieur de la fiche, en arrondissant les coins en arcs d’un cercle de
10 pixels de diamètre :
void __fastcall TForm1::FormPaint(TObject *Sender)
{
Canvas->RoundRect(0, 0, ClientWidth/2, ClientHeight/2, 10, 10);
}
Dessin de polygones
Pour dessiner, sur un canevas, un polygone ayant un nombre quelconque de côtés,
appelez la méthode Polygon du canevas.
Polygon prend un tableau de points comme seul paramètre et relie les points avec le
crayon, puis relie le dernier point au premier de façon à fermer le polygone. Après
10-12
Guide du développeur
Présentation de la programmation relative aux graphiques
avoir dessiné les lignes, Polygon utilise le pinceau pour remplir la zone interne au
polygone.
Le code suivant dessine un triangle rectangle dans la moitié inférieure gauche de la
fiche :
void __fastcall TForm1::FormPaint(TObject *Sender)
{
TPoint vertices[3];
vertices[0] = Point(0, 0);
vertices[1] = Point(0, ClientHeight);
vertices[2] = Point(ClientWidth,ClientHeight);
Canvas->Polygon(vertices,2);
}
Gestion de plusieurs objets de dessin dans votre application
Différentes méthodes de dessin (rectangle, forme, ligne, etc.) sont typiquement
disponibles sur la barre d’outils et le volet de boutons. Les applications peuvent
répondre à des clics sur des turboboutons de façon à définir les objets de dessin
voulu. Cette section décrit comment :
• Faire le suivi de l’outil de dessin à utiliser
• Changer d’outil de dessin en utilisant des turboboutons
• Utiliser des outils de dessin
Faire le suivi de l’outil de dessin à utiliser
Une application graphique doit pouvoir connaître à tout moment le type d’outil de
dessin (une ligne, un rectangle, une ellipse ou un rectangle arrondi, par exemple)
que l’utilisateur veut utiliser. En général, vous utiliserez le type énuméré C++
pour lister les outils disponibles. Comme un type énuméré est également une
déclaration de type, vous pouvez utiliser la vérification de type C++ pour
vérifier que vous n’affectez que ces valeurs spécifiques.
Par exemple, le code suivant déclare un type énuméré pour tous les outils de
dessin de l’application graphique :
typedef enum {dtLine, dtRectangle, dtEllipse, dtRoundRect} TDrawingTool;
Il n’est possible d’affecter à une variable de type TDrawingTool que l’une des
constantes dtLine, dtRectangle, dtEllipse ou dtRoundRect.
Par convention, les identificateurs de type commencent par la lettre T, et les
constantes similaires (celles constituant le type énuméré) commencent par un même
préfixe de deux caractères (comme ici dt pour “drawing tool”).
Dans le code suivant, un champ ajouté à une fiche fera le suivi de l’outil de
dessin de la fiche :
enum TDrawingTool {dtLine, dtRectangle, dtEllipse, dtRoundRect};
class TForm1 : public TForm
{
__published: // Composants gérés par l’EDI
Utilisation des graphiques et du multimédia
10-13
Présentation de la programmation relative aux graphiques
void __fastcall FormMouseDown(TObject *Sender, TMouseButton Button,
TShiftState Shift, int X, int Y);
void __fastcall FormMouseMove(TObject *Sender, TShiftState Shift, int X,
int Y);
void __fastcall FormMouseUp(TObject *Sender, TMouseButton Button,
TShiftState Shift, int X, int Y);
private: // Déclarations de l’utilisateur
public: // Déclarations de l’utilisateur
__fastcall TForm1(TComponent* Owner);
//champ stockant si le bouton a été appuyé
bool Drawing;
TPoint Origin, MovePt;
// champs pour le stockage des points
TDrawingTool DrawingTool; // champ stockant l’outil actif
};
Changement d’outil en utilisant un turbobouton
Chaque outil de dessin de votre application doit avoir un gestionnaire pour son
événement OnClick. Supposons que votre application ait une barre d’outils
comportant un bouton pour chacun des quatre outils de dessin : ligne, rectangle,
ellipse et rectangle arrondi. Vous attacherez les gestionnaires suivants aux
événements OnClick des quatre boutons, en affectant à DrawingTool la valeur
correspondant à chaque outil :
void __fastcall TForm1::LineButtonClick(TObject *Sender) // LineButton
{
DrawingTool = dtLine;
}
void __fastcall TForm1::RectangleButtonClick(TObject *Sender) // RectangleButton
{
DrawingTool = dtRectangle;
}
void __fastcall TForm1::EllipseButtonClick(TObject *Sender) // EllipseButton
{
DrawingTool = dtEllipse;
}
void __fastcall TForm1::RoundedRectButtonClick(TObject *Sender) // RoundRectBtn
{
DrawingTool = dtRoundRect;
}
Utilisation des outils de dessin
Vous savez maintenant spécifier l’outil à utiliser. Il vous reste à indiquer
comment dessiner les différentes formes. Les seules méthodes réalisant des
dessins sont les gestionnaires de souris (déplacement de souris et relâchement de
bouton de souris), et le seul code de dessin dessine des lignes, quel que soit
l’outil sélectionné.
Pour utiliser les différents outils de dessin, votre code doit spécifier comment
dessiner selon l’outil sélectionné. Vous devez ajouter l’instruction au gestionnaire
d’événement de chaque outil.
10-14
Guide du développeur
Présentation de la programmation relative aux graphiques
Cette section explique comment :
• dessiner des formes
• partager du code entre les gestionnaires d’événements
Dessiner des formes
Dessiner des formes est aussi simple que dessiner des lignes. Une seule instruction
suffit. Vous n’avez besoin que des coordonnées.
Voici réécrit le gestionnaire de l’événement OnMouseUp qui dessine des formes
pour les quatre outils :
void __fastcall TForm1::FormMouseUp(TObject *Sender, TMouseButton Button,
TShiftState Shift, int X, int Y){
switch (DrawingTool)
{
case dtLine:
Canvas->MoveTo(Origin.x, Origin.y);
Canvas->LineTo(X, Y);
break;
case dtRectangle:
Canvas->Rectangle(Origin.x, Origin.y, X, Y);
break;
case dtEllipse:
Canvas->Ellipse(Origin.x, Origin.y, X, Y);
break;
case dtRoundRect:
Canvas->Rectangle(Origin.x, Origin.y, X, Y, (Origin.x - X)/2,
(Origin.y - Y)/2);
break;
}
Drawing = false;
}
Il est également nécessaire de modifier le gestionnaire de OnMouseMove pour
dessiner des formes :
void __fastcall TForm1::FormMouseMove(TObject *Sender, TMouseButton Button,
TShiftState Shift, int X, int Y)
{
if (Drawing)
{
Canvas->Pen->Mode = pmNotXor;
// Utiliser le mode XOR pour dessiner/effacer
switch (DrawingTool)
{
case dtLine:
Canvas->MoveTo(Origin.x, Origin.y);
Canvas->LineTo(MovePt.x, MovePt.y);
Canvas->MoveTo(Origin.x, Origin.y);
Canvas->LineTo(X, Y);
break;
case dtRectangle:
Canvas->Rectangle(Origin.x, Origin.y, MovePt.x, MovePt.y);
Canvas->Rectangle(Origin.x, Origin.y, X, Y);
break;
Utilisation des graphiques et du multimédia
10-15
Présentation de la programmation relative aux graphiques
case dtEllipse:
Canvas->Ellipse(Origin.x, Origin.y, MovePt.x, MovePt.y);
Canvas->Ellipse(Origin.x, Origin.y, X, Y);
break;
case dtRoundRect:
Canvas->Rectangle(Origin.x, Origin.y, MovePt.x, MovePt.y,
(Origin.x - MovePt.x)/2,(Origin.y - MovePt.y)/2);
Canvas->Rectangle(Origin.x, Origin.y, X, Y,
(Origin.x - X)/2, (Origin.y - Y)/2);
break;
}
MovePt = Point(X, Y);
}
Canvas->Pen->Mode = pmCopy;
}
En principe, tout le code répétitif de l’exemple précédent devrait être dans une
routine séparée. La section suivante présente le code relatif au dessin des formes
dans une seule routine pouvant être appelée par tous les gestionnaires
d’événements de souris.
Partage de code entre plusieurs gestionnaires d’événements
Chaque fois que plusieurs gestionnaires d’événements utilisent le même code, vous
rendez l’application plus efficace en plaçant le code répété dans une méthode
partagée par les gestionnaires d’événements.
Pour ajouter une méthode à une fiche,
1 Ajoutez la déclaration de la méthode à l’objet fiche.
Il est possible d’ajouter la déclaration dans les sections public ou private, à la
fin des déclarations de l’objet fiche. Si le code partage uniquement les détails
de la manipulation de certains événements, il est préférable de créer une
méthode partagée private.
2 Ecrivez l’implémentation de la méthode dans le fichier .cpp de l’unité de la
fiche.
L’en-tête de l’implémentation de la méthode doit correspondre exactement à la
déclaration, les mêmes paramètres apparaissant dans le même ordre.
Le code suivant ajoute à la fiche une méthode appelée DrawShape et l’appelle
depuis chacun des gestionnaires. D’abord, la déclaration de DrawShape est
ajoutée à la déclaration de l’objet fiche :
enum TDrawingTool {dtLine, dtRectangle, dtEllipse, dtRoundRect};
class TForm1 : public TForm
{
__published: // Composants gérés par l’EDI
void __fastcall FormMouseDown(TObject *Sender, TMouseButton Button,
TShiftState Shift, int X, int Y);
void __fastcall FormMouseMove(TObject *Sender, TShiftState Shift, int X,
int Y);
void __fastcall FormMouseUp(TObject *Sender, TMouseButton Button,
TShiftState Shift, int X, int Y);
10-16
Guide du développeur
Présentation de la programmation relative aux graphiques
private:// Déclarations de l’utilisateur
void __fastcall DrawShape(TPoint TopLeft, TPoint BottomRight, TPenMode AMode);
public:// Déclarations de l’utilisateur
__fastcall TForm1(TComponent* Owner);
bool Drawing; //champ stockant si le bouton a été appuyé
TPoint Origin, MovePt; // champs pour le stockage des points
TDrawingTool DrawingTool; // champ stockant l’outil actif
};
Ensuite l’implémentation de DrawShape est écrite dans le fichier .cpp de l’unité :
void __fastcall TForm1::DrawShape(TPoint TopLeft, TPoint BottomRight,
TPenMode AMode)
{
Canvas->Pen->Mode = AMode;
switch (DrawingTool)
{
case dtLine:
Canvas->MoveTo(TopLeft.x, TopLeft.y);
Canvas->LineTo(BottomRight.x, BottomRight.y);
break;
case dtRectangle:
Canvas->Rectangle(TopLeft.x, TopLeft.y, BottomRight.x, BottomRight.y);
break;
case dtEllipse:
Canvas->Ellipse(TopLeft.x, TopLeft.y, BottomRight.x, BottomRight.y);
break;
case dtRoundRect:
Canvas->Rectangle(TopLeft.x, TopLeft.y, BottomRight.x, BottomRight.y,
(TopLeft.x - BottomRight.x)/2,(TopLeft.y - BottomRight.y)/2);
break;
}
}
Les autres gestionnaires d’événements sont modifiés pour appeler DrawShape.
void __fastcall TForm1::FormMouseUp(TObject *Sender)
{
DrawShape(Origin, Point(X,Y), pmCopy); // dessine la forme finale
Drawing = false;
}
void __fastcall TForm1::FormMouseMove(TObject *Sender, TMouseButton Button,
TShiftState Shift, int X, int Y)
{
if (Drawing)
{
DrawShape(Origin, MovePt, pmNotXor); // efface la forme précédente
MovePt = Point(X, Y);
DrawShape(Origin, MovePt, pmNotXor); // dessine la forme en cours
}
}
Utilisation des graphiques et du multimédia
10-17
Présentation de la programmation relative aux graphiques
Dessiner sur un graphique
Vous n’avez pas besoin de composant pour manipuler les objets graphiques de
votre application. Vous pouvez construire des objets graphiques, dessiner sur
eux, les sauvegarder et les détruire sans même dessiner sur l’écran. En fait, il est
rare qu’une application dessine directement sur une fiche. Le plus souvent, une
application doit dessiner sur un graphique. Elle utilise ensuite un composant
contrôle image pour afficher le graphique sur une fiche.
Une fois les opérations de dessin de l’application reportées sur le graphique du
contrôle image, il est facile d’y ajouter les fonctionnalités relatives à l’impression,
aux opérations sur le presse-papiers, à l’ouverture et à l’enregistrement des objets
graphiques. Les objets graphiques peuvent être des fichiers bitmap, des dessins,
des icônes ou toute autre classe graphique installée en tant que graphique jpeg.
Remarque
Etant donné que vous dessinez sur une image hors écran, comme un canevas
Tbitmap, l’image n’apparaît pas tant qu’un contrôle effectue la copie d’un bitmap
sur le canevas du contrôle. En d’autres mots, lorsque vous dessinez des bitmaps
et les affectez à un contrôle image, l’image n’apparaît que si le contrôle a la
possibilité de traiter son message. En revanche, si vous dessinez directement sur
la propriété Canvas d’un contrôle, l’objet image apparaît immédiatement.
Création de graphiques défilables
Le graphique ne doit pas être de la même taille que la fiche : il doit être soit
plus petit, soit plus grand. En ajoutant un contrôle boîte de défilement sur la
fiche et en plaçant une image graphique à l’intérieur, vous pouvez afficher des
graphiques beaucoup plus grands que la fiche et même plus grands que l’écran.
Pour ajouter un graphique défilable, vous devez commencer par ajouter un
composant TScrollbox puis ajouter ensuite le contrôle image.
Ajout d’un contrôle image
Un contrôle image est un composant conteneur qui vous permet d’afficher vos
objets bitmap. Un contrôle image peut être utilisé pour contenir un bitmap qui
n’est pas nécessairement affiché en permanence, ou un bitmap dont l’application a
besoin pour générer d’autres images.
Remarque
Pour des informations sur l’utilisation des graphiques dans des contrôles, voir
“Ajout de graphiques à des contrôles” à la page 6-12.
Positionnement du contrôle
Un contrôle image peut être placé n’importe où dans une fiche. Pour tirer le
meilleur parti de la capacité d’un contrôle image à ajuster sa taille sur celle de son
image, le seul point à définir est le coin supérieur gauche du contrôle. Si le contrôle
image sert d’emplacement non visible pour un bitmap, il peut être placé n’importe
où dans la fiche, comme un composant non visuel.
Si vous placez le contrôle image en le mettant à l’intérieur de la boîte de
défilement déjà installée dans la zone client de la fiche, vous serez sûr que la boîte
de défilement affichera des barres de défilement pour permettre l’accès aux parties
10-18
Guide du développeur
Présentation de la programmation relative aux graphiques
du dessin n’apparaissant pas à l’écran. Définissez ensuite les propriétés du contrôle
image.
Définition de la taille initiale du bitmap
Lorsqu’un contrôle image est ajouté, il n’existe qu’en tant que conteneur. La
propriété Picture du contrôle image peut être définie en mode conception de
façon à contenir un graphique statique. Mais, le contrôle peut également charger
l’image depuis un fichier pendant l’exécution, comme décrit dans la section
“Chargement et enregistrement de fichiers graphiques” à la page 10-20.
Pour créer un bitmap vide au démarrage de l’application,
1 Attachez un gestionnaire à l’événement OnCreate de la fiche contenant l’image.
2 Créez un objet bitmap, et affectez-le à la propriété Picture->Graphic du contrôle
image.
Dans cet exemple, l’image est dans Form1, la fiche principale de l’application. Le
code attache donc un gestionnaire à l’événement OnCreate de Form1 :
void __fastcall TForm1::FormCreate(TObject *Sender)
{
Graphics::TBitmap *Bitmap = new Graphics::TBitmap(); // créer l’objet bitmap
Bitmap->Width = 200;
// définir la largeur initiale et ...
Bitmap->Height = 200;
// ...la hauteur initiale
Image->Picture->Graphic = Bitmap; // affecter le bitmap au contrôle image
// libérer l’objet bitmap
delete Bitmap;
}
L’affectation du bitmap à la propriété Graphic de l’image copie le bitmap dans
l’objet Picture. Mais, l’objet Picture ne devient pas propriétaire du bitmap, c’est
pourquoi, après avoir effectué cette affectation, vous devez le libérer.
Si vous exécutez l’application maintenant, la zone client de la fiche apparaît
comme une zone blanche représentant le bitmap. Si vous redimensionnez la
fenêtre de sorte que la zone client ne puisse afficher toute l’image, la boîte de
défilement affiche automatiquement des barres de défilement pour permettre la
visualisation du reste de l’image. Mais si vous essayez de dessiner dans l’image,
rien n’apparaît : l’application dessine toujours dans la fiche qui est derrière
l’image et la boîte de défilement.
Dessiner sur un bitmap
Pour dessiner sur un bitmap, utilisez le canevas du contrôle image et attachez les
gestionnaires d’événements de souris aux événements appropriés du contrôle
image. Typiquement, vous devriez utiliser des opérations sur des régions
(rectangles, polylignes et ainsi de suite). Ce sont des méthodes rapides et
efficaces pour dessiner.
Un moyen efficace de dessiner des images lorsque vous avez besoin d’accéder de
manière individuelle aux pixels est d’utiliser la propriété ScanLine du bitmap.
Pour une utilisation plus générale, vous pouvez définir un format de pixels de
24 bits et traiter le pointeur renvoyé par ScanLine comme un tableau de couleurs
RVB. Vous devrez sinon connaître le format natif de la propriété ScanLine.
Utilisation des graphiques et du multimédia
10-19
Présentation de la programmation relative aux graphiques
Cet exemple explique comment utiliser ScanLine pour extraire des pixels ligne
par ligne.
void __fastcall TForm1::Button1Click(TObject *Sender)
{
Graphics::TBitmap *pBitmap = new Graphics::TBitmap();
// Cet exemple montre comment dessiner directement dans le Bitmap
Byte *ptr;
try
{
pBitmap->LoadFromFile("C:\\Program Files\\Borland\\CBuilder\\Images\\Splash\\256color\\
factory.bmp ");
for (int y = 0; y < pBitmap->Height; y++)
{
ptr = pBitmap->ScanLine[y];
for (int x = 0; x < pBitmap->Width; x++)
ptr[x] = (Byte)y;
}
Canvas->Draw(0,0,pBitmap);
}
catch (...)
{
ShowMessage("Impossible de charger ou de modifier le bitmap");
}
delete pBitmap;
}
CLX
Pour les applications multiplates-formes, changez le code spécifique à Windows
et à la VCL pour que votre application s’exécute sous Linux. Par exemple, les
chemins d’accès dans Linux utilise le caractère “/” comme délimiteur. Pour
davantage d’informations sur la CLX et les applications multiplates-formes, voir
chapitre 14, “Développement d’applications multiplates-formes”.
Chargement et enregistrement de fichiers graphiques
Des images graphiques n’existant que pour la durée de l’exécution d’une
application sont d’un intérêt limité. Le plus souvent, la même image est utilisée à
chaque exécution, ou bien l’image créée est enregistrée pour une utilisation
ultérieure. Le composant image facilite le chargement d’une image depuis un
fichier et son enregistrement.
Les composants que vous utilisez pour charger, sauvegarder et remplacer des
images graphiques supportent la plupart des formats de graphiques dont les
fichiers bitmap, les métafichiers, les glyphes, et ainsi de suite. Ils supportent
aussi les classes graphiques installables.
Le mécanisme d’ouverture et d’enregistrement des fichiers graphiques est
semblable à celui utilisé pour les autres fichiers et est décrit dans les sections
suivantes :
• Charger une image depuis un fichier
• Enregistrer une image dans un fichier
• Remplacer l’image
10-20
Guide du développeur
Présentation de la programmation relative aux graphiques
Chargement d’une image depuis un fichier
Votre application doit fournir la possibilité de charger une image depuis un
fichier si votre application a besoin de modifier l’image ou si vous voulez la
stocker à l’extérieur de l’application afin qu’un autre utilisateur ou une autre
application puisse la modifier.
Pour charger un fichier graphique dans un contrôle image, appelez la méthode
LoadFromFile de l’objet Picture du contrôle image.
Le code suivant extrait un nom de fichier dans une boîte de dialogue
d’ouverture de fichiers graphiques, et charge ensuite ce fichier dans un contrôle
image nommé Image :
void __fastcall TForm1::Open1Click(TObject *Sender)
{
if (OpenPictureDialog1->Execute())
{
CurrentFile = OpenPictureDialog1->FileName;
Image->Picture->LoadFromFile(CurrentFile);
}
}
Enregistrement d’une image dans un fichier
L’objet Picture peut charger et enregistrer des graphiques sous divers formats. Vous
pouvez créer et recenser vos propres formats de fichiers graphiques afin que les
objets image puissent également les enregistrer et les stocker.
Pour enregistrer le contenu d’un contrôle image dans un fichier, appelez la
méthode SaveToFile de l’objet Picture du contrôle image.
La méthode SaveToFile nécessite de spécifier le nom du fichier de sauvegarde.
L’image est nouvellement créée et n’a pas encore de nom de fichier, ou bien l’image
existe déjà mais l’utilisateur veut l’enregistrer dans un fichier différent. Dans l’un ou
l’autre cas, l’application doit demander à l’utilisateur un nom de fichier avant
l’enregistrement, comme le montre la section suivante.
Les deux gestionnaires d’événements suivants, attachés respectivement aux
éléments de menu Fichier|Enregistrer et Fichier|Enregistrer sous, gèrent
l’enregistrement des fichiers ayant déjà un nom, l’enregistrement des fichiers
n’ayant pas de nom et l’enregistrement des fichiers sous un nouveau nom.
void __fastcall TForm1::Save1Click(TObject *Sender)
{
if (!CurrentFile.IsEmpty())
Image->Picture->SaveToFile(CurrentFile); // Enregistrer s’il a déjà un nom
// Sinon obtenir un nom
else SaveAs1Click(Sender);
}
void __fastcall TForm1::SaveAs1Click(TObject
{
//
if (SaveDialog1->Execute())
{
CurrentFile = SaveDialog1->FileName; //
//
*Sender)
Obtenir un nom de fichier
Enregistrer sous le nom spécifié par
l’utilisateur
Utilisation des graphiques et du multimédia
10-21
Présentation de la programmation relative aux graphiques
Save1Click(Sender);
// Puis enregistrer normalement
}
}
Remplacement de l’image
Il est possible à tout moment de remplacer l’image d’un contrôle image. Si un
nouveau graphique est affecté à l’image ayant déjà un graphique, le nouveau
graphique remplace l’ancien.
Pour remplacer l’image contenue dans un contrôle image, affectez un nouveau
graphique à l’objet Picture du contrôle image.
La création d’un nouveau graphique passe par le même processus que la création
du premier graphique (voir la section “Définition de la taille initiale du bitmap” à
la page 10-19), mais il faut également permettre à l’utilisateur de choisir une taille
différente de celle utilisée par défaut pour le graphique initial. Un moyen simple de
proposer une telle option est de présenter une boîte de dialogue comme celle de la
figure suivante.
Figure 10.1 Boîte de dialogue Dimension bitmap de l’unité BMPDlg
WidthEdit
HeightEdit
Cette boîte de dialogue particulière est créée dans l’unité BMPDlg incluse dans le
projet GraphEx (répertoire EXAMPLES\DOC\GRAPHEX).
Cette boîte de dialogue étant dans votre projet, ajoutez une instruction include
pour BMPDlg.hpp dans le fichier .cpp de votre fiche principale. Vous pouvez
ensuite attacher un gestionnaire à l’événement OnClick de l’élément de menu
Fichier|Nouveau. Voici un exemple :
void __fastcall TForm1::New1Click(TObject *Sender)
{
Graphics::TBitmap *Bitmap;
// vérifier que la focalisation se trouve dans la largeur de champ
NewBMPForm->ActiveControl = NewBMPForm->WidthEdit;
// initialiser les dimensions en cours comme défaut ...
NewBMPForm->WidthEdit->Text = IntToStr(Image->Picture->Graphic->Width);
NewBMPForm->HeightEdit->Text = IntToStr(Image->Picture->Graphic->Height);
// Si l’utilisateur n’annule pas ...
if (NewBMPForm->ShowModal() != IDCANCEL){
Bitmap = new Graphics::TBitmap();
// créer un nouvel objet bitmap
// utiliser les dimensions spécifiées
Bitmap->Width = StrToInt(NewBMPForm->WidthEdit->Text);
Bitmap->Height = StrToInt(NewBMPForm->HeightEdit->Text);
Image->Picture->Graphic = Bitmap; // remplacer le graphique par le nouveau bitmap
CurrentFile = EmptyStr;
//Indiquer un fichier sans nom
delete Bitmap;
}
}
10-22
Guide du développeur
Présentation de la programmation relative aux graphiques
Remarque
L’affectation d’un nouveau bitmap à la propriété Graphic de l’objet Picture oblige
l’objet Picture à copier le nouveau graphique, mais il ne devient pas son
propriétaire. L’objet Picture maintient son propre objet graphique interne. A
cause de cela, le code précédent libère l’objet bitmap après l’affectation.
Utilisation du presse-papiers avec les graphiques
Vous pouvez utiliser le presse-papiers de Windows pour copier et coller des
graphiques dans les applications ou pour échanger des graphiques avec d’autres
applications. L’objet Clipboard de la VCL facilite la gestion de différents types
d’informations, y compris les graphiques.
Avant d’utiliser l’objet Clipboard dans une application, il faut ajouter une instruction
include pour Clipbrd.hpp dans chaque fichier .cpp devant accéder aux données
du presse-papiers.
Pour les applications multiplates-formes, les données, qui sont stockées dans le
presse-papiers lorsque vous utilisez CLX, sont stockées en tant que type MIME
en association avec un objet TStream. CLX fournit des constantes prédéfinies
pour les types MIME suivant.
Tableau 10.4 Types MIME et constantes
Type MIME
Constante CLX
‘image/delphi.bitmap’
SDelphiBitmap
‘image/delphi.component’
SDelphiComponent
‘image/delphi.picture’
SDelphiPicture
‘image/delphi.drawing’
SDelphiDrawing
Copier des graphiques dans le presse-papiers
Toute image graphique, y compris le contenu d’un contrôle image, peut être copiée
dans le presse-papiers. Une fois placée dans le presse-papiers, l’image est disponible
pour toutes les applications.
Pour copier une image dans le presse-papiers, affectez l’image à l’objet Clipboard
en utilisant la méthode Assign.
Le code suivant montre comment copier dans le presse-papiers l’image d’un
contrôle image nommé Image en réponse au choix d’un élément de menu
Edition|Copier :
void __fastcall TForm1::Copy1Click(TObject *Sender)
{
Clipboard()->Assign(Image->Picture);
}
Couper des graphiques dans le presse-papiers
L’opération qui consiste à couper un graphique dans le presse-papiers est semblable
à la copie, sauf que le graphique est supprimé de la source.
Utilisation des graphiques et du multimédia
10-23
Présentation de la programmation relative aux graphiques
Pour couper un graphique dans le presse-papiers, commencez par le copier dans le
presse-papiers, puis supprimez l’original.
Lorsque vous coupez un graphique, la seule question est de savoir comment
montrer que l’image originale a été effacée. La solution classique consiste à mettre
la région à blanc, comme dans le code suivant qui attache un gestionnaire à
l’événement OnClick d’un élément de menu Edition|Couper :
void __fastcall TForm1::Cut1Click(TObject *Sender)
{
TRect ARect;
Copy1Click(Sender);
// copier l’image dans le presse-papiers
Image->Canvas->CopyMode = cmWhiteness; // copier tout en blanc
ARect = Rect(0, 0, Image->Width, Image->Height); // obtenir les dimensions de l’image
Image->Canvas->CopyRect(ARect, Image->Canvas, ARect); // copier le bitmap sur lui-même
Image->Canvas->CopyMode = cmSrcCopy; // rétablir le mode par défaut
}
Coller des graphiques depuis le presse-papiers
Si le presse-papiers contient un graphique bitmap, il est possible de le coller dans
tout objet image, y compris les contrôles image et la surface d’une fiche.
Pour coller un graphique depuis le presse-papiers,
1 Appelez la méthode HasFormat (si vous utilisez la VCL) ou la méthode
Provides (si vous utilisez CLX) du presse-papiers pour savoir s’il contient un
graphique.
HasFormat (ou Provides dans CLX) est une fonction booléenne. Elle renvoie true
si le presse-papiers contient un élément du type spécifié par le paramètre. Pour
tester la présence d’un graphique, passez le paramètre CF_BITMAP sur la
plate-forme Windows. Passez SDelphiBitmap dans les applications multiplatesformes.
2 Affectez le presse-papiers à la destination.
VCL
Ce code VCL montre comment coller une image depuis le presse-papiers dans
un contrôle image en réponse à un clic sur un élément de menu Edition|Coller :
void __fastcall TForm1::Paste1Click(TObject *Sender)
{
Graphics::TBitmap *Bitmap;
if (Clipboard()->HasFormat(CF_BITMAP)){
Image1->Picture->Bitmap->Assign(Clipboard());
}
}
CLX
Le même exemple dans CLX pour le développement multiplate-forme doit
ressembler à ceci :
void __fastcall TForm1::Paste1Click(TObject *Sender)
{
QGraphics::TBitmap *Bitmap;
if (Clipboard()->Provides(SDelphiBitmap)){
Image1->Picture->Bitmap->Assign(Clipboard());
}
}
10-24
Guide du développeur
Présentation de la programmation relative aux graphiques
Le graphique du presse-papiers peut provenir de cette application ou y avoir été
copié par une autre application, comme Microsoft Paint. Dans ce cas, il n’est pas
nécessaire de vérifier le format du presse-papiers, car le menu Coller serait
indisponible si le presse-papiers contenait un format non supporté.
Techniques de dessin dans une application
Cet exemple explique les détails relatifs à l’implémentation de l’effet “rubber
banding” dans une application graphique qui suit les mouvements de la souris
au fur et à mesure que l’utilisateur dessine un graphique en mode exécution. Le
code qui suit provient d’une application exemple située dans le répertoire
Examples\Doc\GraphEx. Cette application dessine des lignes et des formes sur
le canevas d’une fenêtre en réponse à des cliquer-glisser : l’appui sur le bouton
de la souris commence le dessin, le relâchement du bouton termine le dessin.
Pour commencer, cet exemple de code montre comment dessiner sur la surface
d’une fiche principale. Les exemples ultérieurs expliquent comment dessiner sur
un bitmap.
Les rubriques suivantes décrivent l’exemple :
• Comment répondre à la souris
• Ajout d’un champ à un objet fiche pour faire le suivi des actions de la souris
• Amélioration du dessin de ligne
Répondre à la souris
Votre application peut répondre aux actions de la souris : enfoncement du
bouton de la souris, déplacement de la souris et relâchement du bouton de la
souris. Elle peut aussi répondre à un clic (un appui suivi d’un relâchement, sans
déplacer la souris) qui peut être généré par certaines frappes de touches (comme
l’appui sur la touche Entrée dans une boîte de dialogue modale).
Cette section traite des sujets suivants :
•
•
•
•
Qu’y a-t’il dans un événement de souris ?
Réponse à l’action bouton de souris enfoncé
Réponse à l’action bouton de souris relâché
Réponse au déplacement de la souris
Qu’y a-t’il dans un événement de souris ?
C++Builder possède trois événements de souris : l’événement OnMouseDown,
l’événement OnMouseMove et l’événement OnMouseUp.
Lorsqu’une application détecte une action de la souris, elle appelle le gestionnaire
que vous avez défini pour l’événement correspondant, en lui transmettant cinq
paramètres. Les informations contenues dans ces paramètres permettent de
personnaliser la réponse aux événements.
Utilisation des graphiques et du multimédia
10-25
Présentation de la programmation relative aux graphiques
Il s’agit des paramètres suivants :
Tableau 10.5 Paramètres des événements de souris
Paramètre
Signification
Sender
L’objet ayant détecté l’action de souris.
Button
Indique le bouton de la souris impliqué : mbLeft, mbMiddle ou mbRight.
Shift
Indique l’état des touches Alt, Ctrl et Maj au moment de l’action de souris.
X, Y
Les coordonnées de l’endroit où l’événement a eu lieu.
La plupart du temps, les informations essentielles pour le gestionnaire d’un
événement souris sont les coordonnées mais, dans certains cas, il est utile de tester
le paramètre Button pour déterminer quel bouton de la souris a provoqué
l’événement.
Remarque
C++Builder utilise le même critère que Microsoft Windows pour déterminer le
bouton enfoncé. Aussi, si vous avez interverti les boutons “primaire” et
“secondaire” par défaut de la souris (pour que le bouton droit de la souris soit le
bouton primaire), un clic du bouton primaire (droit) entraînera une valeur mbLeft
pour le paramètre Button.
Réponse à l’action bouton de souris enfoncé
Lorsque l’utilisateur appuie sur un bouton de la souris, un événement
OnMouseDown est adressé à l’objet situé en dessous du pointeur de la souris.
L’objet peut alors répondre à l’événement.
Pour répondre à une action bouton de souris enfoncé, attachez un gestionnaire à
l’événement OnMouseDown.
C++Builder génère un gestionnaire vide pour l’événement souris enfoncée se
produisant sur la fiche :
void __fastcall TForm1::FormMouseDown(TObject *Sender, TMouseButton Button,
TShiftState Shift, int X, int Y)
{
}
Exemple : réponse à l’action bouton de souris enfoncé
Le code suivant affiche la chaîne ’Ici!’ sur une fiche à l’emplacement du clic de la
souris :
void __fastcall TForm1::FormMouseDown(TObject *Sender, TMouseButton Button,
TShiftState Shift, int X, int Y)
{
Canvas->TextOut(X, Y, "Ici!");// Ecrire le texte en (X, Y)
}
Lorsque l’application est exécutée, le curseur étant sur la fiche, tout appui sur le
bouton de la souris fera apparaître la chaîne “Ici!” au point cliqué. Ce code
10-26
Guide du développeur
Présentation de la programmation relative aux graphiques
définit la position de dessin en cours par les coordonnées du point où l’utilisateur
a enfoncé le bouton de la souris :
void __fastcall TForm1::FormMouseDown(TObject *Sender, TMouseButton Button,
TShiftState Shift, int X, int Y)
{
Canvas->MoveTo(X, Y);// Définir la position du crayon
}
Désormais, l’enfoncement du bouton de la souris définit la position du crayon et
initialise ainsi le point de départ de la ligne. Pour dessiner une ligne jusqu’au point
où l’utilisateur a relâché le bouton, vous devez répondre à l’événement bouton de
souris relâché.
Réponse à l’action bouton de souris relâché
Un événement OnMouseUp se produit dès que l’utilisateur relâche le bouton de la
souris. L’événement est, en général, adressé à l’objet au-dessus duquel la souris se
trouvait lorsque le bouton a été enfoncé, qui n’est pas nécessairement celui
au-dessus duquel le bouton de la souris est relâché. Cela permet, par exemple, de
dessiner une ligne comme si elle s’étendait au-delà des bords de la fiche.
Pour répondre à une action bouton de souris relâché, définissez le gestionnaire de
l’événement OnMouseUp.
Voici un gestionnaire OnMouseUp qui dessine une ligne jusqu’au point où le
bouton de la souris a été relâché :
void __fastcall TForm1::FormMouseUp(TObject *Sender, TMouseButton Button,
TShiftState Shift, int X, int Y)
{
Canvas->LineTo(X, Y);// Dessiner une ligne de PenPos à (X, Y)
}
Le code permet à l’utilisateur de dessiner des lignes en cliquant, en déplaçant la
souris, puis en relâchant le bouton. Dans ce cas, l’utilisateur ne voit pas la ligne
tant qu’il ne relâche pas le bouton de la souris.
Réponse au déplacement de la souris
Un événement OnMouseMove se produit périodiquement lorsque l’utilisateur
déplace la souris. L’événement est adressé à l’objet qui était sous le pointeur de la
souris lorsque l’utilisateur a enfoncé le bouton. Cela vous permet de fournir un
retour d’informations à l’utilisateur en dessinant des lignes temporaires au fur et
à mesure que la souris est déplacée.
Pour répondre aux déplacements de la souris, définissez un gestionnaire pour
l’événement OnMouseMove de la fiche. Cet exemple utilise les événements
déplacement de la souris pour dessiner sur la fiche des formes intermédiaires
pendant que l’utilisateur maintient enfoncé le bouton de la souris, offrant ainsi à
l’utilisateur un aperçu de ce qu’il obtiendra. Le gestionnaire de l’événement
OnMouseMove dessine une ligne dans la fiche à l’emplacement de l’événement
OnMouseMove :
void __fastcall TForm1::FormMouseMove(TObject *Sender, TMouseButton Button,
Utilisation des graphiques et du multimédia
10-27
Présentation de la programmation relative aux graphiques
TShiftState Shift, int X, int Y)
{
Canvas->LineTo(X, Y);// dessiner une ligne à la position en cours
}
Avec ce code, le dessin suit le déplacement de la souris sur la fiche, avant même
que le bouton de la souris ne soit enfoncé.
Les événements déplacement de la souris se produisent, même lorsque le bouton de
la souris n’a pas été enfoncé.
Pour déterminer si un bouton de la souris est enfoncé, il est nécessaire d’ajouter un
objet champ à l’objet fiche.
Ajout d’un champ à un objet fiche
Lorsque vous ajoutez un composant à une fiche, C++Builder ajoute également un
champ représentant ce composant dans l’objet fiche. Il est ensuite possible de faire
référence à ce composant par le nom de son champ. Vous pouvez également
ajouter vos propres champs en modifiant la déclaration de type dans le fichier
d’en-tête de l’unité de la fiche.
Dans l’exemple suivant, il faut que la fiche détermine si l’utilisateur a enfoncé le
bouton de la souris. Pour cela, un champ booléen a été ajouté dont la valeur est
définie lorsque l’utilisateur enfonce le bouton de la souris.
Pour ajouter un champ à un objet, modifiez la définition de type de l’objet, en
spécifiant l’identificateur du champ et son type après la directive public à la fin de
la déclaration.
C++Builder est “propriétaire” de toutes les déclarations placées avant la directive
public : c’est là qu’il place tous les champs qui représentent les contrôles et les
méthodes répondant aux événements.
Le code suivant ajoute dans la déclaration de l’objet fiche un champ de type bool,
appelé Drawing. Il ajoute également deux champs afin de stocker les points de
type TPoint : Origin et MovePt.
class TForm1 : public TForm
{
__published: // Composants gérés par l’EDI
void __fastcall FormMouseDown(TObject *Sender, TMouseButton Button,
TShiftState Shift, int X, int Y);
void __fastcall FormMouseMove(TObject *Sender, TShiftState Shift, int X,
int Y);
void __fastcall FormMouseUp(TObject *Sender, TMouseButton Button,
TShiftState Shift, int X, int Y);
private:// Déclarations de l’utilisateur
public:// Déclarations de l’utilisateur
__fastcall TForm1(TComponent* Owner);
bool Drawing; //champ stockant si le bouton a été appuyé
TPoint Origin, MovePt; // champs pour le stockage des points
};
10-28
Guide du développeur
Présentation de la programmation relative aux graphiques
Nous disposons maintenant d’un champ Drawing permettant de déterminer s’il faut
dessiner. Il faut donc l’initialiser à true lorsque l’utilisateur enfonce le bouton de la
souris et à false lorsqu’il le relâche :
void __fastcall TForm1::FormMouseDown(TObject *Sender, TMouseButton Button,
TShiftState Shift, int X, int Y)
{
Drawing = true;
// Définit l’indicateur de dessin
Canvas->MoveTo(X, Y);
// Définit la position du crayon
}
void __fastcall TForm1::FormMouseUp(TObject *Sender, TMouseButton Button,
TShiftState Shift, int X, int Y)
{
Canvas->LineTo(X, Y);
// dessine une ligne de PenPos à (X, Y)
Drawing = false;
// efface l’indicateur de dessin
}
Ensuite, vous pouvez modifier le gestionnaire de l’événement OnMouseMove de
façon à ne dessiner que si Drawing est à true :
void __fastcall TForm1::FormMouseMove(TObject *Sender, TMouseButton Button,
TShiftState Shift, int X, int Y)
{
if (Drawing)
Canvas->LineTo(X, Y);// ne dessiner que si la souris est enfoncée
}
Désormais, le dessin n’est effectué qu’entre les événements bouton de souris
enfoncé et bouton de souris relâché, mais il y a toujours cette ligne brisée qui suit le
déplacement de la souris au lieu d’une belle ligne droite.
Le problème tient à ce qu’à chaque déplacement de la souris, le gestionnaire de
l’événement déplacement de souris appelle LineTo qui déplace la position du
crayon. Par conséquent, le point d’origine de la ligne droite est perdu lorsque le
bouton de la souris est relâché.
Amélioration du dessin des lignes
Maintenant que nous disposons de champs pour garder la trace des divers points, il
est possible d’améliorer le dessin des lignes dans l’application.
Suivi du point d’origine
Lors du dessin d’une ligne, le point de départ de la ligne doit être suivi avec le
champ Origin.
Il faut initialiser Origin avec le point où l’événement bouton de souris enfoncé se
produit. De cette manière, le gestionnaire de l’événement bouton de souris relâché
peut utiliser Origin pour tracer le début de la ligne, comme dans le code :
void __fastcall TForm1::FormMouseDown(TObject *Sender, TMouseButton Button,
TShiftState Shift, int X, int Y)
{
Drawing = true;
// Définit l’indicateur de dessin
Canvas->MoveTo(X, Y);
// Définit la position du crayon
Origin = Point(X, Y);
// stocker le point de départ de la ligne
Utilisation des graphiques et du multimédia
10-29
Présentation de la programmation relative aux graphiques
}
void __fastcall TForm1::FormMouseUp(TObject *Sender, TMouseButton Button,
TShiftState Shift, int X, int Y)
{
Canvas->MoveTo(Origin.x, Origin.y); // placer le crayon sur le point de départ
Canvas->LineTo(X, Y);
// dessine une ligne de PenPos à (X, Y)
Drawing = false;
// efface l’indicateur de dessin
}
Cette modification permet de redessiner la ligne finale. Mais qu’en est-il des dessins
intermédiaires ?
Suivi des déplacements
Tel qu’est écrit le gestionnaire de l’événement OnMouseMove, il présente
l’inconvénient de dessiner une ligne, non pas depuis sa position d’origine, mais
depuis la position actuelle de la souris. Pour y remédier, il suffit de placer la
position de dessin au point d’origine et de tirer la ligne jusqu’au point en cours :
void __fastcall TForm1::FormMouseMove(TObject *Sender, TMouseButton Button,
TShiftState Shift, int X, int Y)
{
if (Drawing)
{
Canvas->MoveTo(Origin.x, Origin.y); // placer le crayon sur le point de départ
Canvas->LineTo(X, Y);
}
}
Le code précédent fait le suivi de la position en cours de la souris, mais les
lignes intermédiaires restent affichées et la ligne finale se voit à peine. L’astuce
consiste à effacer chaque ligne avant de tracer la ligne suivante. Cela implique de
garder la trace de son emplacement. C’est la fonction du champ MovePt ajouté
préalablement.
Vous devez définir MovePt par le point d’arrivée de chaque ligne intermédiaire, et
utiliser MovePt et Origin pour effacer cette ligne avant de dessiner la suivante :
void __fastcall TForm1::FormMouseDown(TObject *Sender, TMouseButton Button,
TShiftState Shift, int X, int Y)
{
Drawing = true;
// Définit l’indicateur de dessin
Canvas->MoveTo(X, Y);
// Définit la position du crayon
Origin = Point(X, Y);
// stocker le point de départ de la ligne
MovePt = Point(X, Y);
// stocker le dernier point final
}
void __fastcall TForm1::FormMouseMove(TObject *Sender, TMouseButton Button,
TShiftState Shift, int X, int Y)
{
if (Drawing)
{
Canvas->Pen->Mode = pmNotXor;
// utiliser le mode XOR pour dessiner/effacer
Canvas->MoveTo(Origin.x, Origin.y); // placer le crayon sur le point de départ
Canvas->LineTo(MovePt.x, MovePt.y); // effacer l’ancienne ligne
Canvas->MoveTo(Origin.x, Origin.y); // placer à nouveau le crayon sur le
10-30
Guide du développeur
Utilisation du multimédia
Canvas->LineTo(X, Y);
// point de départ
// dessiner la nouvelle ligne
}
MovePt = Point(X, Y);
// stocker le nouveau point final
Canvas->Pen->Mode = pmCopy;
}
Maintenant, un effet satisfaisant est obtenu lorsque la ligne est dessinée. En
modifiant le mode du crayon en pmNotXor, il combine la ligne avec les pixels de
l’arrière-plan. Lorsque la ligne est effacée, les pixels sont en fait ramenés à leur état
antérieur. En remettant le mode du crayon à pmCopy (sa valeur par défaut) après
avoir dessiné les lignes, le crayon est prêt pour le dessin final lorsque le bouton de
la souris est relâché.
Utilisation du multimédia
C++Buildervous permet d’ajouter des composants multimédia à vos applications
Windows (mais pas dans les applications CLX ou Linux). Vous pouvez le faire
en ajoutant le composant TAnimate de la page Win32 ou le composant
TMediaPlayer de la page Système de la palette des composants. Utilisez le
composant animation pour jouer des séquences vidéo silencieuses dans votre
application. Utilisez le composant lecteur multimédia pour jouer des séquences
audio ou vidéo dans une application.
Pour davantage d’informations sur les composants TAnimate et TMediaPlayer, voir
l’aide en ligne de la VCL.
Cette section aborde les sujets suivants :
• Ajout de séquences vidéo silencieuses à une application
• Ajout de séquences audio et/ou vidéo à une application
Ajout de séquences vidéo silencieuses à une application
Le contrôle animation de C++ Builder vous permet d’ajouter des séquences vidéo
silencieuses à votre application.
Pour ajouter une séquence vidéo silencieuse à une application :
1 Double-cliquez sur l’icône animation dans la page Win32 de la palette des
composants. Cela place automatiquement un contrôle animation dans la fiche
dans laquelle vous voulez afficher la séquence vidéo.
2 En utilisant l’inspecteur d’objets, sélectionnez la propriété Name et entrez un
nouveau nom pour votre contrôle animation. Vous utiliserez ce nom pour
appeler le contrôle animation (respectez les conventions standard de
nomenclature des identificateurs C++).
Travaillez toujours directement dans l’inspecteur d’objets pour initialiser des
propriétés de conception ou pour créer des gestionnaires d’événements.
Utilisation des graphiques et du multimédia
10-31
Utilisation du multimédia
3 Effectuez l’une des opérations suivantes :
• Sélectionnez la propriété Common AVI et choisissez l’un des AVI proposés
dans la liste déroulante.
• Ou sélectionnez la propriété FileName, cliquez sur le bouton points de
suspension et choisissez un fichier AVI dans l’un des répertoires
disponibles localement ou sur le réseau, puis choisissez Ouvrir dans la boîte
de dialogue Ouvrir AVI.
• Ou sélectionnez une ressource AVI en utilisant les propriétés ResName ou
ResID. Utilisez la propriété ResHandle pour indiquer le module contenant la
ressource identifiée par ResName ou ResID.
Cela charge le fichier AVI en mémoire. Pour afficher à l’écran le premier plan
de la séquence AVI, utilisez la propriété Active ou la méthode Play, puis
affectez la valeur true à la propriété Open.
4 Affectez à la propriété Repetitions le nombre spécifiant combien de fois la
séquence AVI doit être jouée. Si cette valeur est nulle, la séquence est répétée
jusqu’à l’appel de la méthode Stop.
5 Faites les autres modifications des propriétés du contrôle animation. Si, par
exemple, vous voulez modifier le premier plan affiché à l’ouverture du
contrôle, affectez le numéro de plan voulu à la propriété StartFrame.
6 Affectez la valeur true à la propriété Active en utilisant la liste déroulante ou
écrivez un gestionnaire d’événement pour exécuter la séquence AVI quand un
événement spécifique a lieu à l’exécution. Par exemple, pour activer la
séquence AVI quand un objet bouton est choisi, écrivez en conséquence le
gestionnaire d’événement OnClick. Vous pouvez également appeler la méthode
Play pour faire jouer la séquence AVI.
Remarque
Si vous faites des modifications à la fiche ou à l’un des composants de la fiche
après avoir affecter la valeur true à Active, la propriété Active revient à false et
vous devez la remettre à true. Vous devez donc faire ceci juste avant la
compilation ou à l’exécution.
Exemple d’ajout de séquences vidéo silencieuses
Vous pouvez, par exemple, afficher un logo animé dans le premier écran
apparaissant au démarrage de votre application. Une fois l’affichage du logo
terminé, l’écran disparaît.
Pour exécuter cet exemple, créez un nouveau projet et enregistrez le fichier
Unit1.cpp sous le nom Frmlogo.cpp et le fichier Project1.bpr sous le nom
Logo.bpr. Ensuite :
1 Double-cliquez sur l’icône animation dans la page Win32 de la palette des
composants.
2 En utilisant l’inspecteur d’objets, affectez à sa propriété Name la valeur Logo1.
3 Sélectionnez sa propriété FileName, cliquez sur le bouton points de
suspension (...), choisissez le fichier dillo.avi dans le répertoire ..\Examples\
10-32
Guide du développeur
Utilisation du multimédia
MFC\General\Cmnctrls. Cliquez ensuite sur le bouton Ouvrir dans la boîte de
dialogue Ouvrir AVI.
Cela charge le fichier dillo.avi en mémoire.
4 Positionnez le contrôle animation dans la fiche en cliquant dessus et en le
faisant glisser sur le coin supérieur droit de la fiche.
5 Affectez la valeur 5 à sa propriété Repetitions.
6 Cliquez sur la fiche pour lui attribuer la focalisation et affectez à sa propriété
Name la valeur LogoForm1 et à sa propriété Caption la valeur Fenêtre Logo.
Diminuez la hauteur de la fiche pour y centrer à droite le contrôle animation.
7 Double-cliquez sur l’événement OnActivate et entrez le code suivant qui
exécute la séquence AVI quand la fiche obtient la focalisation à l’exécution :
Logo1->Active = true;
8 Double-cliquez sur l’icône de libellé dans la page Standard de la palette des
composants. Sélectionnez la propriété Caption du composant et entrez
Bienvenue à Armadillo Enterprises 4.0. Sélectionnez ensuite sa propriété Font,
cliquez sur le bouton points de suspension (...) et choisissez dans la boîte de
dialogue Fonte, Style : gras, Taille : 18, Couleur : Navy, puis choisissez OK.
Cliquez sur le contrôle libellé et faites-le glisser pour le centrer dans la fiche.
9 Cliquez sur le contrôle animation pour lui donner la focalisation. Doublecliquez sur son événement OnStop et écrivez le code suivant pour fermer la
fiche à l’arrêt du fichier AVI :
LogoForm1->Close();
10 Sélectionnez Exécuter|Exécuter pour exécuter la fenêtre au logo animé.
Ajout de séquences audio et/ou vidéo à une application
Le composant lecteur multimédia de C++ Builder vous permet d’ajouter des
séquences audio et/ou vidéo à votre application. Il ouvre un périphérique de
média et peut jouer, arrêter, faire une pause, enregistrer, etc., les séquences audio
et/ou vidéo utilisées par le périphérique de média. Le périphérique de média
peut être matériel ou logiciel.
Remarque
Les séquences audio et/ou vidéo ne sont pas prises en charge en programmation
multiplate-forme.
Pour ajouter une séquence audio et/ou vidéo à une application :
1 Double-cliquez sur l’icône du lecteur multimédia dans la page Système de la
palette des composants. Cela place automatiquement un contrôle lecteur
multimédia dans la fiche à laquelle vous voulez jouer les caractéristiques
multimédia.
2 En utilisant l’inspecteur d’objets, sélectionnez la propriété Name et entrez le
nouveau nom du contrôle lecteur multimédia. Vous utiliserez ce nom pour
désigner le contrôle lecteur multimédia. Respectez les conventions standard
des identificateurs de nom C++).
Utilisation des graphiques et du multimédia
10-33
Utilisation du multimédia
Travaillez toujours directement dans l’inspecteur d’objets pour initialiser des
propriétés de conception ou pour créer des gestionnaires d’événements.
3 Sélectionnez la propriété DeviceType et choisissez le type de périphérique
approprié ouvert par la propriété AutoOpen ou la méthode Open. Si DeviceType
a la valeur dtAutoSelect, le type de périphérique est sélectionné en fonction de
l’extension du fichier média spécifié par la propriété FileName. Pour davantage
d’informations sur les types de périphériques et leurs fonctions, voir le tableau
suivant.
4 Si le périphérique stocke son média dans un fichier, spécifiez le nom du
fichier média en utilisant la propriété FileName. Sélectionnez la propriété
FileName. Cliquez sur le bouton points de suspension et choisissez un fichier
média dans un répertoire disponible localement ou sur le réseau, puis
choisissez Ouvrir dans la boîte de dialogue Ouvrir. Sinon à l’exécution, insérez
dans le lecteur matériel le support contenant le média (disque, cassette, etc)
pour le périphérique de média sélectionné.
5 Affectez la valeur true à la propriété AutoOpen. Ainsi, le lecteur multimédia
ouvre automatiquement le périphérique spécifié quand la fiche contenant le
lecteur est créée à l’exécution. Si AutoOpen a la valeur false, le périphérique
doit être ouvert par un appel de la méthode Open.
6 Affectez la valeur true à la propriété AutoEnable pour activer ou désactiver
automatiquement à l’exécution les boutons nécessaires du lecteur multimédia.
Sinon, double-cliquez sur la propriété EnabledButtons pour affecter la valeur
true ou false à chaque bouton selon que vous souhaitez l’activer ou pas.
Le périphérique multimédia est exécuté, mis en pause ou arrêté quand
l’utilisateur clique sur les boutons correspondants du composant lecteur
multimédia. Il est également possible de contrôler le périphérique via les
méthodes correspondant aux boutons (Play, Pause, Stop, Next, Previous, etc).
7 Positionnez la barre de contrôle du lecteur multimédia dans la fiche. Vous
pouvez le faire en cliquant dessus et en la faisant glisser à la position de votre
choix ou en sélectionnant la propriété Align et en choisissant l’alignement
souhaité dans sa liste déroulante.
Si vous voulez que le lecteur multimédia soit invisible à l’exécution, affectez la
valeur false à sa propriété Visible et contrôlez le périphérique en appelant les
méthodes appropriées (Play, Pause, Stop, Next, Previous, Step, Back,
Start Recording, Eject).
8 Effectuez les autres paramétrages du contrôle lecteur multimédia. Si, par
exemple, le média nécessite une fenêtre d’affichage, affectez à la propriété
10-34
Guide du développeur
Utilisation du multimédia
Display le contrôle affichant le média. Si le périphérique utilise plusieurs
pistes, affectez à la propriété Tracks la piste souhaitée.
Tableau 10.6 Types de périphériques multimédia et leurs fonctions
Logiciel/matériel utilisé
Joue
Utilise
des
pistes
dtAVIVideo
Lecteur AVI Vidéo pour
Window
fichiers AVI Video
Non
Oui
dtCDAudio
Lecteur CD Audio pour
Windows ou un lecteur
CD Audio
Disques CD Audio
Oui
Non
dtDAT
Lecteur de cassettes
audio-numériques
Cassettes audionumériques
Oui
Non
dtDigitalVideo
Lecteur vidéo-numérique
pour Windows
fichiers AVI, MPG,
MOV
Non
Oui
dtMMMovie
Lecteur de films MM
films MM
Non
Oui
dtOverlay
Périphérique overlay
Vidéo analogique
Non
Oui
dtScanner
Scanner d’image
N/d pour Play
(scanne des images
avec Record)
Non
Non
dtSequencer
Séquenceur MIDI pour
Windows
fichiers MIDI
Oui
Non
dtVCR
Enregistreur de cassettes
vidéo
Cassettes vidéo
Non
Oui
dtWaveAudio
Lecteur audio Wav pour
Windows
fichiers WAV
Non
Non
Type de
périphérique
Utilise
une fenêtre
d’affichage
Exemple d’ajout de séquences audio et/ou vidéo (VCL seulement)
Cet exemple exécute une séquence vidéo AVI pour une publicité multimédia de
C++Builder. Pour exécuter cet exemple, créez un nouveau projet et enregistrez le
fichier Unit1.cpp sous le nom FrmAd.cpp et le fichier Project1.bpr sous le nom
MmediaAd.bpr. Puis :
1 Double-cliquez sur l’icône lecteur multimédia dans la page Système de la
palette des composants.
2 En utilisant l’inspecteur d’objets, affectez à la propriété Name du lecteur
multimédia la valeur VideoPlayer1.
3 Sélectionnez sa propriété DeviceType et choisissez dtAVIVideo dans la liste
déroulante.
4 Sélectionnez sa propriété FileName, cliquez sur le bouton points de
suspension (...) et choisissez le fichier dans le répertoire ..\Examples\Coolstuf.
Choisissez le bouton Ouvrir dans la boîte de dialogue Ouvrir.
5 Affectez la valeur true à sa propriété AutoOpen et la valeur false à sa
propriété Visible.
Utilisation des graphiques et du multimédia
10-35
Utilisation du multimédia
6 Double-cliquez sur l’icône animation dans la page Win32 de la palette des
composants. Affectez la valeur false à sa propriété AutoSize, 175 à sa
propriété Height et 200 à sa propriété Width. Cliquez sur le contrôle
animation et faites-le glisser dans le coin supérieur gauche de la fiche.
7 Cliquez sur le contrôle lecteur multimédia pour lui donner la focalisation.
Sélectionnez sa propriété Display et choisissez Animate1 dans la liste
déroulante.
8 Cliquez sur la fiche pour lui attribuer la focalisation et sélectionnez sa
propriété Name et affectez-lui la valeur C++_Ad. Redimensionnez la fiche
pour lui donner la taille du contrôle animation.
9 Double-cliquez sur l’événement OnActivate et écrivez le code suivant pour
exécuter la séquence vidéo AVI quand la fiche a la focalisation :
VideoPlayer1->Play();
10 Choisissez Exécuter|Exécuter pour exécuter la vidéo AVI.
10-36
Guide du développeur
Chapitre
11
Ecriture d’applications multithreads
Chapitre 11
C++Builder dispose de plusieurs objets facilitant la conception d’applications
multithreads. Les applications multithreads sont des applications qui contiennent
plusieurs chemins d’exécution simultanés. Même si l’utilisation de plusieurs
threads doit être mûrement réfléchie, elle peut améliorer un programme :
• En évitant les engorgements. Avec un seul thread, un programme doit arrêter
complètement l’exécution pour attendre les processus lents comme les accès
disque, la communication avec d’autres machines ou l’affichage de données
multimédia. La CPU est inactive jusqu’à l’achèvement du processus. Avec
plusieurs threads, l’application peut poursuivre l’exécution dans des threads
séparés pendant qu’un thread attend le résultat du processus lent.
• En organisant le comportement d’un programme. Le comportement d’un
programme peut souvent être décomposé en plusieurs processus fonctionnant
de manière indépendante. L’utilisation de threads permet d’exécuter
simultanément une section de code pour chacun de ces processus. Utilisez les
threads pour assigner des priorités aux diverses tâches du programme afin
d’attribuer davantage de temps machine aux tâches critiques.
• En gérant plusieurs processeurs. Si le système exécutant le programme
dispose de plusieurs processeurs, il est possible d’améliorer les performances
en décomposant le travail en plusieurs threads s’exécutant simultanément sur
des processeurs distincts.
Remarque
Tous les systèmes d’exploitation ne gèrent pas réellement l’utilisation de
plusieurs processeurs, même si cela est proposé par le matériel sous-jacent.
Par exemple, Windows 9x ne fait que simuler l’utilisation de processeurs
multiples, même si le matériel sous-jacent le gère.
Ecriture d’applications multithreads
11-1
Définition d’objets thread
Définition d’objets thread
Dans la plupart des applications, vous pouvez utiliser un objet thread pour
représenter un thread d’exécution de l’application. Les objets thread simplifient
l’écriture d’applications multithreads en encapsulant les utilisations les plus
fréquentes des threads.
Remarque
Les objets thread ne permettent pas de contrôler les attributs de sécurité ou la
taille de la pile des threads. Si vous souhaitez contrôler ces paramètres, vous
devez utiliser la fonction CreateThread de l’API Windows ou la fonction
BeginThread. Même si vous utilisez des appels de l’API thread Windows ou
BeginThread, vous pouvez néanmoins utiliser les objets de synchronisation de
threads et les méthodes décrites dans la section “Coordination de threads” à la
page 11-8. Pour davantage d’informations sur l’utilisation de CreateThread ou de
BeginThread, voir l’aide en ligne Windows.
Pour utiliser un objet thread dans une application, créez un nouveau descendant
de TThread. Pour créer un descendant de TThread, choisissez Fichier|Nouveau|
Autre dans le menu principal. Dans la boîte de dialogue Nouveaux éléments,
double-cliquez sur Objet thread (ou sur Objet Thread CLX pour les applications
multiplates-formes) et entrez un nom de classe, tel que TMyThread. Pour nommer
ce nouveau thread, vous devez cocher la case Thread nommé et spécifier un
nom de thread (VCL seulement). Nommer votre thread permet d’effectuer son
suivi pendant le débogage. Quand vous sélectionnez OK, C++Builder crée un
nouveau fichier .CPP et un en-tête pour implémenter le thread. Pour davantage
d’informations sur les threads nommés, voir “Nommer un thread” à la
page 11-14.
Remarque
A la différence de la plupart des boîtes de dialogue de l’EDI demandant un nom
de classe, la boîte de dialogue Nouvel objet thread ne préfixe pas
automatiquement le nom de classe avec un ‘T’.
Le fichier .cpp automatiquement généré contient le squelette du code de la
nouvelle classe de thread. Si vous avez nommé ce thread TMyThread, le code
doit avoir l’aspect suivant :
//--------------------------------------------------------------------------#include <vcl.h>
#pragma hdrstop
#include "Unit2.h"
#pragma package(smart_init)
//--------------------------------------------------------------------------__fastcall TMyThread::TMyThread(bool CreateSuspended): TThread(CreateSuspended)
{
}
//--------------------------------------------------------------------------void __fastcall TMyThread::Execute()
{
// ---- Placez le code du thread ici ---}
//---------------------------------------------------------------------------
11-2
Guide du développeur
Définition d’objets thread
Vous devez spécifier le code de la méthode Execute et du constructeur. Ces
étapes sont décrites dans les sections suivantes.
Initialisation du thread
Utilisez le constructeur pour initialiser la nouvelle classe thread. C’est là que
vous pouvez affecter une priorité par défaut au thread et indiquer s’il doit être
libéré automatiquement à la fin de son exécution.
Affectation d’une priorité par défaut
La priorité indique la préférence accordée au thread quand le système
d’exploitation répartit le temps machine entre les différents threads de
l’application. Utilisez un thread de priorité élevée pour gérer les tâches critiques
et un thread de priorité basse pour les autres tâches. Pour indiquer la priorité de
l’objet thread, affectez la propriété Priority.
Si vous écrivez une application Windows, les valeurs de Priority se répartissent
sur une échelle, comme décrit dans le tableau suivant :
Tableau 11.1 Priorités des threads
Valeur
Priorité
tpIdle
Le thread s’exécute uniquement quand le système est inoccupé. Windows
n’interrompt pas d‘autres threads pour exécuter un thread de priorité tpIdle.
tpLowest
La priorité du thread est deux points en dessous de la normale.
tpLower
La priorité du thread est un point en dessous de la normale.
tpNormal
Le thread a une priorité normale.
tpHigher
La priorité du thread est un point au-dessus de la normale.
tpHighest
La priorité du thread est deux points au-dessus de la normale.
tpTimeCritical
Le thread a la priorité la plus élevée.
Remarque
Si vous écrivez une application multiplate-forme, vous devez séparer le code
destiné à attribuer les priorités pour Windows du code pour Linux. Sous Linux,
Priority est une valeur numérique qui dépend de la politique choisie pour les
threads, politique qui ne peut être modifiée que par l’utilisateur root. Voir la
version CLX de TThread et Priority dans l’aide en ligne pour plus de détails.
Attention
“Gonfler” la priorité du thread pour une opération utilisant intensivement la
CPU peut “sous-alimenter” les autres threads de l’application. Il ne faut accorder
une priorité élevée qu’à des threads qui passent l’essentiel du temps à attendre
des événements extérieurs.
Le code suivant illustre le constructeur d’un thread de priorité basse qui effectue
des tâches d’arrière-plan ne devant pas interférer avec les performances du reste
de l’application :
//--------------------------------------------------------------------------__fastcall TMyThread::TMyThread(bool CreateSuspended): TThread(CreateSuspended)
{
Priority = tpIdle;
Ecriture d’applications multithreads
11-3
Définition d’objets thread
}
//---------------------------------------------------------------------------
Libération des threads
Généralement, lorsque les threads ont fini d’être exécutés, ils peuvent être
simplement libérés. Dans ce cas, le plus simple consiste à laisser l’objet thread se
libérer lui-même. Pour ce faire, affectez la valeur true à la propriété
FreeOnTerminate.
Il y a cependant des cas où la fin d’un thread doit être coordonnée avec les
autres threads. Par exemple, il se peut que vous deviez attendre qu’un thread
renvoie une valeur avant d’effectuer une action dans un autre thread. Pour ce
faire, vous ne souhaitez pas libérer le premier thread avant que le second n’ait
reçu la valeur renvoyée. Vous pouvez traiter ce type de situation en affectant la
valeur false à FreeOnTerminate et en libérant explicitement le premier thread à
partir du second.
Ecriture de la fonction thread
La méthode Execute constitue la fonction thread. Vous pouvez la concevoir
comme un programme qui est exécuté par l’application, à cette différence près
qu’il partage le même espace de processus. L’écriture d’une fonction thread est
plus délicate que celle d’un programme distinct car il faut prendre garde à ne
pas écraser la mémoire utilisée par d’autres threads de l’application. D’un autre
côté, comme le thread partage le même espace de processus que les autres
threads, il est possible d’utiliser la mémoire partagée pour faire communiquer les
threads.
Utilisation du thread principal VCL/CLX
Quand vous utilisez des objets appartenant aux hiérarchies d’objets VCL et CLX,
leurs propriétés et méthodes ne sont pas nécessairement adaptées à l’utilisation
de threads. C’est-à-dire que l’accès aux propriétés et méthodes peut effectuer des
actions utilisant de la mémoire qui n’est pas protégée de l’action d’autres
threads. De ce fait, un thread principal est placé à part pour l’accès aux objets
VCL et CLX. C’est ce thread qui gère tous les messages Windows reçus par les
composants d’une application.
Si tous les objets accèdent à leurs propriétés et exécutent leurs méthodes dans ce
seul thread, il n’est pas nécessaire de se préoccuper d’éventuelles interférences
entre les objets. Pour utiliser le thread principal, créez une routine séparée
effectuant les actions nécessaires. Appelez cette routine séparée depuis la
méthode Synchronize de votre thread. Par exemple :
void __fastcall TMyThread::PushTheButton(void)
{
Button1->Click();
}
ƒ
void __fastcall TMyThread::Execute()
11-4
Guide du développeur
Définition d’objets thread
{
ƒ
Synchronize((TThreadMethod)PushTheButton);
ƒ
}
Synchronize attend le thread principal pour entrer dans la boucle des messages
puis exécute la méthode qui lui est transmise.
Remarque
Comme Synchronize utilise la boucle des messages, elle ne fonctionne pas dans
les applications console. Vous devez utiliser d’autres mécanismes, comme les
sections critiques, pour protéger l’accès aux objets VCL ou CLX dans les
applications console.
Il n’est pas toujours nécessaire d’utiliser le thread principal. Certains objets sont
adaptés aux threads. Il est préférable de ne pas utiliser la méthode Synchronize
quand vous savez que les méthodes d’un objet sont adaptées à l’utilisation des
threads, car cela améliore les performances en évitant d’avoir à attendre le
thread VCL ou CLX pour entrer dans la boucle de messages. Il n’est pas
nécessaire d’utiliser la méthode Synchronize dans les objets suivants :
• Les composants d’accès aux données sont adaptés aux threads comme suit :
pour les ensembles de données BDE chaque thread doit disposer de son
propre composant session de base de données. Il n’y a qu’une seule
exception : les pilotes Microsoft Access. Ces pilotes sont conçus en utilisant la
bibliothèque ADO Microsoft qui n’est pas adaptée aux threads. Pour dbDirect,
il suffit que la bibliothèque client du fournisseur soit adaptée aux threads
pour que les composants dbDirect le soient également. Les composants ADO
et InterbaseExpress sont adaptés aux threads.
Lorsque vous utilisez des composants d’accès aux données, vous devez
néanmoins encadrer tous les appels aux contrôles orientés données dans la
méthode Synchronize. Ainsi, il est nécessaire de synchroniser les appels qui
relient un contrôle orienté données à un ensemble de données, mais il n’est
pas nécessaire de le faire pour accéder aux données d’un champ de l’ensemble
de données.
Pour davantage d’informations sur l’utilisation de sessions de base de données
avec les threads dans des applications BDE, voir “Gestion de sessions
multiples” à la page 24-32.
• Les objets DataCLX sont adaptés aux threads mais pas les objets VisualCLX.
• Les objets graphiques sont adaptés aux threads. Il n’est pas nécessaire
d’utiliser le thread principal VCL ou CLX pour accéder aux objets TFont, TPen,
TBrush, TBitmap, TMetafile (VCL seulement), TDrawing (CLX seulement) et
TIcon. Il est possible d’accéder aux objets canevas en dehors de la méthode
Synchronize en les verrouillant (voir “Verrouillage d’objets” à la page 11-8).
• Les listes d’objets ne sont pas adaptées aux threads, mais vous pouvez utiliser
une version adaptée aux threads, TThreadList, à la place de TList.
Appelez régulièrement la routine CheckSynchronize depuis le thread principal de
votre application pour que les threads d’arrière-plan synchronisent leur exécution
sur le thread principal. Le meilleur emplacement pour appeler CheckSynchronize
Ecriture d’applications multithreads
11-5
Définition d’objets thread
est lorsque l’application est inactive (par exemple, dans le gestionnaire de
l’événement OnIdle). Cela vous garantit qu’il est sans danger de faire appels aux
méthodes du thread d’arrière-plan.
Utilisation de variables locales aux threads
La méthode Execute et toutes les routines qu’elle appelle ont leurs propres
variables locales comme toute routine C++. Ces routines peuvent également
accéder à toutes les variables globales. En fait, les variables globales constituent
un mécanisme puissant de communication entre les threads.
Mais dans certains cas, vous souhaitez utiliser des variables globales pour les
routines du thread sans qu’elles ne soient partagées par les autres instances de la
même classe de thread. Il est possible pour ce faire de déclarer des variables
locales au thread. Déclarez une variable locale au thread en ajoutant le
modificateur __thread à la variable. Par exemple :
int __thread x;
déclare une variable de type entier privée pour chaque thread de l’application,
mais globale à l’intérieur de chaque thread.
Le modificateur __thread ne peut être utilisé que pour des variables globales
(portée fichier) ou statiques. Les variables pointeur et fonction ne peuvent pas
être des variables de thread. Les types utilisant une sémantique de copie lors de
l’écriture, comme les AnsiStrings ne peuvent pas non plus faire office de
variables de thread. Un élément de programme nécessitant l’initialisation ou la
finalisation à l’exécution ne peut être déclaré comme étant de type __thread.
Les déclarations suivantes nécessitent une initialisation à l’exécution et sont donc
illégales.
int f( );
int __thread x = f( );
// illégal
L’instanciation d’une classe ayant un constructeur ou un destructeur défini par
l’utilisateur nécessite une initialisation à l’exécution et est donc interdite :
class X {
X( );
~X( );
};
X __thread myclass;
// illégal
Vérification de l’arrêt par d’autres threads
Un thread commence son exécution quand la méthode Execute est appelée
(voir “Exécution d’objets thread” à la page 11-12) et se poursuit jusqu’à l’arrêt de
Execute. Cela correspond à une situation dans laquelle le thread effectue une
tâche spécifique puis s’arrête une fois celle-ci terminée. Dans certains cas, une
application a besoin qu’un thread poursuive son exécution jusqu’à ce qu’un
critère externe soit respecté.
Il est possible de permettre à d’autres threads de signaler qu’il est temps que
votre thread arrête de s’exécuter en testant la propriété Terminated. Quand un
11-6
Guide du développeur
Définition d’objets thread
autre thread tente de terminer votre thread, il appelle la méthode Terminate.
Terminate affecte la valeur true à la propriété Terminated de votre thread. C’est à
la méthode Execute de votre thread d’implémenter la méthode Terminate en
testant la valeur de la propriété Terminated. L’exemple suivant illustre une
manière de procéder :
void __fastcall TMyThread::Execute()
{
while (!Terminated)
PerformSomeTask();
}
Gestion des exceptions dans la fonction thread
La méthode Execute doit capturer toutes les exceptions qui se produisent dans le
thread. Si vous échouez à capturer une exception dans votre fonction thread,
votre application risque de provoquer des violations d’accès. Cela ne se voit pas
lorsque vous développez car l’EDI capture l’exception, mais lorsque vous
exécuterez votre application hors du débogueur, l’exception provoquera une
erreur d’exécution et l’application cessera de s’exécuter.
Pour capturer les exceptions se produisant à l’intérieur de votre fonction thread,
ajoutez un bloc try...catch à l’implémentation de la méthode Execute :
void __fastcall TMyThread::Execute()
{
try
{
while (!Terminated)
PerformSomeTask();
}
catch (...)
{
// faire quelque chose avec les exceptions
}
}
Ecriture du code de nettoyage
Vous pouvez centraliser le code de nettoyage lors de la fin de l’exécution du
thread. Juste avant la fin du thread, un événement OnTerminate a lieu. Placez
l’éventuel code de nettoyage dans le gestionnaire d’événement OnTerminate afin
de garantir son exécution quel que soit le chemin d’exécution suivi par la
méthode Execute.
Le gestionnaire d’événement OnTerminate n’est pas exécuté comme partie de
votre thread. Il est en fait exécuté dans le contexte du thread principal VCL ou
CLX de votre application. Cela a deux implications :
• Il n’est pas possible d’utiliser de variables locales au thread dans un
gestionnaire d’événement OnTerminate (sauf à vouloir utiliser les valeurs du
thread principal VCL ou CLX).
Ecriture d’applications multithreads
11-7
Coordination de threads
• Il est possible d’accéder en toute sécurité à tous les composants et objets VCL
ou CLX dans le gestionnaire d’événement OnTerminate sans se préoccuper des
conflits avec les autres threads.
Pour davantage d’informations sur le thread principal VCL ou CLX, voir
“Utilisation du thread principal VCL/CLX” à la page 11-4.
Coordination de threads
Quand vous écrivez le code exécuté lorsque le thread s’exécute, vous devez tenir
compte du comportement des autres threads qui peuvent s’exécuter
simultanément. En particulier, il faut éviter que deux threads tentent d’utiliser
simultanément le même objet ou la même variable globale. De plus, le code d’un
thread peut dépendre de tâches effectuées par d’autres threads.
Eviter les accès simultanés
Pour éviter les conflits avec d’autres threads lors de l’accès à des objets ou des
variables, il peut être nécessaire de bloquer l’exécution des autres threads jusqu’à
ce que le code d’un thread ait terminé une opération. Mais il ne faut pas bloquer
inutilement l’exécution des threads. Cela peut provoquer une dégradation
importante des performances et réduire à néant les avantages liés à l’utilisation
de threads multiples.
Verrouillage d’objets
Certains objets disposent d’un verrouillage intégré qui empêche les autres
threads d’utiliser cette instance d’objet.
Ainsi, les objets canevas (TCanvas et ses descendants) ont une méthode Lock qui
empêche les autres threads d’accéder au canevas jusqu’à l’appel de la méthode
Unlock.
La VCL et CLX contiennent également tous les deux un objet liste adapté aux
threads, TThreadList. L’appel deTThreadList::LockList renvoie l’objet liste tout en
empêchant les autres threads d’exécution d’utiliser la liste jusqu’à l’appel de la
méthode UnlockList. Les appels des méthodes TCanvas::Lock et
TThreadList::LockList peuvent être imbriqués. Le verrou n’est pas libéré tant que le
dernier verrouillage n’est pas associé au déverrouillage correspondant dans le
même thread.
Utilisation de sections critiques
Pour les objets ne disposant pas de verrouillage intégré, vous pouvez utiliser une
section critique. Les sections critiques fonctionnent comment une porte ne
pouvant être franchie que par un seul thread à la fois. Pour utiliser une section
critique, créez une instance globale de TCriticalSection. TCriticalSection dispose de
deux méthodes, Acquire (qui empêche les autres threads d’exécuter la section) et
Release (qui retire le blocage).
11-8
Guide du développeur
Coordination de threads
Chaque section critique est associée à la mémoire globale devant être protégée.
Chaque thread accédant à cette mémoire globale doit commencer par utiliser la
méthode Acquire pour vérifier qu’un autre thread n’est pas en train de l’utiliser.
Une fois terminé, le thread appelle la méthode Release afin que les autres threads
puissent accéder à la mémoire globale en appelant Acquire.
Attention
Les sections critiques ne peuvent fonctionner que si tous les threads les utilisent
pour accéder à la mémoire globale associée. Les threads qui ne tiennent pas
compte des sections critiques et accèdent à la mémoire globale sans appeler
Acquire peuvent provoquer des problèmes d’accès simultanés.
Par exemple, une application a une section critique des variables globales,
pLockXY, qui bloque l’accès aux variables globales X et Y. Tout thread utilisant X
ou Y doit encadrer cette utilisation d’appels à la section critique comme cidessous :
pLockXY->Acquire(); // Bloque les autres threads
try
{
Y = sin(X);
}
__finally
{
pLockXY->Release();
}
Utilisation du synchronisateur à écriture exclusive et lecture multiple
Lorsque vous utilisez des sections critiques pour protéger la mémoire globale,
seul un thread peut utiliser la mémoire à un moment donné. Cette protection
peut être exagérée, notamment si un objet ou une variable doit être souvent lu
mais dans lequel vous écrivez très rarement. Il n’y a aucun danger à ce que
plusieurs threads lisent la même mémoire simultanément, pourvu qu’aucun
thread n’y écrit.
Lorsqu’une mémoire globale est souvent lue, mais dans laquelle les threads
n’écrivent qu’occasionnellement, vous pouvez la protéger à l’aide de l’objet
TMultiReadExclusiveWriteSynchronizer. Cet objet agit comme une section critique
mais permet à plusieurs threads de lire la mémoire qu’il protège à condition
qu’aucun thread n’y écrive. Les threads doivent disposer d’un accès exclusif en
écriture à la mémoire protégée par TMultiReadExclusiveWriteSynchronizer.
Pour utiliser un synchronisateur à écriture exclusive et à lecture multiple, créez
une instance globale de TMultiReadExclusiveWriteSynchronizer associée à la
mémoire globale que vous souhaitez protéger. Chaque thread qui lit cette
mémoire doit au préalable appeler la méthode BeginRead. BeginRead évite qu’un
autre thread n’écrive simultanément dans la mémoire. Lorsqu’un thread a fini de
lire la mémoire protégée, il appelle la méthode EndRead. Tout thread qui écrit
dans la mémoire protégée doit au préalable appeler BeginWrite. BeginWrite évite
qu’un autre thread ne lise ou n’écrive simultanément dans la mémoire.
Lorsqu’un thread a fini d’écrire dans la mémoire protégée, il appelle la méthode
EndWrite, de sorte que les threads en attente puissent commencer à lire
la mémoire.
Ecriture d’applications multithreads
11-9
Coordination de threads
Attention
Comme les sections critiques, le synchronisateur à écriture exclusive et à lecture
multiple ne fonctionne que si chaque thread l’utilise pour accéder à la mémoire
globale associée. Les threads qui ignorent le synchronisateur et accèdent à la
mémoire globale sans appeler BeginRead ou BeginWrite génèrent des problèmes
d’accès simultané.
Autres techniques de partage de la mémoire
Si vous utilisez des objets de la VCL ou de CLX, utilisez le thread principal pour
exécuter votre code. L’utilisation du thread principal garantit que les objets
n’accèdent pas indirectement à de la mémoire utilisée par d’autres objets VCL ou
CLX dans d’autres threads. Voir “Utilisation du thread principal VCL/CLX” à la
page 11-4 pour davantage d’informations sur le thread principal.
Si la mémoire globale n’a pas besoin d’être partagée par plusieurs threads,
envisagez d’utiliser des variables locales aux threads au lieu de variables
globales. En utilisant des variables locales aux threads, votre thread n’a pas
besoin d’attendre ou de bloquer les autres threads. Voir “Utilisation de variables
locales aux threads” à la page 11-6 pour davantage d’informations sur les
variables locales aux threads.
Attente des autres threads
Si votre thread doit attendre la fin d’autres threads pour terminer une tâche,
vous pouvez demander au thread de suspendre son exécution. Vous pouvez
attendre la fin de l’exécution d’un autre thread ou attendre qu’un autre thread
signale qu’il a achevé une tâche.
Attente de la fin d’exécution d’un thread
Pour attendre la fin de l’exécution d’un thread, utilisez la méthode WaitFor de
l’autre thread. WaitFor ne revient que lorsque l’autre thread se termine, soit en
finissant sa propre méthode Execute, soit à la suite d’une exception. Par exemple,
le code suivant attend qu’un autre thread remplisse un objet liste de threads
avant d’accéder aux objets de la liste :
if (pListFillingThread->WaitFor())
{
TList *pList = ThreadList1->LockList();
for (int i = 0; i < pList->Count; i++)
ProcessItem(pList->Items[i]);
ThreadList1->UnlockList();
}
Dans l’exemple précédent, l’accès aux éléments de la liste ne se fait que lorsque
la méthode WaitFor indique que la liste a été remplie. La valeur renvoyée doit
être affectée par la méthode Execute du thread en attente. Cependant, puisque les
threads appelant WaitFor veulent connaître le résultat de l’exécution du thread, la
méthode Execute ne renvoie pas de valeur. A la place, la méthode Execute
initialise la propriété ReturnValue. ReturnValue est alors renvoyée par la méthode
11-10
Guide du développeur
Coordination de threads
WaitFor quand elle est appelée par d’autres threads. Les valeurs renvoyées sont
des entiers. Votre application en détermine la signification.
Attente de l’achèvement d’une tâche
Parfois, il est nécessaire d’attendre qu’un thread termine une opération au lieu
d’attendre la fin de l’exécution d’un thread particulier. Pour ce faire, utilisez un
objet événement. Les objets événements (TEvent) doivent être créés avec une
portée globale afin qu’ils puissent agir comme des signaux visibles pour tous les
threads.
Quand un thread termine une opération dont dépendent d’autres threads, il
appelle TEvent::SetEvent. SetEvent active le signal afin que les autres threads le
surveillant sachent que l’opération a été achevée. Pour désactiver le signal,
utilisez la méthode ResetEvent.
Par exemple, imaginons une situation dans laquelle vous devez attendre la fin de
l’exécution de plusieurs threads au lieu d’un seul. Comme vous ne savez pas
quel sera le dernier thread, vous ne pouvez pas utiliser la méthode WaitFor de
l’un d’eux. A la place, vous pouvez faire en sorte que chaque thread incrémente
un compteur à la fin de son exécution et que le dernier thread signale
l’achèvement de l’exécution de tous les threads en générant un événement.
Le code suivant montre la fin du gestionnaire d’événement OnTerminate de tous
les threads dont l’exécution doit être achevée. CounterGuard est un objet section
critique global qui évite l’utilisation simultanée du compteur par plusieurs
threads. Counter est une variable globale qui compte le nombre de threads dont
l’exécution est achevée.
void __fastcall TDataModule::TaskThreadTerminate(TObject *Sender)
{
ƒ CounterGuard->Acquire(); // verrouille le compteur
// décrémente le compteur global
if (--Counter == 0)
Event1->SetEvent();
// signale si c’est le dernier thread
CounterGuard->Release(); // libère le verrou sur le compteur
}
Le thread principal initialise la variable Counter, lance les threads de tâche et
attend le signal indiquant l’achèvement de l’exécution de tous les threads en
appelant la méthode WaitFor. WaitFor attend l’activation du signal pendant une
durée spécifiée et renvoie l’une des valeurs du tableau suivant :
Tableau 11.2 Valeurs renvoyées par WaitFor
Valeur
Signification
wrSignaled
Le signal de l’objet événement a été activé.
wrTimeout
La durée spécifiée s’est écoulée sans que le signal soit défini.
wrAbandoned
L’objet événement a été détruit avant l’écoulement de la durée spécifiée.
wrError
Une erreur a eu lieu pendant l’attente.
Ecriture d’applications multithreads
11-11
Exécution d’objets thread
Le code suivant montre comment le thread principal lance les threads de tâche et
reprend la main lorsque leur exécution est achevée :
Event1->ResetEvent(); // Efface l’événement avant de lancer les threads
for (int i = 0; i < Counter; i++)
new TaskThread(false); // Crée et exécute les threads de tâche
if (Event1->WaitFor(20000) != wrSignaled)
throw Exception;
// poursuivre maintenant dans le thread principal, toutes les tâches sont finies
Remarque
Si vous ne voulez pas cesser d’attendre un événement après un délai spécifié,
transmettez à la méthode WaitFor une valeur de paramètre INFINITE. Faites
attention en utilisant INFINITE, car cela peut provoquer le blocage du thread si
le signal attendu n’arrive pas.
Exécution d’objets thread
Une fois une classe thread implémentée en définissant sa méthode Execute, vous
pouvez l’utiliser dans une application pour exécuter le code de sa méthode
Execute. Pour utiliser un thread, créez une instance de la classe thread. L’instance
de thread peut être créée pour un démarrage immédiat ou placée en état
d’attente afin de n’être exécutée qu’avec l’appel de la méthode Resume. Pour
créer un thread s’exécutant immédiatement, affectez la valeur false au paramètre
CreateSuspended du constructeur. Par exemple, la ligne suivante crée un thread et
commence son exécution :
TMyThread *SecondThread = new TMyThread(false); // Crée et exécute le thread
Attention
Ne créez pas trop de threads dans une application. Le surcoût lié à la gestion de
plusieurs threads peut influer sur les performances. La limite recommandée est
de 16 threads par processus sur une machine disposant d’un seul processeur.
Cette limite suppose que la plupart de ces threads attendent des événements
externes. Si tous les threads sont actifs, il convient de réduire encore ce nombre.
Vous pouvez créer plusieurs instances du même type de thread pour exécuter
du code parallèle. Vous pouvez, par exemple, démarrer une nouvelle instance
d’un thread en réponse à une action de l’utilisateur, ce qui permet à chaque
thread de générer la réponse attendue.
Redéfinition de la priorité par défaut
Quand le temps machine accordé au thread est lié à la tâche accomplie par le
thread, sa priorité est définie dans le constructeur, comme décrit dans
“Initialisation du thread” à la page 11-3. Par contre, si la priorité du thread varie
en fonction du moment de l’exécution du thread, créez le thread en état
suspendu, affectez sa priorité puis démarrez l’exécution du thread :
TMyThread *SecondThread = new TMyThread(true); // Créer sans exécuter
SecondThread->Priority = tpLower; // Définir une priorité inférieure à la normale
SecondThread->Resume(); // Exécuter le thread maintenant
11-12
Guide du développeur
Débogage d’applications multithreads
Remarque
Si vous écrivez une application multiplate-forme, vous devez séparer le code
destiné à attribuer les priorités pour Windows du code pour Linux. Sous Linux,
Priority est une valeur numérique qui dépend de la politique choisie pour les
threads, politique qui ne peut être modifiée que par l’utilisateur root. Voir la
version CLX de TThread et Priority dans l’aide en ligne pour plus de détails.
Démarrage et arrêt des threads
Un thread peut être démarré et arrêté plusieurs fois avant de terminer son
exécution. Pour interrompre temporairement l’exécution d’un thread, appelez sa
méthode Suspend. Quand il faut reprendre l’exécution du thread, appelez sa
méthode Resume. Suspend augmente un compteur interne, il est donc possible
d’imbriquer les appels aux méthodes Suspend et Resume. L’exécution du thread
ne reprend que si à chaque appel de Suspend correspond un appel de Resume.
Vous pouvez mettre un terme à l’exécution d’un thread en appelant sa méthode
Terminate. Terminate affecte la valeur true à la propriété Terminated du thread. Si
vous avez correctement implémenté la méthode Execute, elle teste
périodiquement la valeur de la propriété Terminated et s’arrête si Terminated a la
valeur true.
Débogage d’applications multithreads
Lors du débogage d’applications multithreads, il est compliqué de surveiller
l’état de tous les threads s’exécutant simultanément ou même de déterminer quel
thread s’exécute quand vous êtes sur un point d’arrêt. Vous pouvez utiliser la
boîte d’état des threads pour surveiller et manipuler tous les threads de
l’application. Pour afficher la boîte de dialogue Etat des threads, choisissez Voir|
Fenêtres de débogage|Threads dans le menu principal.
Quand un événement de débogage a lieu, (point d’arrêt, exception, pause), la
vue Etat des threads indique l’état de chaque thread. Cliquez avec le bouton
droit de la souris dans la boîte de dialogue Etat des threads pour accéder aux
commandes permettant d’accéder au code source correspondant ou changer le
thread courant. Quand un thread est marqué comme en cours, l’étape suivante
ou l’opération d’exécution suivante se fait relativement à ce thread.
La boîte de dialogue Etat des threads liste tous les threads d’exécution de
l’application par leur identificateur de thread. Si vous utilisez des objets thread,
l’identificateur de thread correspond à la valeur de la propriété ThreadID. Si vous
n’utilisez pas d’objets thread, l’identificateur de chaque thread est renvoyé lors
de l’appel de CreateThread ou de BeginThread.
Pour davantage d’informations sur la boîte d’état des threads, voir l’aide
en ligne.
Ecriture d’applications multithreads
11-13
Débogage d’applications multithreads
Nommer un thread
Comme il est difficile de déterminer quel ID de thread correspond à quel thread
dans la boîte de dialogue Etat des threads, vous pouvez nommer vos classes de
thread. Lors de la création d’une classe de thread dans la boîte dialogue Objet
thread, outre la saisie du nom de la classe, cochez la case Thread nommé,
saisissez le nom du thead et choisissez OK.
Nommer la classe de thread ajoute à la classe une méthode appelée SetName.
Au démarrage du thread, il commence par appeler la méthode SetName.
CLX
Vous ne pouvez nommer les threads que dans les applications VCL.
Conversion d’un thread anonyme en thread nommé
Vous pouvez convertir un thread anonyme en thread nommé. Si, par exemple,
vous avez une classe de thread créée avec C++Builder 5, convertissez-la en un
thread nommé en suivant la procédure suivante.
1 Ajoutez la méthode SetName à votre classe de thread :
//--------------------------------------------------------------------------void TMyThread::SetName()
{
THREADNAME_INFO info;
info.dwType = 0x1000;
info.szName = "MyThreadName";
info.dwThreadID = -1;
info.dwFlags = 0;
__try
{
RaiseException( 0x406D1388, 0, sizeof(info)/sizeof(DWORD),(DWORD*)&info );
}
__except (EXCEPTION_CONTINUE_EXECUTION)
{
}
}
//--------------------------------------------------------------------------Remarque
Attribuez le nom de votre classe de thread à info.szName.
Le débogeur trouve l’exception et recherche le nom de thread dans la
structure transmise. Lors du débogage, le débogeur affiche le nom du thread
dans la zone ID de la boîte de dialogue Etat des threads.
2 Ajoutez un appel de la méthode SetName au début de la méthode Execute du
thread :
//--------------------------------------------------------------------------void __fastcall TMyThread::Execute()
{
SetName();
//---- Placez ici le code existant de la méthode Execute ---}
//---------------------------------------------------------------------------
11-14
Guide du développeur
Débogage d’applications multithreads
Affectation de noms distincts à des threads similaires
Toutes les instances d’une même classe de thread portent le même nom. Vous
pouvez cependant affecter à l’exécution un nom différent à chaque instance de
thread en utilisant la procédure suivante.
1 Ajoutez une propriété ThreadName à la classe de thread en ajoutant le code
suivant à la définition de classe :
__property AnsiString ThreadName = {read=FName, write=FName};
2 Dans la méthode SetName, changez le code :
info.szName = “MyThreadName”;
en :
info.szName = ThreadName;
3 Lors de la création de l’objet thread :
1 Créez le thread comme suspendu. Pour davantage d’informations, voir
“Exécution d’objets thread” à la page 11-12.
2 Affectez un nom au thread, par exemple
MyThread.ThreadName=”RechercheFichiers”;
3 Relancez le thread. Pour davantage d’informations, voir “Démarrage et arrêt
des threads” à la page 11-13.
Ecriture d’applications multithreads
11-15
11-16
Guide du développeur
Chapitre
12
Gestion des exceptions
Chapitre 12
C++Builder supporte la gestion d’exceptions C++, la gestion d’exceptions C
structurées et la gestion d’exceptions VCL et CLX.
Vous pouvez déclencher des exceptions Ansi Standard C++ et de type VCL,
qui comportent des routines de gestion des erreurs.
Les exceptions structurées Win32 C sont également gérées, de sorte que votre
code puisse réagir de façon appropriée aux exceptions déclenchées par le
système d’exploitation Windows.
Gestion des exceptions C++
Les exceptions sont des conditions exceptionnelles qui requièrent une gestion
spéciale et peuvent incorporer des erreurs se produisant à l’exécution comme les
divisions par zéro et le manque d’espace libre. La gestion d’exceptions fournit une
méthode standard pour traiter les erreurs, découvrir des problèmes anticipés et
des problèmes inattendus. Elle permet aussi aux développeurs de reconnaître,
suivre et réparer les bogues.
Quand une erreur se produit, le programme déclenche une exception. Celle-ci
contient généralement des informations sur ce qui s’est passé. Ces informations
permettent à une autre partie du programme de diagnostiquer la cause de
l’exception.
Les programmes se préparent aux exceptions en plaçant des instructions
susceptibles de les déclencher dans un bloc try. Si aucune de ces instructions ne
déclenche une exception, le contrôle est transféré à un gestionnaire d’exception qui
gère ce type d’exception. On dit que le gestionnaire intercepte l’exception et
spécifie les actions à entreprendre avant l’arrêt du programme.
Gestion des exceptions
12-1
Gestion des exceptions C++
Syntaxe de gestion des exceptions
Le mécanisme de gestion des exceptions nécessite l’utilisation de trois mots clés :
try, throw et catch. Le mot clé throw permet de générer une exception. Le bloc
try contient des instructions susceptibles de déclencher des exceptions et il est
suivi d’une ou plusieurs instructions catch. Chaque instruction catch gère un
type d’exception spécifique.
Remarque
Les mots clés try, catch et throw ne sont pas autorisés dans les programmes C.
Le bloc try
Le bloc try contient une ou plusieurs instructions qui peuvent déclencher une
exception. Un programme lance une exception en exécutant une instruction
throw. L’instruction throw se produit généralement à l’intérieur d’une fonction.
Par exemple :
void SetFieldValue(DF *dataField, int userValue)
{
if ((userValue < 0) || userValue > 10)
throw EIntegerRange(0, 10, userValue);
. . .
}
Une autre partie du programme peut intercepter l’objet exception déclenché et le
gérer en conséquence. Par exemple :
try
{
SetFieldValue(dataField, userValue);
}
catch (EIntegerRange &rangeErr)
{
printf("Valeur attendue entre %d et %d, mais obtention de %d\n",
rangeErr.min, rangeErr.max, rangeErr.value);
}
Dans l’exemple précédent, si la fonction SetFieldValue constate que ses paramètres
d’entrée sont incorrects, elle peut déclencher une exception pour indiquer cette
erreur. Le bloc try/catch encapsule SetFieldValue pour intercepter l’exception
déclenchée par SetFieldValue et exécute l’instruction printf. Si aucune exception
n’est déclenchée, l’instruction printf ne s’exécute pas.
Un bloc try spécifié par try doit être immédiatement suivi par le gestionnaire
spécifié par catch. Le bloc try est une instruction qui spécifie le flux de contrôle
au fur et à mesure que le programme s’exécute. Si une exception est déclenchée
dans ce bloc, le contrôle du programme est remis au gestionnaire d’exception
approprié.
Le gestionnaire est un bloc de code conçu pour gérer l’exception. Le langage C++
nécessite qu’il y ait au moins un gestionnaire de disponible après un bloc try.
Il doit y avoir un gestionnaire pour chaque exception susceptible d’être générée
par le programme.
12-2
Guide du développeur
Gestion des exceptions C++
L’instruction throw
L’instruction throw peut déclencher divers types d’objets. Dans C++, les objets
peuvent être déclenchés par valeur, par référence ou par pointeur. Par exemple :
// déclenche un objet à intercepter par valeur ou par référence
throw EIntegerRange(0, 10, userValue);
// déclenche un objet à intercepter par pointeur
throw new EIntegerRange(0, 10, userValue);
Les deux exemples suivants montrent des fonctionnalités qui sont fournies dans
un souci d’exhaustivité dans le système standard. Il est préférable de déclencher
des exceptions plus descriptives. Dans certains cas particuliers, vous pouvez
déclencher des types intégrés, par exemple des entiers. Enfin, il est préférable de
ne pas déclencher des exceptions par pointeur.
// déclenche un entier
throw 1;
// déclenche un char *
throw "foo";
Dans la plupart des cas, vous intercepterez les exceptions par référence, en
particulier par référence const. Parfois, vous intercepterez avec précaution des
objets par valeur. Les objets ainsi interceptés doivent être copiés avant d’être
affectés au paramètre catch. Si un utilisateur fournit un constructeur de copie,
celui-ci est appelé, ce qui peut altérer l’efficacité.
L’instruction catch
L’instruction catch se présente sous diverses formes. Les objets peuvent être
interceptés par valeur, par référence ou par pointeur. En outre, des modificateurs
const peuvent être appliqués au paramètre catch. Pour qu’un bloc try puisse
intercepter plusieurs types d’exceptions différents, plusieurs instructions catch
peuvent être ajoutées. Dans tous les cas, il doit y avoir une instruction catch
pour chaque exception susceptible d’être déclenchée. Par exemple :
try
CommitChange(dataBase, recordMods);
catch (const EIntegerRange &rangeErr)
printf("Obtention d’une exception d’intervalle d’entiers");
catch (const EFileError &fileErr)
printf("Obtention d’une erreur d’E/S de fichier");
Si la fonction CommitChange utilise plusieurs sous-systèmes qui peuvent
déclencher différents types d’exceptions, vous pouvez gérer chaque type
d’exception séparément. Avec plusieurs instructions catch pour une seule
instruction try, vous pouvez avoir des gestionnaires pour chaque type
d’exception.
Si un objet exception est dérivé d’une classe de base, vous pouvez ajouter des
gestionnaires spécialisés pour certaines exceptions dérivées, mais aussi inclure un
gestionnaire générique pour la classe de base. Pour ce faire, placez les
instructions catch dans l’ordre dans lequel vous voulez qu’elles soient
Gestion des exceptions
12-3
Gestion des exceptions C++
recherchées quand une exception est déclenchée. Par exemple, le code suivant
gère d’abord EIntegerRange puis ERange, dont EIntegerRange est dérivée.
try
SetFieldValue(dataField, userValue);
catch (const EIntegerRange &rangeErr)
printf("Obtention d’une exception d’intervalle d’entiers");
catch (const ERange &rangeErr)
printf("Obtention d’une exception d’intervalle");
Enfin, si vous voulez que votre gestionnaire intercepte toutes les exceptions
susceptibles d’être déclenchées après le bloc try, utilisez la forme spéciale
catch(…). Cette instruction indique que le gestionnaire doit être invoqué pour
toutes les exceptions. Par exemple :
try
SetFieldValue(dataField, userValue);
catch (...)
printf("Obtention d’une exception quelconque");
Redéclenchement des exceptions
Dans certains cas, un gestionnaire d’exception peut traiter une exception, puis
redéclencher cette exception ou en déclencher une autre.
Si le gestionnaire souhaite redéclencher l’exception en cours, il peut se contenter
d’utiliser l’instruction throw sans paramètre. Cette instruction indique au
compilateur/RTL de prendre l’objet exception en cours et de le déclencher à
nouveau. Par exemple :
catch (EIntegerRange &rangeErr)
{
// Code ici pour une gestion locale de l’exception
throw; // redéclencher l’exception
}
Si le gestionnaire souhaite déclencher une autre exception, il utilise l’instruction
throw normalement.
Spécifications des exceptions
Vous pouvez spécifier les exceptions qu’une fonction peut déclencher. Le
déclenchement d’une exception de type incorrect après une fonction constitue
une erreur d’exécution. La syntaxe d’une spécification d’exceptions est la suivante :
exception-specification:
throw (type-id-list) // type-id-list est facultatif
type-id-list:
type-id
type-id-list, type-id
Voici des exemples de fonctions avec des spécifications d’exceptions.
void f1();
12-4
Guide du développeur
// La fonction peut déclencher n’importe quelle exception
Gestion des exceptions C++
void f2() throw();
// Elle ne devrait déclencher aucune exception
void f3() throw( A, B* ); // Peut déclencher des exceptions publiquement dérivées de A,
// ou un pointeur vers des exceptions publiquement dérivées de B
La définition et toutes les déclarations d’une telle fonction doivent présenter une
spécification d’exceptions contenant le même ensemble d’identificateurs de types. Si
une fonction déclenche une exception non listée dans sa spécification
d’exceptions, le programme appelle unexpected.
Vous n’avez pas besoin de spécifier une exception dans les cas suivants :
La spécification d’exceptions pour une fonction a des effets sur les performances
d’exécution de Windows.
Des erreurs inattendues peuvent survenir à l’exécution. Par exemple, supposons
que votre système utilise des spécifications d’exceptions et un autre sous-système
dans son implémentation. Supposons maintenant que le sous-système est modifié
pour pouvoir déclencher de nouveaux types d’exceptions. Quand vous utilisez le
nouveau code du sous-système, vous pouvez avoir des erreurs d’exécution sans
jamais avoir obtenu d’indication de ces erreurs de la part du compilateur.
Enfin, si vous utilisez des fonctions virtuelles, vous pouvez aller à l’encontre de
la programmation. En effet, la spécification d’exceptions n’est pas considérée
comme faisant partie du type de la fonction. Par exemple, dans le code suivant,
la classe dérivée BETA::vfunc est définie de telle sorte qu’elle ne déclenche
aucune exception—une déviation par rapport à la déclaration originelle de
la fonction.
class ALPHA {
public:
struct ALPHA_ERR {};
virtual void vfunc(void) throw (ALPHA_ERR) {} // Spécification de l’exception
};
class BETA : public ALPHA {
void vfunc(void) throw() {} // La spécification de l’exception est modifiée
};
Déroulement des exceptions
Quand une exception est déclenchée, une bibliothèque d’exécution prend l’objet
déclenché, obtient le type de l’objet et recherche vers le haut dans la pile
d’appels un gestionnaire dont le type correspond au type de l’objet déclenché.
Une fois le gestionnaire trouvé, la RTL déroule la pile jusqu’au point du
gestionnaire, puis exécute le gestionnaire.
Dans le processus de déroulement, la RTL appelle les destructeurs de tous les
objets locaux dans les cadres de piles entre le point où l’exception a été
déclenchée et celui où elle a été interceptée. Si un destructeur est à l’origine
d’une exception lors d’un déroulement de pile et qu’il ne la gère pas, la fonction
terminate est appelée. Les destructeurs sont appelés par défaut, mais ce mode de
fonctionnement peut être inhibé en utilisant l’option de compilation -xd-.
Gestion des exceptions
12-5
Gestion des exceptions C++
Pointeurs sécurisés
Si vous avez des variables locales qui sont des pointeurs sur des objets et si une
exception est déclenchée, ces pointeurs ne sont pas automatiquement supprimés.
Ceci est dû au fait que le compilateur ne peut pas faire la différence entre un
pointeur sur des données qui ont été allouées à cette fonction uniquement et un
autre pointeur. Pour vous assurer que des objets alloués à une utilisation locale
sont détruits même en cas d’exception, utilisez la classe auto_ptr. Dans certains
cas, la mémoire est libérée pour un pointeur alloué dans une fonction :
TMyObject *pMyObject = new TMyObject;
Dans cet exemple, si le constructeur de TMyObject déclenche une exception, le
pointeur sur l’objet alloué à TMyObject est supprimé par la RTL quand elle
déroule l’exception. C’est le seul cas où le compilateur supprime
automatiquement une valeur de pointeur.
Constructeurs dans la gestion d’exceptions
Les constructeurs de classe peuvent déclencher des exceptions s’ils ne
parviennent pas à construire un objet. Si un constructeur déclenche une
exception, le destructeur de cet objet n’est pas obligatoirement appelé. Les
destructeurs ne sont appelés que pour les classes de base et pour les objets ayant
été entièrement construits à l’intérieur des classes suite à l’entrée du bloc try.
Gestion des exceptions non interceptées et inattendues
Si une exception est déclenchée et qu’aucun gestionnaire n’est trouvé (c’est-à-dire
que l’exception n’est pas interceptée), le programme appelle une fonction
terminate. Vous pouvez spécifier votre propre fonction de terminaison avec
set_terminate. Si vous ne spécifiez aucune fonction de terminaison, la fonction
terminate est appelée. Par exemple, le code suivant utilise la fonction my_terminate
pour gérer les exceptions qui ne sont interceptées par aucun gestionnaire.
void SetFieldValue(DF *dataField, int userValue)
{
if ((userValue < 0) || (userValue) > 10));
throw EIntegerRange(0, 10, userValue);
. . .
}
void my_terminate()
{
printf("Exception non interceptée");
abort();
}
// Définit my_terminate() comme fonction de terminaison
set_terminate(my_terminate);
// Appelle SetFieldValue. Ceci génère une exception car la valeur de l’utilisateur est
// supérieure à 10. Comme l’appel n’est pas dans un bloc try, my_terminate est appelée.
SetFieldValue(DF, 11);
12-6
Guide du développeur
Exceptions structurées sous Win32
Si une fonction spécifie les exceptions qu’elle déclenche puis déclenche une
exception non spécifiée, une fonction inattendue est appelée. Vous pouvez
spécifier votre propre fonction inattendue avec set_unexpected. Si vous ne
spécifiez aucune fonction inattendue, la fonction unexpected est appelée.
Exceptions structurées sous Win32
Win32 supporte la gestion des exceptions structurées du C comme c’est le cas
pour les exceptions C++. Toutefois, il existe certaines différences qui requièrent
de procéder avec attention lorsque ces exceptions sont combinées à du code C++
sensible aux exceptions.
Lorsque vous utilisez la gestion des exceptions structurées dans des applications
C++Builder, vous devez garder les considérations suivantes à l’esprit :
• Les exceptions structurées du langage C sont utilisables dans les programmes
C++.
• Les exceptions C++ ne peuvent pas être utilisées dans les programmes C car
leur gestionnaire est spécifié par le mot clé catch qui n’est pas reconnu par le
langage C.
• Une exception générée par un appel à la fonction RaiseException est gérée par
un bloc try/__except (C++) ou __try/__except (C). Vous pouvez aussi utiliser
des blocs try/__finally ou __try/__finally. Voir “Syntaxe des exceptions
structurées” à la page 12-8. Tous les gestionnaires des blocs try/catch sont
ignorés lorsque RaiseException est appelée.
• Les exceptions qui ne sont pas gérées par l’application n’entraînent pas
d’appel à la fonction terminate(), mais sont transmises au système
d’exploitation (en principe, le résultat final est l’arrêt du processus).
• Les gestionnaires d’exceptions ne reçoivent pas de copie de l’objet exception, à
moins qu’ils ne le demandent.
Les fonctions suivantes d’aide à la gestion des exceptions du langage C peuvent
être utilisées dans les programmes C et C++ :
•
•
•
•
GetExceptionCode
GetExceptionInformation
SetUnhandledExceptionFilter
UnhandledExceptionFilter
C++Builder n’exige pas que la fonction UnhandledExceptionFilter soit utilisée
uniquement dans le filtre d’exception des blocs __try/__except ou try/__except.
Toutefois, le comportement du programme est indéfini lorsque cette fonction est
appelée en dehors de l’un des blocs __try/__except ou try/__except.
CLX
Les applications CLX exécutées sur Linux ne gèrent pas les exceptions
structurées C/C++.
Gestion des exceptions
12-7
Exceptions structurées sous Win32
Syntaxe des exceptions structurées
Dans un programme en C, les mots clés compatibles ANSI utilisés pour
implémenter les exceptions structurées sont __except, __finally et __try.
Remarque
Le mot clé __try ne peut apparaître que dans les programmes C. Si vous voulez
que votre code soit portable, n’utilisez pas de gestion des exceptions structurées
dans vos programmes C++.
Syntaxe de gestion des exceptions try-except
Pour une gestion d’exceptions try-except, la syntaxe est la suivante :
bloc-try :
__try instruction-composée (dans un module C)
try instruction-composée (dans un module C++)
handler:
__except (expression) instruction-composée
Syntaxe de terminaison try-finally
Pour une gestion de terminaison try-finally, la syntaxe est la suivante :
bloc-try :
__try instruction-composée (dans un module C)
try instruction-composée (dans un module C++)
termination:
__finally instruction-composée
Gestion des exceptions structurées
Les exceptions structurées peuvent être gérées en utilisant une extension de la
gestion d’exceptions C++ :
try {
foo();
}
__except(__expr__) {
// gestionnaire ici
}
__expr__ est une expression qui est évaluée à l’une de ces trois valeurs :
12-8
Valeur
Description
EXCEPTION_CONTINUE_SEARCH (0)
Pas d’entrée dans le gestionnaire et le système
d’exploitation continue de rechercher un
gestionnaire d’exception.
EXCEPTION_CONTINUE_EXECUTION (-1)
Continue l’exécution au point de l’exception.
Guide du développeur
Exceptions structurées sous Win32
Valeur
Description
EXCEPTION_EXECUTE_HANDLER (1)
Entre dans le gestionnaire d’exception. Si le
code a été compilé alors que le nettoyage des
destructeurs était activé (option -xd, activée
par défaut), tous les destructeurs des objets
locaux créés entre le point de l’exception et le
gestionnaire d’exception sont appelés lorsque
la pile est déroulée. Le déroulement de la pile
prend fin avant d’entrer dans le gestionnaire.
Win32 fournit deux fonctions qui peuvent être utilisées pour obtenir des
informations sur l’exception en cours : GetExceptionCode() et
GetExceptionInformation(). Si vous voulez appeler une fonction depuis l’expression
de “filtre” présentée précédemment, elle doit l’être directement depuis le
contexte de __except() :
#include <Windows.h>
#include <excpt.h>
int filter_func(EXCEPTION_POINTERS *);
...
EXCEPTION_POINTERS *xp = 0;
try {
foo();
}
__except(filter_func(xp = GetExceptionInformation())) {
//...
}
Si vous le souhaitez, vous pouvez aussi utiliser l’opérateur virgule pour les
affectations imbriquées dans des appels de fonction, comme dans l’exemple cidessous :
__except((xp = GetExceptionInformation()), filter_func(xp))
Filtres d’exceptions
Une expression filtre peut appeler une fonction filtre, mais une fonction filtre ne
peut pas appeler GetExceptionInformation. La valeur de retour de
GetExceptionInformation peut être transmise comme paramètre de la fonction filtre.
Pour transmettre les informations EXCEPTION_POINTERS à un gestionnaire
d’exceptions, l’expression filtre ou la fonction filtre doit copier le pointeur ou les
données de GetExceptionInformation à un emplacement où le gestionnaire peut
ultérieurement y accéder.
Dans le cas où des instructions try-except sont imbriquées, l’expression filtre de
chaque instruction est évaluée jusqu’à ce qu’elle trouve
EXCEPTION_EXECUTE_HANDLER ou EXCEPTION_CONTINUE_EXECUTION.
Une expression filtre peut appeler GetExceptionInformation pour obtenir des
informations sur l’exception.
Gestion des exceptions
12-9
Exceptions structurées sous Win32
Tant que GetExceptionInformation ou GetExceptionCode est appelée directement
dans l’expression fournie à __except, vous pouvez utiliser une fonction pour
déterminer comment traiter l’exception plutôt que d’essayer de créer une
expression C++ complexe. Toutefois, toutes les informations nécessaires à la
gestion de l’exception peuvent être extraites du résultat de
GetExceptionInformation(). GetExceptionInformation() renvoie un pointeur sur une
structure EXCEPTION_POINTERS :
struct EXCEPTION_POINTERS {
EXCEPTION_RECORD *ExceptionRecord;
CONTEXT *Context;
};
EXCEPTION_RECORD contient l’état indépendamment de la machine :
struct EXCEPTION_RECORD {
DWORD ExceptionCode;
DWORD ExceptionFlags;
struct EXCEPTION_RECORD *ExceptionRecord;
void *ExceptionAddress;
DWORD NumberParameters;
DWORD ExceptionInformation[EXCEPTION_MAXIMUM_PARAMETERS];
};
Généralement, la fonction filtre consulte les informations de ExceptionRecord
pour décider comment répondre. Parfois des informations plus précises sont
nécessaires (notamment si l’action à prendre est
EXCEPTION_CONTINUE_EXECUTION: si rien n’est fait, le code ayant provoqué
l’exception pourrait être exécuté à nouveau). Pour cette situation, l’autre champ
de la structure EXCEPTION_POINTERS indique l’état du processeur au moment
de l’exception. Si cette structure est modifiée et que le filtre renvoie
EXCEPTION_CONTINUE_EXCEPTION, il est utilisé pour définir l’état du thread
avant de poursuivre l’exécution. Par exemple :
static int xfilter(EXCEPTION_POINTERS *xp)
{
int rc;
EXCEPTION_RECORD *xr = xp->ExceptionRecord;
CONTEXT *xc = xp->Context;
switch (xr->ExceptionCode) {
case EXCEPTION_BREAKPOINT:
// hops, quelqu’un a oublié un point d’arrêt incorporé.
// passer dessus (1 octet sur x86)
++xc->Eip;
rc = EXCEPTION_CONTINUE_EXECUTION;
break;
case EXCEPTION_ACCESS_VIOLATION:
rc = EXCEPTION_EXECUTE_HANDLER;
break;
default:
// abandon
12-10
Guide du développeur
Exceptions structurées sous Win32
rc = EXCEPTION_CONTINUE_SEARCH;
break;
};
return rc;
}
...
EXCEPTION_POINTERS *xp;
try {
func();
}
__except(xfilter(xp = GetExceptionInformation())) {
abort();
}
Mélange du C++ avec des exceptions structurées
Lorsque vous utilisez des exceptions structurées dans des programmes C++,
vous devez avoir conscience de certains problèmes. Bien que C++Builder
implémente les exceptions C++ avec des exceptions structurées Win32, les
exceptions C++ sont transparentes pour un bloc __except.
Un bloc try peut être suivi par un bloc except ou par au moins un bloc catch.
Toute tentative de combiner les deux provoque une erreur de compilation. Pour
que du code puisse gérer les deux types d’exceptions, vous devez l’imbriquer
dans deux blocs try :
try {
EXCEPTION_POINTERS *xp;
try {
func();
}
__except(xfilter(xp = GetExceptionInformation())) {
//...
}
}
catch (...) {
//...
}
La spécification throw() d’une fonction n’affecte pas le comportement d’un
programme en ce qui concerne une exception Win32. En conséquence, une
exception non gérée est finalement gérée par le système d’exploitation (si un
débogueur ne la gère pas d’abord), à la différence d’un programme C++ qui
appelle terminate().
Tout module compilé avec l’option de compilation -xd (activée par défaut)
appellera les destructeurs pour tous les objets avec le stockage auto. Le
déroulement de pile se produit depuis le point où l’exception est déclenchée
jusqu’au point où elle est interceptée.
Gestion des exceptions
12-11
Exceptions structurées sous Win32
Exemple d’un programme C++ ayant des exceptions C
/* Résultat du programme :
Une autre exception :
Interception d’une exception C.
Interception d’une exception C++[Erreur matérielle : Division par 0]
C++ autorise aussi __finally !
*/
#include <stdio.h>
#include <string.h>
#include <windows.h>
class Exception
{
public:
Exception(char* s = "Unknown"){ what = strdup(s);
}
Exception(const Exception& e ){ what = strdup(e.what); }
~Exception()
{ delete[] what;
}
char* msg() const
{ return what;
}
private:
char* what;
};
int main()
{
float e, f, g;
try
{
try
{
f = 1.0;
g = 0.0;
try
{
puts("Une autre exception:");
e = f / g;
}
__except(EXCEPTION_EXECUTE_HANDLER)
{
puts("Interception d’une exception C.");
throw(Exception("Erreur matérielle: division par 0"));
}
}
catch(const Exception& e)
{
printf("Interception d’une exception C++ : %s :\n", e.msg());
}
}
__finally
{
puts("C++ autorise aussi __finally !");
}
return e;
}
12-12
Guide du développeur
Exceptions structurées sous Win32
Définition d’exceptions
Le fait de déclencher une exception Win32 gérée à l’intérieur d’un même
programme n’a pas grand intérêt : les exceptions C++ ont plus d’efficacité, sont
plus portables et utilisent une syntaxe plus simple. Les exceptions Win32 ont
toutefois un avantage : elles peuvent être gérées par des composants n’ayant pas
été compilés par le même compilateur C++.
La première étape consiste à définir l’exception. Une exception est un entier
32 bits qui a le format suivant (en commençant au bit 0) :
Bit
Signification
31-30
11
00
01
10
29
1 = défini par
l’utilisateur
=
=
=
=
erreur (normal)
réussite,
informatif
avertissement
28
Réservé
27-0
Défini par
l’utilisateur
En plus de la définition de code d’exception, vous devez décider si des
informations supplémentaires doivent être incluses avec l’exception (accessibles
au filtre/gestionnaire depuis l’enregistrement exception). Il n’existe aucune
méthode conventionnelle pour coder des paramètres supplémentaires dans le
code d’exception. Pour plus d’informations, reportez-vous à la documentation
Win32 (disponible dans l’aide en ligne de C++Builder).
Déclenchement d’exceptions
Une exception Win32 est déclenchée par un appel à RaiseException(), qui est
déclarée comme suit :
void RaiseException(DWORD ec, DWORD ef, DWORD na, const DWORD *a);
où :
ec
Code d’exception
ef
Indicateurs d’exception (0 ou EXCEPTION_NONCONTINUABLE)
(Si l’exception est considérée comme ne pouvant pas être continuée et qu’un filtre tente
de la continuer, EXCEPTION_NONCONTINUABLE_EXCEPTION est déclenchée.)
na
Nombre d’éléments dans le tableau d’arguments.
a
Pointeur sur le premier élément du tableau d’arguments – la signification de ces
arguments dépend de chaque exception.
Gestion des exceptions
12-13
Exceptions structurées sous Win32
Blocs de terminaison
Le modèle de gestion des exceptions structurées supporte un bloc de terminaison
qui est exécuté même si la sortie s’effectue normalement d’un bloc protégé ou
via une exception. Le compilateur C++Builder supporte cette opération en C avec
la syntaxe suivante :
__try {
func();
}
__finally {
// cela se produit selon que func() déclenche une exception ou non
}
Les blocs de terminaison sont supportés par une extension C++ où vous pouvez
traiter du code final dans le bloc __finally :
try {
func();
}
__finally {
// cela se produit selon que func() déclenche une exception ou non
}
L’exemple suivant illustre les blocs de terminaison :
/* Résultat du programme :
Une exception :
Interception d’une exception.
Le bloc __finally est aussi exécuté !
Aucune exception :
Aucune exception ne s’est produite, mais __finally s’exécute toujours !
*/
#include <stdio.h>
#include <windows.h>
int main()
{
float e, f, g;
try
{
f = 1.0;
g = 0.0;
try
{
puts("Une exception:");
e = f / g;
}
__except(EXCEPTION_EXECUTE_HANDLER)
{
puts("Interception d’une exception.");
}
}
__finally
12-14
Guide du développeur
Exceptions structurées sous Win32
{
puts("Le bloc __finally est aussi exécuté !");
}
try
{
f = 1.0;
g = 2.0;
try
{
puts("Aucune exception:");
e = f / g;
}
__except(EXCEPTION_EXECUTE_HANDLER)
{
puts("Interception d’une exception.");
}
}
__finally
{
puts("Aucune exception ne s’est produite, mais le bloc __finally s’exécute toujours
!");
}
return e;
}
Le code C++ peut aussi gérer un “bloc de terminaison” en créant des objets
locaux avec des destructeurs qui sont appelés au moment de la sortie de la
portée. Etant donné que les exceptions structurées C++Builder supportent le
nettoyage des destructeurs, cela fonctionne quel que soit le type de l’exception.
Remarque
Un cas particulier concerne ce qui se produit lorsqu’une exception est déclenchée
et qu’elle n’est jamais gérée par le programme. Pour une exception C++, le
compilateur C++Builder appelle des destructeurs pour les objets locaux (non
requis par les définitions de langage), étant donné qu’aucun nettoyage de
destructeur ne se produit avec une exception Win32 non gérée.
Options de gestion des exceptions C++Builder
Voici la liste des options de gestion des exceptions du compilateur C++Builder.
Tableau 12.1 Options de gestion des exceptions du compilateur
Commutateur
de la ligne
de commande
Description
-x
Active la gestion des exceptions C++ (Activée par défaut).
-xd
Active le nettoyage des destructeurs. Appelle les destructeurs de tous les
objets déclarés automatiquement entre la portée des instructions catch et
throw lorsqu’une exception est déclenchée. (Option avancée – activée par
défaut)
Gestion des exceptions
12-15
Gestion des exceptions VCL/CLX
Tableau 12.1 Options de gestion des exceptions du compilateur (suite)
Commutateur
de la ligne
de commande
-xp
Description
Active les informations de localisation des exceptions. Permet
l’identification des exceptions en mode exécution en plaçant des numéros
de ligne dans le code source à l’emplacement de l’exception. Quand une
erreur s’est produite, ceci permet au programme de demander le fichier et
le numéro de ligne en utilisant les fonctions globales __ThrowFileName et
__ThrowLineNumber. (Option avancée)
Gestion des exceptions VCL/CLX
Si vous utilisez des composants VCL et CLX dans vos applications, vous devez
comprendre le mécanisme de gestion des exceptions VCL/CLX. En effet, les
exceptions sont incorporées à de nombreuses classes et sont déclenchées
automatiquement lorsque quelque chose d’inattendu se produit. Si vous ne gérez
pas l’exception, la VCL et la CLX la géreront par défaut d’une manière
appropriée. En principe, un message décrivant le type de l’erreur s’affiche.
Lorsque vous rencontrez une exception qui affiche un message indiquant le type
de l’exception ayant été déclenchée, vous pouvez consulter les informations sur
la classe de l’exception dans l’aide en ligne. Les informations fournies vous
aideront à déterminer l’endroit où l’erreur s’est produite et sa cause.
Par ailleurs, le chapitre 13, “Gestion en langage C++ de VCL et CLX”, décrit les
subtiles différences du langage qui peuvent provoquer des exceptions. La section
“Exceptions déclenchées dans les constructeurs” à la page 13-14 fournit un
exemple démontrant ce qui se produit lorsqu’une exception est déclenchée lors
de la construction d’un objet.
Différences entre la gestion d’exceptions C++ et VCL/CLX
Voici quelques différences notables entre la gestion des exceptions C++
et VCL/CLX :
Exceptions déclenchées par des constructeurs :
• Les destructeurs C++ sont appelés pour les membres et les classes de base
totalement construits.
• Les destructeurs de classes de base VCL et CLX sont appelés même si l’objet
ou la classe de base n’est pas totalement construit.
Déclenchement et interception des exceptions :
• Les exceptions C++ peuvent être interceptées par référence, par pointeur ou
par valeur. Les exceptions VCL et CLX, qui sont dérivées de TObject, ne
peuvent être interceptées que par référence ou par pointeur. Toute tentative
d’intercepter des exceptions TObject par valeur résulte en une erreur signalée
lors de la compilation. Les exceptions matérielles ou du système d’exploitation
(comme EAccessViolation) doivent être interceptées par référence.
12-16
Guide du développeur
Gestion des exceptions VCL/CLX
• Les exceptions VCL et CLX sont interceptées par référence.
• Il n’est pas possible de réutiliser throw pour déclencher à nouveau une
exception ayant été interceptée à l’intérieur du code VCL ou CLX.
Gestion des exceptions du système d’exploitation
C++Builder permet de gérer les exceptions produites par le système
d’exploitation. Ces exceptions incluent les violations d’accès, les erreurs
arithmétiques de nombres entiers, les erreurs arithmétiques de nombres flottants,
les débordements de pile et les interruptions Ctrl+C. Ces exceptions sont gérées
dans la RTL C++ et converties en objets de classe d’exception VCL et CLX avant
d’être transmis à votre application. Vous pouvez ensuite écrire du code C++ de
la forme :
try
{
char * p = 0;
*p = 0;
}
// Il faut toujours intercepter par référence.
catch (const EAccessViolation &e)
{
printf("Il ne faut pas faire ça!\n");
}
Les classes d’exceptions utilisées par C++Builder sont exactement celles utilisées
par Delphi et elles ne sont disponibles que pour les applications VCL et CLX de
C++Builder. Elles dérivent de TObject et nécessitent la VCL et la CLX.
Voici quelques caractéristiques de la gestion des exceptions de C++Builder :
• L’utilisateur n’est pas responsable de la libération de l’objet exception.
• Les exceptions du système d’exploitation ne peuvent être interceptées que par
référence.
• L’utilisateur ne peut pas redéclencher une exception du système d’exploitation
après la sortie du cadre d’interception et faire en sorte qu’elle soit interceptée
par d’autres cadres d’interception de la VCL et CLX.
• L’utilisateur ne peut pas redéclencher une exception du système d’exploitation
après la sortie du cadre d’interception et faire en sorte qu’elle soit interceptée
par d’autres cadres d’interception du système d’exploitation.
Les deux derniers points peuvent se traduire à peu près comme suit : quand une
exception du système d’exploitation a été interceptée en tant qu’exception C++,
vous ne pouvez pas la redéclencher comme s’il s’agissait d’une exception du
système d’exploitation ou d’une exception VCL ou CLX à moins que vous ne
vous trouviez dans le cadre de pile d’interception.
Gestion des exceptions
12-17
Gestion des exceptions VCL/CLX
Gestion des exceptions VCL et CLX
C++Builder élargit la sémantique pour la gestion des exceptions logicielles
déclenchées à partir de la VCL et CLX ou, de manière équivalente, des
exceptions déclenchées à partir de C++ quand la classe d’exception déclenchée
dérive de TObject. Dans un tel cas, il découle plusieurs règles du fait que les
classes de style VCL peuvent uniquement être allouées sur le tas.
• Les classes d’exception de style VCL ne peuvent être interceptées que par
pointeur, s’il s’agit d’une exception logicielle, ou par référence (cette dernière
possibilité étant préférable).
• Les exceptions de style VCL ne peuvent être déclenchées qu’avec une syntaxe
“par valeur”.
Classes d’exception VCL et CLX
C++Builder comprend un ensemble de classes d’exception incorporées pour gérer
automatiquement les erreurs de division par zéro, les erreurs d’E/S, les
transtypages incorrects et de nombreuses autres conditions d’exception. Toutes
les classes d’exception VCL et CLX dérivent d’un objet racine appelé Exception.
Exception encapsule les propriétés et les méthodes fondamentales de toutes les exceptions
VCL et fournit une interface pour les applications gérant les exceptions.
Vous pouvez transmettre des exceptions à un bloc catch qui prend un paramètre
de type Exception. Pour intercepter des exceptions VCL et CLX, vous devez
utiliser la syntaxe suivante :
catch (exception_class &exception_variable)
Vous devez spécifier la classe d’exception que vous souhaitez intercepter et
fournir une variable servant à référencer l’exception.
Voici un exemple expliquant comment déclencher une exception VCL ou CLX :
void __fastcall TForm1::ThrowException(TObject *Sender)
{
try
{
throw Exception("Une erreur s’est produite");
}
catch(const Exception &E)
{
ShowMessage(AnsiString(E.ClassName())+ E.Message);
}
}
L’instruction throw de l’exemple précédent crée une instance de la classe
Exception et appelle son constructeur. Toutes les exceptions dérivant de Exception
ont la possibilité d’afficher un message, de le transmettre par l’intermédiaire de
constructeurs et de le récupérer via la propriété Message.
12-18
Guide du développeur
Gestion des exceptions VCL/CLX
Les classes d’exception VCL/CLX sont décrites dans le tableau suivant.
Tableau 12.2 Classes d’exception sélectionnées
Classe
d’exception
Description
EAbort
Interrompt une séquence d’événements sans afficher de boîte de
dialogue de message d’erreur.
EAccessViolation
Vérifie la présence d’erreurs d’accès mémoire.
EBitsError
Empêche les tentatives incorrectes d’accès à un tableau booléen.
EComponentError
Signale une tentative incorrecte de recensement ou de modification
du nom d’un composant.
EConvertError
Indique des erreurs de conversion d’objet ou de chaîne.
EDatabaseError
Spécifie une erreur d’accès à une base de données.
EDBEditError
Intercepte des données incompatibles avec un masque spécifié.
EDivByZero
Intercepte des erreurs de division par zéro des entiers.
EExternalException
Indique un code d’exception non reconnu.
EInOutError
Représente une erreur d’E/S de fichier.
EIntOverflow
Spécifie des calculs d’entiers dont les résultats sont trop élevés pour
le registre alloué.
EInvalidCast
Vérifie s’il existe des transtypages incorrects.
EInvalidGraphic
Indique une tentative d’utilisation d’un format de fichier graphique
incorrect.
EInvalidOperation
Se produit lorsque des opérations incorrectes sont tentées sur un
composant.
EInvalidPointer
Se produit suite à des opérations de pointeur incorrect.
EMenuError
Implique un problème dû à un élément de menu.
EOleCtrlError
Détecte des problèmes de liaison avec les contrôles ActiveX.
EOleError
Spécifie des erreurs d’automation OLE.
EPrinterError
Signale une erreur d’impression.
EPropertyError
Se produit suite à des tentatives infructueuses de définition de la
valeur d’une propriété.
ERangeError
Indique que la valeur d’un entier est trop élevée pour le type déclaré
auquel il est affecté.
ERegistryException
Spécifie des erreurs de registre.
EZeroDivide
Intercepte des erreurs de division par zéro de virgule flottante.
Comme vous avez pu le voir dans la liste précédente, les classes d’exception
VCL et CLX incorporées gèrent la plupart des exceptions à votre place et vous
permettent de simplifier la programmation. Dans certaines situations, il est
nécessaire de créer ses propres classes d’exception pour gérer des situations
uniques. Vous pouvez déclarer une nouvelle classe d’exception en créant un
descendant de type Exception et en créant autant de constructeurs que nécessaire
(ou en copiant les constructeurs depuis une classe existant dans Sysutils.hpp).
Gestion des exceptions
12-19
Gestion des exceptions VCL/CLX
Considérations de portabilité
Plusieurs bibliothèques d’exécution (RTL) sont fournies avec C++Builder. La
plupart appartiennent à des applications C++Builder, mais une d’entre elles
(cw32mt.lib) est la RTL multithread normale ne contenant aucune référence à la
VCL/CLX. Cette RTL est fournie pour la gestion des applications existantes
pouvant faire partie d’un projet mais ne dépendant pas de la VCL/CLX. Cette
RTL ne supporte pas l’interception des exceptions du système d’exploitation, car
ces objets exceptions dérivent de TObject et nécessiteraient de lier des parties de
la VCL/CLX à l’application.
La bibliothèque cp32mt.lib (bibliothèque d’exécution multithread) propose une
gestion de la mémoire et une gestion des exceptions avec la VCL/CLX.
Deux bibliothèques d’importation (cw32mti.lib et cp32mti.lib) vous permettent
d’utiliser la DLL RTL. Utilisez la bibliothèque cp32mti.lib pour les exceptions
VCL et CLX.
12-20
Guide du développeur
Chapitre
13
Gestion en langage C++
de VCL et CLX
Chapitre 13
C++Builder influence les possibilités de développement rapide d’applications
(RAD) de la bibliothèque des composants visuels (VCL) et de la bibliothèque des
composants multiplate-formes (CLX) écrites en Pascal Objet. Ce chapitre explique
la manière dont sont implémentés dans C++Builder les caractéristiques,
constructions et concepts Pascal Objet pour gérer la VCL et la CLX. Ce chapitre
est destiné aux programmeurs utilisant les objets VCL dans leurs applications et
aux développeurs créant de nouvelles classes dérivées des classes de la VCL et
de la CLX.
La première partie de ce chapitre compare les modèles objet C++ et Pascal Objet,
décrivant comment C++Builder combine ces deux approches. La deuxième partie
de ce chapitre décrit comment les constructions du langage Pascal Objet ont été
traduites en équivalents C++ dans C++Builder. Elle donne des détails sur les
mots clés ajoutés pour gérer la VCL et la CLX. Certaines de ces extensions,
comme les closures et les propriétés sont des caractéristiques utiles
indépendamment de leur utilisation dans du code VCL ou CLX.
Remarque
Les références aux classes C++ dérivées de TObject désignent les classes dont
TObject est l’ancêtre ultime et pas nécessairement l’ancêtre immédiat. Pour être
cohérent avec les diagnostics du compilateur, ces classes sont également appelées
“classes de style VCL”.
Les modèles objet en C++ et en Pascal Objet
Il y a des différences (certaines évidentes, d’autres moins) dans les modèles de
classe C++ et Pascal Objet. Une différence évidente, c’est l’héritage multiple
qu’autorise le C++ alors que Pascal Objet se limite à un modèle d’héritage
unique. De plus, C++ et Pascal Objet présentent des différences subtiles dans la
manière de créer, d’initialiser, de référencer, de copier ou de détruire des objets.
Gestion en langage C++ de VCL et CLX
13-1
Les modèles objet en C++ et en Pascal Objet
Ces différences et leur impact sur les classes de style VCL dans C++Builder sont
décrites dans cette section.
Héritage et interfaces
A la différence du C++, le Pascal Objet ne supporte pas l’héritage multiple.
Toute classe créée en ayant un ancêtre VCL ou CLX hérite de cette restriction.
Vous ne pouvez donc pas avoir des classes de base multiple pour une classe
C++ de style VCL, même si la classe VCL, ou CLX, n’est pas l’ancêtre immédiat.
Utilisation d’interface à la place de l’héritage multiple
Dans la plupart des situations où vous utiliseriez l’héritage multiple en C++, le
code Pascal Objet utilise des interfaces à la place. Il n’y a pas de construction
C++ correspondant directement au concept d’interface Pascal Objet. Une interface
Pascal Objet se comporte comme une classe sans implémentation. Une interface
ressemble donc à une classe sans données membre et dont toutes les méthodes
sont purement virtuelles. Si une classe Pascal Objet ne peut avoir qu’une seule
classe parent, elle peut gérer plusieurs interfaces. Il est possible dans du code
Pascal Objet d’affecter une instance de classe à une variable du type d’une de
ces interfaces, tout comme vous affectez une instance de classe à une variable
ayant le type d’une des classes ancêtres. Cela permet un comportement
polymorphique dans des classes qui partagent la même interface, même si elles
n’ont pas d’ancêtre commun.
Le compilateur C++Builder reconnaît les classes n’ayant que des méthodes
virtuelles pures et pas de données membres comme des interface Pascal Objet.
Ainsi, quand vous créez une classe de style VCL, vous pouvez utiliser l’héritage
multiple seulement si toutes les classes de base, sauf celles de style VCL ou CLX,
n’ont pas de données membres et uniquement des méthodes virtuelles pures.
Remarque
Les classes interface n’ont pas besoin d’être des classes de style VCL, la seule
contrainte c’est qu’elles n’aient pas de données membres et uniquement des
méthodes virtuelles pures.
Déclaration des classes interfaces
Vous déclarez une classe représentant une interface comme toute autre classe
C++. Néanmoins, en utilisant certaines conventions, vous pouvez expliciter que
cette classe doit agir comme une interface. Ces conventions sont :
• Au lieu d’utiliser le mot clé class, les interfaces sont déclarées en utitilisant
__interface. __interface est une macro qui correspond au mot clé class. Il n’est
pas nécessaire, mais il rend explicite que cette classe doit agir comme une
interface.
• Généralement, le nom des interfaces commence par la lettre ’I’. Par exemple :
IComponentEditor ou IDesigner. En respectant cette convention, vous n’avez pas
à examiner la déclaration de la classe pour savoir si cette classe se comporte
comme une interface.
13-2
Guide du développeur
Les modèles objet en C++ et en Pascal Objet
• Généralement les interfaces ont un GUID associé. Ce n’est pas obligatoire,
mais le code gérant les interfaces s’attend disposer d’un GUID. Vous pouvez
utiliser le modificateur __declspec avec l’argument uuid pour associer une
interface à un GUID. Pour les interfaces, la macro INTERFACE_UUID fait le
même lien.
La déclaration d’interface suivante illustre ces conventions :
__interface INTERFACE_UUID("{C527B88F-3F8E-1134-80e0-01A04F57B270}") IHelloWorld :
public IInterface
{
public:
virtual void __stdcall SayHelloWorld(void) = 0 ;
};
Généralement, lors de la déclaration d’une classe interface, le code C++Builder
déclare également une classe DelphiInterface correspondante qui simplifie
l’utilisation de l’interface :
typedef System::DelphiInterface< IHelloWorld > _di_IHelloWorld;
Pour davantage d’informations sur la classe DelphiInterface, voir “Interfaces
Delphi” à la page 13-21.
IUnknown et IInterface
Toutes les interfaces Pascal Objet dérivent d’un seul et même ancêtre commun,
IInterface. Les classes interface C++ ne sont pas obligées d’utiliser IInterface
comme classe de base, car une classe de style VCL peut l’utiliser comme classe
de base supplémentaire, mais le code VCL et CLX gérant les interfaces suppose
que les méthodes de IInterface sont présentes.
Dans la programmation COM, toutes les interfaces dérivent de IUnknown. La
gestion COM dans la VCL est basée sur une définition de IUnknown qui
correspond directement à IInterface. Donc, en Pascal Objet, IUnknown et IInterface
sont identiques.
Cependant, la définition Pascal Objet de IUnknown ne correspond pas à la
définition IUnknown utilisée dans C++Builder. Le fichier unknwn.h définit
IUnknown pour inclure les trois méthodes suivantes :
virtual HRESULT STDCALL QueryInterface( const GUID &guid, void ** ppv ) = 0;
virtual ULONG STDCALL AddRef() = 0;
virtual ULONG STDCALL Release() = 0;
Cela correspond à la définition de IUnknown indiquée par Microsoft dans la
spécification COM.
Remarque
Pour obtenir des informations sur IUnknown et son utilisation, voir chapitre 38,
“Présentation des technologies COM”, ou la documentation Microsoft.
A la différence du Pascal Objet, la définition en C++Builder de IInterface n’est
pas équivalente à celle de IUnknown. IInterface est un descendant de IUnknown
comportant une méthode supplémentaire, Supports :
template <typename T>
HRESULT __stdcall Supports(DelphiInterface<T>& smartIntf)
Gestion en langage C++ de VCL et CLX
13-3
Les modèles objet en C++ et en Pascal Objet
{
return QueryInterface(__uuidof(T),
reinterpret_cast<void**>(static_cast<T**>(&smartIntf)));
}
Supports vous permet d’obtenir un DelphiInterface pour une autre interface gérée
par l’objet qui implémente IInterface. Si, par exemple vous avez un DelphiInterface
pour une interface IMyFirstInterface et que l’objet d’implémentation implémente
également IMySecondInterface (dont le type DelphiInterface est
_di_IMySecondInterface), vous pouvez obtenir le DelphiInterface de la seconde
interface de la manière suivante :
_di_IMySecondInterface MySecondIntf;
MyFirstIntf->Supports(MySecondIntf);
La VCL et la CLX utilisent un correspondant de IUnknown en Pascal Objet. Cette
correspondance convertit les types utilisés dans les méthodes de IUnknown en
types Pascal Objet et renomme les méthodes AddRef et Release en _AddRef et
_Release pour indiquer qu’elles ne doivent jamais être appelées directement. En
Pascal Objet, le compilateur génère automatiquement les appels nécessaires aux
méthodes de IUnknown. Quand les méthodes de IUnknown (ou de IInterface)
gérées par les objets VCL ou CLX sont converties en C++, cela donne les
signatures de méthode suivantes :
virtual HRESULT __stdcall QueryInterface(const GUID &IID, void *Obj);
int __stdcall _AddRef(void);
int __stdcall _Release(void);
Cela signifie que les objets VCL et CLX gérant IUnknown ou IInterface en Pascal
Objet ne gèrent pas les versions C++Builder de IUnknown et IInterface.
Création de classes gérant IUnknown
De nombreuses classes de la VCL et de la CLX gèrent les interfaces. En fait, la
classe de base de toutes les classes de style VCL, TObject, dispose d’une méthode
GetInterface qui vous permet d’obtenir une classe DelphiInterface pour toute
interface gérée par l’instance d’objet. TComponent et TInterfacedObject
implémentent les méthodes de IInterface (IUnknown). Donc, tout descendant de
TComponent ou de TInterfacedObject que vous créez, hérite automatiquement de la
gestion des méthodes communes à toutes les interfaces Pascal Objet. En Pascal
Objet, quand vous créez un descendant de l’une de ces classes, vous pouvez
gérer une nouvelle interface en n’implémentant que les méthodes introduites par
la nouvelle interface et en vous remettant à l’implémentation par défaut pour les
méthodes héritées de IInterface.
Malheureusement, comme la signature des méthodes de IUnknown et IInterface
n’est pas la même dans C++Builder et dans les classes VCL et CLX, les classes
de style VCL que vous créez ne gèrent pas automatiquement IInterface ou
IUnknown, même si elles dérivent (directement ou pas) de TComponent ou
TInterfacedObject. Donc, pour gérer les interfaces que vous définissez en C++ et
qui dérivent de IUnknown ou de IInterface, vous devez quand même ajouter une
implémentation des méthodes de IUnknown.
13-4
Guide du développeur
Les modèles objet en C++ et en Pascal Objet
Le moyen le plus simple d’implémenter les méthodes de IUnknown dans une
classe dérivant de TComponent ou de TInterfacedObject reste quand même
d’exploiter la gestion intégrée de IUnknown en dépit de la différence des
signatures de fonction. Ajoutez simplement une implémentation de la version
C++ des méthodes de IUnknown qui délèguent aux versions héritées de Pascal
Objet. Par exemple :
virtual HRESULT __stdcall QueryInterface(const GUID& IID, void **Obj)
{
return TInterfacedObject::QueryInterface(IID, (void *)Obj);
}
virtual ULONG __stdcall AddRef()
{
return TInterfacedObject::_AddRef();
}
virtual ULONG __stdcall Release()
{
return TInterfacedObject::_Release();
}
En ajoutant l’implémentation des trois méthodes précédentes à un descendant de
TInterfacedObject, votre classe gère complètement IUnknown ou IInterface.
Classes interfaces et gestion de la durée de vie
Les méthodes IUnknown des classes interface ont des conséquences sur la
manière dont vous allouez et libérez les objets qui implémentent l’interface.
Quand IUnknown est implémentée par un objet COM, l’objet utilise les méthodes
de IUnknown pour suivre le nombre de références à son interface en cours
d’utilisation. Lorsque ce compteur de références tombe à zéro, l’objet se libère
automatiquement. TInterfacedObject implémente les méthodes IUnknown afin
d’effectuer automatiquement le même type de gestion de la durée de vie.
Néanmoins, cette interprétation des méthodes de IUnknown n’est pas strictement
nécessaire. Ainsi, l’implémentation par défaut des méthodes de IUnknown fournie
par TComponent ne tient pas compte du nombre de références à l’interface, le
composant ne se libère donc pas quand ce compteur de références tombe à zéro.
En effet; TComponent s’en remet à l’objet spécifié par sa propriété Owner pour le
libérer.
Certains composants utilisent un mixte de ces deux modèles. Si leur propriété
Owner vaut NULL, ils utilisent le compteur de références sur leur interface pour
la gestion de leur durée de vie, et se libèrent eux-mêmes quand ce compteur de
références tombe à zéro. S’ils ont un propriétaire, ils ne tiennent pas compte du
compteur de références et laissent leur propriétaire les libérer. Attention, que ce
soit pour ces objets hybrides ou pour les autres objets utilisant le compteur de
réferences pour la gestion de durée de vie, les objets ne sont pas
automatiquement libérés si l’application crée l’objet sans jamais en obtenir
d’interface.
Gestion en langage C++ de VCL et CLX
13-5
Les modèles objet en C++ et en Pascal Objet
Identification et instanciation des objets
En C++, une instance d’une classe est un objet effectif. Cet objet peut
directement être manipulé et il est possible d’y accéder indirectement via une
référence ou un pointeur. Par exemple, étant donné une classe C++ CPP_class
utilisant un constructeur sans argument, le code suivant définit des variables
d’instance valides de cette classe :
CPP_class by_value;// un objet de type CPP_class
CPP_class& ref = by_value;// une référence à l’objet by_value ci-dessus
CPP_class* ptr = new CPP_class();// un pointeur sur un objet de type CPP_class
Par contre, en Pascal Objet, une variable de type object désigne toujours l’objet
indirectement. La mémoire de tous les objets est allouée dynamiquement. Par
exemple, étant donné une classe Pascal Objet OP_class
ref: OP_class;
ref := OP_class.Create;
ref est une “référence” à un objet de type OP_class. Traduit en code C++Builder,
cela donnerait :
OP_class* ref = new OP_class;
Différences entre les références C++ et Pascal Objet
La documentation désigne fréquemment une variable d’instance de classe Pascal
Objet sous le terme de référence, mais décrit son comportement comme celui
d’un pointeur. C’est qu’elle a à la fois les propriétés des deux. Une référence
d’objet en Pascal Objet est similaire à un pointeur C++ avec les exceptions
suivantes :
• Une référence Pascal Objet est implicitement déréférencée (elle se comporte
alors davantage comme une référence C++).
• Une référence Pascal Objet n’a pas d’arithmétique de pointeur définie.
Quand on compare une référence Pascal Objet et une référence C++, il y a
également des similitudes et des différences. Les références sont déréférencées
implicitement dans les deux langages, mais :
• Une référence Pascal Objet peut être réassociée alors qu’une référence C++ ne
peut pas l’être.
• Une référence Pascal Objet peut valoir nil, alors qu’une référence C++ doit
désigner un objet valide.
Certaines décisions de conception sous-jacentes au cadre VCL et CLX dépendent
de l’utilisation de ce type de variable d’instance. Un pointeur est la construction
du langage C++ la plus proche d’une référence en Pascal Objet. De ce fait,
presque tous les identificateurs d’objet VCL et CLX sont traduits en pointeurs
C++ dans C++Builder.
Remarque
13-6
Les paramètres Pascal Objet de type var correspondent assez précisément aux
références en C++. Pour davantage d’informations sur les paramètres var, voir
“Paramètres var” à la page 13-17.
Guide du développeur
Les modèles objet en C++ et en Pascal Objet
Copie d’objets
A la différence de C++, Pascal Objet ne dispose pas d’une gestion par le
compilateur de la copie d’objets. Cette section décrit les effets de cette différence
sur les opérateurs d’affectation et les constructeurs de copie dans les classes de
style VCL.
Opérateurs d’affectation
L’opérateur d’affectation Pascal Objet (:=) n’est pas un opérateur d’affectation de
classe (opérateur=()). L’opérateur d’affectation copie la référence et non l’objet.
Ainsi, dans le code suivant, B et C désignent tout deux le même objet :
B, C: TButton;
B:= TButton.Create(ownerCtrl);
C:= B;
Cet exemple se traduit dans le code C++Builder suivant :
TButton *B = NULL;
TButton *C = NULL;
B = new TButton(ownerCtrl);
C = B; // copie le pointeur et pas l’objet
Les classes de style VCL en C++Builder suivent les règles du langage Pascal
Objet pour les opérateurs d’affectation. Cela signifie que dans le code suivant, les
affectations entres les pointeurs déréférencés ne sont pas valides car elles tentent
de copier l’objet et pas le pointeur :
TVCLStyleClass *p = new TVCLStyleClass;
TVCLStyleClass *q = new TVCLStyleClass;
*p = *q; // interdit pour les objets des classes de style VCL
Remarque
Pour les classes de style VCL, il est légal d’utiliser une syntaxe C++ pour lier à
une référence. Ainsi, le code suivant est légal :
TVCLStyleClass *ptr = new TVCLStyleClass;
TVCLStyleClass &ref = *ptr;// OK pour les classes de style VCL
Bien que cela soit différent de l’utilisation d’un opérateur d’affectation, la syntaxe
est suffisamment semblable pour la mentionner ici à titre de comparaison.
Constructeurs de copie
Pascal Objet n’a pas de constructeurs de copie intégrés. De ce fait, les classes de
style VCL de C++Builder n’ont pas de constructeurs de copie prédéfinis.
L’exemple de code suivant tente de créer un pointeur TButton en utilisant un
constructeur de copie :
TButton *B = new TButton(ownerCtrl);
TButton *C = new TButton(*B);// interdit pour les objets des classes de style VCL
N’écrivez pas de code dépendant de l’existence d’un constructeur de copie pour
les classes de style VCL et CLX. Pour créer une copie d’un objet d’une classe de
style VCL avec C++Builder, vous pouvez écrire le code d’une fonction membre
copiant cet objet. Sinon, les descendants de la classe VCL et CLX TPersistent
peuvent redéfinir la méthode Assign pour copier les données d’un objet dans un
autre. Cela se fait, par exemple, dans les classes graphiques comme TBitmap ou
Gestion en langage C++ de VCL et CLX
13-7
Les modèles objet en C++ et en Pascal Objet
TIcon qui contiennent des ressources image. Enfin, la manière de copier un objet
peut être déterminée par le programmeur (le concepteur de composants) ; mais il
faut faire attention car certaines méthodes de copie utilisées en C++ standard ne
sont pas utilisables pour les classes de style VCL.
Utilisation d’objets comme arguments de fonction
Comme indiqué, les instances de variables diffèrent en C++ et en Pascal Objet.
Vous devez en tenir compte quand vous transmettez des objets comme
arguments de fonctions. En C++, les objets peuvent être transmis aux fonctions
par valeur, par référence ou par pointeur. Dans Pascal Objet, quand un objet est
transmis par valeur à une fonction, souvenez-vous que l’argument objet est déjà
une référence à un objet. C’est donc la référence qui est transmise par valeur et
non pas l’objet lui-même. Il n’y a pas d’équivalent en Pascal Objet au transfert
par valeur réelle d’un objet comme cela peut se faire en C++. Les objets de style
VCL transmis à des fonctions suivent les règles Pascal Objet.
Construction d’objets en C++Builder pour les classes VCL/CLX
Le C++ et le Pascal Objet construisent différemment les objets. Cette section
aborde cette question et décrit comment C++Builder combine les deux approches.
Construction d’objets en C++
En C++ standard, l’ordre de construction va des classes virtuelles de base,
suivies des classes de base et enfin de la classe dérivée. La syntaxe C++ utilise la
liste d’initialisation du constructeur pour appeler les constructeurs des classes de
base. Le type à l’exécution de l’objet est celui de la classe dont c’est le
constructeur qui est appelé. La répartition des méthodes virtuelles respecte le
type à l’exécution de l’objet et change en conséquence pendant la construction.
Construction d’objets Pascal Objet
En Pascal Objet, seul le constructeur de la classe instanciée est appelé de manière
certaine ; cependant la mémoire des classes de base est allouée. La construction
de chaque classe de base immédiate est assurée en appelant inherited dans le
constructeur de la classe dérivée correspondante. Par convention, les classes VCL
et CLX utilisent inherited pour appeler les constructeurs (non vides) des classes
de base. Attention, car ce n’est pourtant pas une règle impérative du langage.
Le type à l’exécution de l’objet est immédiatement déterminé comme étant celui
de la classe instanciée et ne change pas quand les constructeurs des classes de
base sont appelés. La répartition des méthodes virtuelles s’effectue en fonction
du type à l’exécution de l’objet et ne change donc pas au cours de la
construction.
Construction des objets C++Builder
Les objets de style VCL sont construits comme des objets Pascal Objet mais avec
une syntaxe C++. Cela signifie que la méthode et l’ordre d’appel des
constructeurs des classes de base respectent la syntaxe C++ en utilisant la liste
13-8
Guide du développeur
Les modèles objet en C++ et en Pascal Objet
d’initialisation de toutes les classes de base non VCL ou CLX et le premier
ancêtre immédiat VCL ou CLX. Cette classe de base VCL ou CLX est la première
classe construite. Elle construit, de manière optionnelle, sa propre classe de base
en utilisant inherited selon la méthode Pascal Objet. Cependant, les classes de
base VCL et CLX sont construites dans l’ordre inverse de celui attendu en C++.
Toutes les classes de base C++ sont ensuite construites en partant de l’ancêtre le
plus éloigné jusqu’à la classe dérivée. Le type à l’exécution de l’objet et la
répartition des méthodes virtuelles est de type Pascal Objet.
La figure suivante illustre la construction d’une instance d’une classe de style
VCL, MyDerived, qui dérive de MyBase, descendant direct de TWinControl.
MyDerived et MyBase sont implémentées en C++. TWinControl est une classe VCL
implémentée en Pascal Objet.
Figure 13.1 Ordre de construction des objets de style VCL
Héritage
Ordre de construction
TObject
TWinControl
(constructeur appelant
inherited)
TPersistent
TControl
(constructeur appelant
inherited)
TComponent
TComponent
TControl
TPersistent
(pas de constructeur)
TWinControl
TPersistent
(constructeur vide)
Frontière
C++ / Pascal Objet
MyBase
MyBase
MyDerived
MyDerived
Remarquez comment l’ordre de construction peut sembler aller à l’inverse de
celui attendu par un programmeur C++, car il va de l’ancêtre immédiat jusqu’à
TObject pour les classes purement VCL et CLX, puis construit MyBase et enfin la
classe dérivée.
Gestion en langage C++ de VCL et CLX
13-9
Les modèles objet en C++ et en Pascal Objet
Remarque
TComponent n’appelle pas inherited car TPersistent n’a pas de constructeur.
TObject a un constructeur vide, il n’est donc pas appelé. Si les constructeurs de
ces classes étaient appelés, l’ordre suivrait le schéma dans lequel ces classes
apparaissent en gris.
Les modèles de construction d’objet en C++, Pascal Objet et C++Builder sont
résumés dans le tableau suivant :
Tableau 13.1 Comparaison des modèles objet
C++
Pascal Objet
C++Builder
Le constructeur de la classe
instancié est le premier et
seul constructeur appelé
automatiquement. Si d’autres
classes sont construites, elles
le sont depuis l’extrémité
jusqu’à la racine.
La classe de base VCL ou
CLX la plus proche, puis la
construction suit le modèle
Pascal Objet, enfin la
construction suit le modèle
C++ (à cette différence que
les classes de base virtuelles
ne sont pas autorisées).
Ordre de construction
Les classes de base virtuelles
puis les classes de base et
enfin la classe dérivée.
Méthode d’appel des constructeurs de la classe de base
Automatiquement à partir de
la liste d’initialisation du
constructeur.
De manière optionnelle et
explicitement n’importe où
dans le corps du constructeur
de la classe dérivée en
utilisant le mot clé inherited.
Automatiquement à partir de
la liste d’initialisation du
constructeur jusqu’au
constructeur de la classe de
base VCL ou CLX la plus
proche. Ensuite, suivant la
méthode Pascal Objet en
appelant les constructeurs
avec inherited.
Type à l’exécution de l’objet pendant sa construction
Change en fonction du type
de constructeur de classe en
cours.
Défini immédiatement
comme étant celui de la
classe instanciée.
Défini immédiatement
comme étant celui de la
classe instanciée.
Respecte le type à l’exécution
de l’objet qui reste le même
tout au long de l’appel de
tous les constructeurs de
classe.
Respecte le type à l’exécution
de l’objet qui reste le même
tout au long de l’appel de
tous les constructeurs de
classe.
Répartition de méthode virtuelle
Change en fonction du type
à l’exécution de l’objet au fil
de l’appel des constructeurs
des classes de base.
La section suivante décrit la signification de ces différences.
Appels de méthodes virtuelles dans les constructeurs des classes
de base
Les méthodes virtuelles appelées depuis le corps des constructeurs des classes de
base VCL ou CLX, c’est-à-dire les classes implémentées en Pascal Objet, sont
réparties comme en C++, selon le type à l’exécution de l’objet. Comme
C++Builder combine le modèle Pascal Objet d’affectation immédiat du type
13-10
Guide du développeur
Les modèles objet en C++ et en Pascal Objet
d’exécution d’un objet avec le modèle C++ de construction des classes de base
avant la construction de la classe dérivée, l’appel des méthodes virtuelles dans
les constructeurs des classes de base dans les classes de style VCL peut induire
des effets secondaires indésirables. Ces effets sont décrits plus loin et illustrés
par un exemple d’une classe instanciée dérivée d’au moins une classe de base.
Dans cette discussion, la classe instanciée est désignée comme étant la classe
dérivée.
Modèle Pascal Objet
En Pascal Objet, les programmeurs peuvent utiliser le mot clé inherited qui leur
permet d’appeler le constructeur de la classe de base n’importe où dans le corps
du constructeur d’une classe dérivée. En conséquence, si la classe dérivée
redéfinit une méthode virtuelle dépendant de la configuration de l’objet ou de
l’initialisation de données membres, cela peut avoir lieu avant l’appel du
constructeur de la classe de base et l’appel des méthodes virtuelles.
Modèle C++
La syntaxe C++ ne dispose pas du mot clé inherited pour appeler le
constructeur de la classe de base lors de la construction de la classe dérivée.
Dans le modèle C++, l’utilisation de inherited est inutile puisque le type à
l’exécution de l’objet est celui de la classe en cours de construction et pas celui
de la classe dérivée. Donc, les méthodes virtuelles appelées sont celles de la
classe en cours et pas celles de la classe dérivée. De ce fait, il n’est pas nécessaire
d’initialiser les données membres ou de configurer l’objet de la classe dérivée
avant d’appeler ces méthodes.
Modèle C++Builder
En C++Builder, les objets de style VCL ont le type à l’exécution de la classe
dérivée durant les appels aux constructeurs de classe de base. Donc, si le
constructeur de la classe de base appelle une méthode virtuelle, la méthode de la
classe dérivée est appelée si elle a été redéfinie par la classe dérivée. Si la
méthode virtuelle dépend de la liste d’initialisation ou du corps du constructeur
de la classe dérivée, la méthode est appelée avant que cela ne se produise. Par
exemple, CreateParams est une fonction membre virtuelle qui est appelée
indirectement dans le constructeur de TWinControl. Si vous dérivez une classe de
TWinControl et redéfinissez CreateParams afin qu’elle dépende de quelque chose
effectué dans votre constructeur, ce code est traité après l’appel de CreateParams.
Cette situation est valable pour toutes les classes dérivées d’une base. Supposons
qu’une classe C dérive de B qui dérive de A. Quand une instance C est créée, A
doit également appeler la méthode redéfinie de B si B redéfinit la méthode alors
que C ne le fait pas.
Remarque
Il faut faire attention aux méthodes virtuelles comme CreateParams qui ne sont
pas appelées directement dans les constructeurs mais qui le sont indirectement.
Exemple : appel de méthodes virtuelles
L’exemple suivant compare des classes C++ et de style VCL qui redéfinissent des
méthodes virtuelles. Cet exemple illustre la manière dont les appels à ces
Gestion en langage C++ de VCL et CLX
13-11
Les modèles objet en C++ et en Pascal Objet
méthodes dans les constructeurs des classes de base sont résolus dans les deux
cas. MyBase et MyDerived sont des classes C++ standard. MyVCLBase et
MyVCLDerived sont des classes de style VCL descendant de TObject. La méthode
virtuelle what_am_I() est redéfinie dans les deux classes dérivées mais elle est
appelée uniquement dans le constructeur de la classe de base et pas dans le
constructeur de la classe dérivée.
#include <sysutils.hpp>
#include <iostream.h>
// Classes de style non VCL
class MyBase {
public:
MyBase() { what_am_I(); }
virtual void what_am_I() { cout << "Je suis une base" << endl; }
};
class MyDerived : public MyBase {
public:
virtual void what_am_I() { cout << "Je suis dérivée" << endl; }
};
// Classes de style VCL
class MyVCLBase : public TObject {
public:
__fastcall MyVCLBase() { what_am_I(); }
virtual void __fastcall what_am_I() { cout << "Je suis une base" << endl; }
};
class MyVCLDerived : public MyVCLBase {
public:
virtual void __fastcall what_am_I() { cout << "Je suis dérivée" << endl; }
};
int main(void)
{
MyDerived d; // instanciation de la classe C++
MyVCLDerived *pvd = new MyVCLDerived; // instanciation de la classe de style VCL
return 0;
}
Le résultat de cet exemple est :
Je suis une base
Je suis dérivée
Il est dû à la différence des types d’exécution de MyDerived et de MyVCLDerived
lors de l’appel à leur constructeur respectif de classe de base.
Initialisation par le constructeur des données membres pour les fonctions
virtuelles
Comme les données membres peuvent être utilisées dans les fonctions virtuelles,
il est important de savoir quand et comment elles sont initialisées. En Pascal
Objet, toutes les données non initialisées, le sont avec des zéros. Cela s’applique,
par exemple, aux classes de base dont les constructeurs ne sont pas appelés avec
13-12
Guide du développeur
Les modèles objet en C++ et en Pascal Objet
inherited. En C++ standard, la valeur des données membres non initialisées est
indéterminée. Les types suivants de données membres de classe doivent être
initialisés dans la liste d’initialisation du constructeur de la classe :
• Les références.
• Les données membres n’ayant pas de constructeur par défaut.
Sinon, la valeur de ces données membres ou de celles initialisées dans le corps
du constructeur est non définie quand les constructeurs de classe de base sont
appelés. En C++Builder, la mémoire des classes de style VCL est initialisée à
zéro.
Remarque
Techniquement parlant c’est la mémoire des classes VCL ou CLX qui est à zéro,
c’est-à-dire les bits qui sont à zéro. Les valeurs elle-mêmes sont indéfinies. Par
exemple, une référence est un zéro.
Une fonction virtuelle dépendant de la valeur de variables membres initialisées
dans le corps du constructeur ou dans la liste d’initialisation peuvent se
comporter comme si les variables sont initialisées à zéro. En effet, le constructeur
de la classe de base est appelé avant le traitement de la liste d’initialisation ou
l’entrée dans le corps du constructeur. Cela est illustré par l’exemple suivant :
#include <sysutils.hpp>
class Base : public TObject {
public:
__fastcall Base() { init(); }
virtual void __fastcall init() { }
};
class Derived : public Base {
public:
Derived(int nz) : not_zero(nz) { }
virtual void __fastcall init()
{
if (not_zero == 0)
throw Exception("not_zero vaut zéro!");
}
private:
int not_zero;
};
int main(void)
{
Derived *d42 = new Derived(42);
return 0;
}
Cet exemple déclenche une exception dans le constructeur de Base. Comme Base
est construite avant Derived, not_zero n’a pas encore été initialisé avec la valeur 42
transmise au constructeur. Attention : vous ne pouvez pas initialiser les données
membres des classes de style VCL avant l’appel des constructeurs de classe
de base.
Gestion en langage C++ de VCL et CLX
13-13
Les modèles objet en C++ et en Pascal Objet
Destruction d’objets
Deux mécanismes diffèrent dans la destruction d’objets en C++ et en Pascal
Objet : Ce sont :
• Les destructeurs appelés à cause d’exceptions déclenchées dans les
constructeurs.
• Les méthodes virtuelles appelées dans les destructeurs.
Les classes de style VCL combinent les méthodes de ces deux langages.
Les sections suivantes décrivent ces points plus en détail.
Exceptions déclenchées dans les constructeurs
Les destructeurs ne sont pas appelés de la même manière en C++ et en Pascal
Objet quand une exception est déclenchée pendant la construction de l’objet.
Par exemple, la classe C dérive de la classe B qui dérive de la classe A :
class A
{
// corps
};
class B: public A
{
// corps
};
class C: public B
{
// corps
};
Si une exception est déclenchée dans le constructeur de la classe B lors de la
construction d’une instance de C. Le résultat obtenu dans les classes C++, Pascal
Object et de style VCL est décrit ci-dessous :
• En C++, les destructeurs des données membres de l’objet B complètement
construites sont appelés puis le destructeur de A est appelé et enfin les
destructeurs des données membres de A complètement construites. Par contre
les destructeurs de B et C ne sont pas appelés.
• En Pascal Objet, seul le destructeur de la classe instanciée est appelé
automatiquement. C’est donc le destructeur de C. Comme pour les
constructeurs, c’est au programmeur d’appeler inherited dans les destructeurs.
Dans cet exemple, si on suppose que tous les destructeurs appellent inherited,
on a donc dans l’ordre l’appel des destructeurs de C, B et A. De plus, que le
mot clé inherited soit ou non déjà appelé dans le constructeur de B avant que
l’exception n’arrive, le destructeur de A est appelé car inherited a été appelé
dans le destructeur de B. L’appel du destructeur de A ne dépend pas du fait
que son constructeur ait ou non été appelé. De plus, comme fréquemment les
constructeurs commencent par appeler inherited, le destructeur de C est
appelé même si la totalité de son constructeur n’a pas été exécutée.
13-14
Guide du développeur
Les modèles objet en C++ et en Pascal Objet
• Dans les classes de style VCL, les classes de base VCL ou CLX (celles
implémentées en Pascal Objet) respectent la méthode Pascal Objet d’appel des
destructeurs. Les classes dérivées (celles implémentées en C++) ne suivent
aucune des deux méthodes précédentes. Tous les destructeurs sont appelés ;
mais l’exécution ne passe pas par le corps de ceux qui n’auraient pas été
appelés en appliquant les règles de langage C++.
Les classes implémentées en Pascal Objet offrent l’opportunité de mettre en
œuvre du code de nettoyage placé dans le corps du destructeur. Cela inclut le
code libérant la mémoire des sous-objets (c’est-à-dire des données membres qui
sont des objets) construits avant que l’exception se soit produite. Attention : pour
les classes de style VCL, le code de nettoyage peut ne pas être exécuté pour la
classe instanciée ou pour ses bases implémentées en C++ même si les
destructeurs sont appelés.
Pour davantage d’informations sur la gestion des exceptions en C++Builder, voir
“Gestion des exceptions VCL/CLX” à la page 12-16.
Méthodes virtuelles appelées dans les destructeurs
La répartition des méthodes virtuelles dans les destructeurs fonctionne comme
dans les constructeurs. Cela signifie que pour les classes de style VCL, la classe
dérivée est détruite d’abord, mais le type à l’exécution de l’objet reste celui de la
classe dérivée durant les appels ultérieurs aux destructeurs des classes de base.
Donc, si des méthodes virtuelles sont appelées dans les destructeurs des classes
de base VCL ou CLX, vous répartissez potentiellement vers une classe qui s’est
déjà détruite elle-même.
AfterConstruction et BeforeDestruction
TObject introduit deux méthodes virtuelles, BeforeDestruction et AfterConstruction,
qui permettent aux programmeurs d’écrire du code traité avant la destruction et
après la création d’objets. AfterConstruction est appelée après l’appel du dernier
constructeur. BeforeDestruction est appelée avant l’appel du premier destructeur.
Ces méthodes sont publiques et appelées automatiquement.
Fonctions virtuelles de classe
Pascal Objet dispose du concept de fonction virtuelle de classe. L’équivalent, si
cela était possible, en C++ serait une fonction virtuelle statique ; mais le C++ n’a
pas d’équivalent exact de ce type de fonction. Ces fonctions sont appelées sans
problème à l’intérieur de la VCL ou de la CLX. Par contre, vous ne devez jamais
appeler une fonction de ce type dans C++Builder. Vous pouvez identifier ces
fonctions dans les fichiers d’en-tête car elles sont précédées du commentaire
suivant :
/* virtual class method */
Gestion en langage C++ de VCL et CLX
13-15
Gestion des types de données et des concepts du langage Pascal Objet
Gestion des types de données et des concepts du langage
Pascal Objet
Pour manipuler la VCL et la CLX, C++Builder implémente, traduit ou adapte
dans le langage C++ la plupart des types de données, constructions et concepts
du Pascal Objet.Cela s’effectue en utilisant les techniques suivantes :
•
•
•
•
•
Typedefs en types C++ natifs
Classes, structures et modèles de classes
Equivalents en langage C++
Macros
Mots clés qui sont des extensions de la définition ANSI du langage
Tous les aspects du langage Pascal objet n’ont pas un équivalent simple en C++.
Parfois, l’utilisation de ces éléments du langage peut produire des résultats
imprévisibles de l’application. Par exemple :
• Certains types existent en Pascal Objet et en C++ mais avec une définition
différente. Cela nécessite des précautions quand du code est partagé par ces
deux langages.
• Certaines extensions ont été ajoutées au Pascal Objet afin de gérer C++Builder.
Dans certains cas, elles peuvent influer de manière subtile sur
l’interopérabilité.
• Les types et les structures du langage Pascal Objet n’ayant pas de
correspondant dans le langage C++ doivent être évités dans C++Builder pour
partager du code entre ces langages.
Cette section est un résumé de la manière dont C++Builder implémente et
correspond au langage Pascal Objet et les situations où il convient d’être
prudent.
Typedefs
La plupart des types de données intrinsèques du Pascal Objet sont implémentés
dans C++Builder en utilisant typedef dans un type C++ natif. Ils se trouvent
dans sysmac.h. Dans la mesure du possible, vous devez utiliser le type C++ de
préférence au type Pascal Objet.
Classes gérant le langage Pascal Objet
Certains types de données et structures du langage Pascal Objet n’ont pas
d’équivalent C++ et sont implémentés sous la forme de classes ou de structures.
Des modèles de classe sont également employés pour implémenter des types de
données ou des structures du langage Pascal Objet comme set, à partir duquel
un type spécifique peut être déclaré. Leurs déclarations se trouvent dans les
fichiers d’en-tête suivants :
• dstring.h
13-16
Guide du développeur
Gestion des types de données et des concepts du langage Pascal Objet
•
•
•
•
•
•
•
•
•
•
•
wstring.h
sysclass.h
syscomp.h
syscurr.h
sysdyn.h
sysopen.h
sysset.h
systdate.h
systobj.h
systvar.h
sysvari.h
Les classes implémentées dans ces fichiers d’en-tête sont créées pour gérer des
types natifs utilisés dans des routines du Pascal Objet. Elles sont également
utilisables pour appeler ces routines dans du code de base VCL et CLX.
Equivalents dans le langage C++ du langage Pascal Objet
Les paramètres var et sans type du Pascal Objet n’existent pas en C++. Les deux
ont des équivalents dans le langage C++ utilisés dans C++Builder.
Paramètres var
Le C++ et le Pascal Objet ont tous les deux le concept de “transfert par
référence” (appelé également transfert par adresse). Ce sont des arguments
modifiables. En Pascal Objet ce sont des paramètres var. La syntaxe des fonctions
attendant un paramètre var est :
procedure myFunc(var x : Integer);
En C++, il faut transmettre ce type de paramètre par référence :
void myFunc(int& x);
Les références C++ et les pointeurs peuvent s’utiliser pour modifier l’objet.
Cependant, une référence correspond mieux à un paramètre var car, à la
différence d’un pointeur, une référence ne peut être réassociée et un paramètre
var ne peut être réaffecté ; toutefois les deux permettent de modifier la valeur de
ce qui est référencé.
Paramètres sans type
Pascal Objet autorise les paramètres de type non spécifié. Ces paramètres sont
transmis aux fonctions sans type défini. La fonction réceptrice peut transtyper le
paramètre dans un type connu avant de l’utiliser. C++Builder interprète les
paramètres sans type comme des pointeurs sur un void (void *). La fonction
réceptrice doit transtyper le pointeur void en un pointeur du type désiré. Par
exemple :
int myfunc(void* MyName)
{
// Transtype le pointeur dans le type correct puis le déréférence.
int* pi = static_cast<int*>(MyName);
Gestion en langage C++ de VCL et CLX
13-17
Gestion des types de données et des concepts du langage Pascal Objet
return 1 + *pi;
}
Tableaux ouverts
Pascal Objet dispose de la construction “tableau ouvert” qui permet de
transmettre un tableau de taille indéterminé à une fonction. Bien qu’il n’y ait pas
d’équivalent direct de ce type en C++, il est possible d’appeler une fonction
Pascal Objet qui attend un paramètre tableau ouvert en lui transmettant
explicitement un pointeur sur le premier élément du tableau et la valeur du
dernier indice (le nombre d’éléments du tableau moins un). Par exemple, la
fonction Mean de math.hpp a la déclaration suivante en Pascal Objet :
function Mean(Data: array of Double): Extended;
La déclaration C++ est :
Extended __fastcall Mean(const double * Data, const int Data_Size);
Le code suivant illustre l’appel de la fonction Mean en C++ :
double d[] = { 3.1, 4.4, 5.6 };
// spécification explicite du dernier indice
long double x = Mean(d, 2);
// mieux : utiliser sizeof pour s’assurer que la valeur correcte est transmise
long double y = Mean(d, (sizeof(d) / sizeof(d[0])) - 1);
// utiliser la macro définie dans sysopen.h
long double z = Mean(d, ARRAYSIZE(d) - 1);
Remarque
Dans des cas similaires à cet exemple mais dans lesquels la fonction Pascal Objet
attend un paramètre var, la déclaration des paramètres de la fonction en C++ ne
serait pas const.
Calcul du nombre d’éléments
Quand vous utilisez sizeof(),, la macro ARRAYSIZE ou la macro
EXISTINGARRAY pour calculer le nombre d’éléments d’un tableau, faites
attention à ne pas utiliser à la place un pointeur sur le premier élément du
tableau. Il faut transmettre à la place le nom d’un tableau :
double d[] = { 3.1, 4.4, 5.6 };
int n = ARRAYSIZE(d); // sizeof(d)/sizeof(d[0]) => 24/8 => 3
double *pd = d;
int m = ARRAYSIZE(pd); // sizeof(pd)/sizeof(pd[0]) => 4/8 => 0 => Erreur!
Utiliser la taille (“sizeof”) d’un tableau ne revient pas au même que d’utiliser la
taille d’un pointeur. Par exemple, étant donné les déclarations suivantes :
double d[3];
double *p = d;
Utiliser la taille du tableau comme suit :
sizeof(d)/sizeof d[0]
13-18
Guide du développeur
Gestion des types de données et des concepts du langage Pascal Objet
ne donne pas le même résultat que la taille du pointeur :
sizeof(p)/sizeof(p[0])
Cet exemple et ceux qui suivent utilisent la macro ARRAYSIZE à la place de
l’opérateur sizeof(). Pour davantage d’informations sur la macro ARRAYSIZE,
voir l’aide en ligne.
Temporaires
Pascal Objet gère le transfert de tableaux ouverts temporaires sans nom à des
fonctions. Il n’y a pas de syntaxe équivalente en C++. Cependant comme des
définitions de variable peuvent être mélangées à d’autres instructions, une
approche consiste à fournir la variable avec un nom.
En Pascal Objet :
Result := Mean([3.1, 4.4, 5.6]);
En C++, en utilisant une variable “temporaire” nommée :
double d[] = { 3.1, 4.4, 5.6 };
return Mean(d, ARRAYSIZE(d) - 1);
Pour limiter la portée de la variable “temporaire” nommée et éviter des conflits
avec des variables locales, il suffit de créer sur place une nouvelle portée :
long double x;
{
double d[] = { 4.4, 333.1, 0.0 };
x = Mean(d, ARRAYSIZE(d) - 1);
}
Pour d’autres solutions, voir “Macro OPENARRAY” à la page 13-20.
Tableau de constantes
Le Pascal Objet gère une construction appelée array of const. Ce type
d’argument revient à utiliser par valeur un tableau ouvert de TVarRec.
Le segment de code suivant déclare en Pascal Objet une fonction attendant un
array of const :
function Format(const Format: string; Args: array of const): string;
En C++, le prototype est ;
AnsiString __fastcall Format(const AnsiString Format,
TVarRec const *Args, const int Args_Size);
La fonction est appelée comme n’importe quelle fonction attendant un tableau
ouvert :
void show_error(int error_code, AnsiString const &error_msg)
{
TVarRec v[] = { error_code, error_msg };
ShowMessage(Format("%d: %s", v, ARRAYSIZE(v) - 1));
}
Gestion en langage C++ de VCL et CLX
13-19
Gestion des types de données et des concepts du langage Pascal Objet
Macro OPENARRAY
La macro OPENARRAY définie dans sysopen.h peut être également utilisée au
lieu de définir une variable nommée pour transmettre un tableau ouvert
temporaire à une fonction qui attend un tableau ouvert transféré par valeur.
Cette macro s’utilise de la manière suivante :
OPENARRAY(T, (value1, value2, value3)) // Jusqu’à 19 valeurs
Où T est le type du tableau ouvert à construire, les paramètres value servant à
remplir le tableau. Les parenthèses autour des paramètres value sont obligatoires.
Par exemple :
void show_error(int error_code, AnsiString const &error_msg)
{
ShowMessage(Format("%d: %s", OPENARRAY(TVarRec, (error_code, error_msg))));
}
Il est possible de transmettre jusqu’à 19 valeurs en utilisant la macro
OPENARRAY. Si un tableau plus important est nécessaire, il faut définir une
variable explicite. De plus, l’utilisation de la macro OPENARRAY entraîne un
certain coût (mais faible) à l’exécution en raison de l’allocation du tableau sousjacent et d’une copie supplémentaire de chaque valeur.
Macro EXISTINGARRAY
La macro EXISTINGARRAY définie dans sysopen.h peut être utilisée pour
transmettre un tableau existant quand un tableau ouvert est attendu. Cette macro
s’utilise de la manière suivante :
long double Mean(const double *Data, const int Data_Size);
double d[] = { 3.1, 3.14159, 2.17128 };
Mean(EXISTINGARRAY (d));
Remarque
La section “Calcul du nombre d’éléments” à la page 13-18 s’applique également à
la macro EXISTINGARRAY.
Fonctions C++ attendant un argument tableau ouvert
Quand vous écrivez une fonction C++ qui reçoit un tableau ouvert de Pascal
Objet, il est important de gérer explicitement la sémantique “transfert par
valeur”. En particulier, si la déclaration de la fonction correspond à un “transfert
par valeur”, assurez-vous de copier explicitement chaque élément avant de les
modifier. En Pascal Objet, un tableau ouvert est un type prédéfini et peut être
transmis par valeur. En C++, le type tableau ouvert étant implémenté en utilisant
un pointeur, il est donc possible de modifier le tableau original sauf si vous en
faites une copie.
Types définis différemment
Les types définis différemment en Pascal Objet et en C++ ne sont normalement
pas causes de problèmes. Les rares cas problématiques peuvent s’avérer subtiles.
Pour cette raison, ces types sont évoqués dans cette section.
13-20
Guide du développeur
Gestion des types de données et des concepts du langage Pascal Objet
Types de données booléens
La valeur True des types de données ByteBool, WordBool et LongBool du Pascal
Objet est représentée par -1 en Pascal Objet. False est représenté par 0.
Remarque
Le type de données Boolean reste inchangé (True = 1, False = 0).
Même si le type de données bool du type C++ convertit correctement ces types
Pascal Objet, il y a un problème lors du partage de fonctions WinAPI ou de
toute autre fonction utilisant le type BOOL de Windows qui est représenté par 1.
Si une valeur est transmise à un paramètre de type BOOL, il est évalué en –1
pour le Pascal Objet et en 1 pour le C++. Donc, si vous partagez du code entre
ces deux langages, la comparaison de deux identificateurs échoue sauf si les
deux valent 0 (False, false). Pour contourner ce problème, utilisez la méthode de
comparaison suivante :
!A == !B;
Le tableau suivant montre les résultats de l’utilisation de cette méthode de test
d’égalité :
Tableau 13.2 Test d’égalité !A == !B de variables BOOL
Pascal Objet
C++
!A == !B
0 (False)
0 (false)
!0 == !0 (TRUE)
0 (False)
1 (true)
!0 == !1 (FALSE)
–1 (True)
–1 (True)
0 (false)
!–1 == !0 (FALSE)
1 (true)
!–1 == !1 (TRUE)
En utilisant cette méthode de comparaison, le résultat obtenu est correct pour
toutes les valeurs.
Types de données Char
Le type char du C++ est un type signé alors que c’est un type non signé en
Pascal Objet. Il est très peu probable que se produise une situation dans laquelle
cette différence pose problème pour partager du code.
Interfaces Delphi
Le compilateur Pascal Objet gère automatiquement de nombreux détails de la
manipulation des interfaces. Il augmente automatiquement le compteur des
références à une interface quand le code de l’application obtient un pointeur
d’interface et le décrémente quand l’interface sort de la portée.
Dans C++Builder, le modèle de classe DelphiInterface fournit certains de ces
services aux classes interface C++. Pour les propriétés et méthodes de la VLC ou
de la CLX qui utilisent les types d’interface Pascal Objet, l’équivalent C++ utilise
un DelphiInterface construit en utilisant la classe interface sous-jacente.
Le constructeur, le constructeur de copie, l’opérateur d’affectation et le
destructeur de DelphiInterface incrémentent ou décrémentent le compteur de
Gestion en langage C++ de VCL et CLX
13-21
Gestion des types de données et des concepts du langage Pascal Objet
références quand cela est nécessaire. Cependant, DelphiInterface n’est pas aussi
pratique que la gestion des interfaces par le compilateur en Pascal Objet.
D’autres opérateurs donnant accès au pointeur d’interface sous-jacent ne gèrent
pas le compteur de références car la classe ne sait pas toujours si c’est approprié.
Il peut être nécessaire d’appeler explicitement AddRef ou Release pour conserver
un compteur de références correct.
Chaînes de ressource
Si vous avez du code dans une unité Pascal qui utilise des chaînes de ressource,
le compilateur Pascal (DCC32) génère une variable globale et une macro du
préprocesseur correspondante pour chaque chaîne de ressource quand il génère
le fichier d’en-tête. Les macros sont utilisées pour charger automatiquement les
chaînes de ressource et doivent être utilisées dans le code C++ à chaque fois
qu’une chaîne de ressource doit être référencée. Par exemple, la section
resourcestring du code Pascal Objet doit contenir
unit borrowed;
interface
resourcestring
Warning = ’Attention quand vous accédez aux ressources chaîne.’;
implementation
begin
end.
Le code correspondant généré par le compilateur Pascal pour C++Builder doit
être :
extern PACKAGE System::Resource ResourceString _Warning;
#define Borrowed_Warning System::LoadResourceString(&Borrowed::_Warning)
Cela vous permet d’utiliser la ressource chaîne Pascal Objet exportée sans avoir à
appeler explicitement LoadResourceString.
Paramètres par défaut
Le compilateur Pascal autorise les paramètres par défaut par compatibilité avec
C++ concernant les constructeurs. A la différence de C++, les constructeurs
Pascal Objet peuvent avoir le même nombre et le même type de paramètres
puisqu’ils ont des noms uniques. Dans de tels cas, des paramètres factices sont
utilisés dans les constructeurs Pascal Objet pour les distinguer quand les fichiers
d’en-tête C++ sont générés. Par exemple, pour une classe nommée TInCompatible,
les constructeurs en Pascal Objet peuvent être :
constructor Create(AOwner: TComponent);
constructor CreateNew(AOwner: TComponent);
Ils pourraient se traduire, sans paramètre par défaut, dans le code C++ suivant
qui est ambigu :
__fastcall TInCompatible(Classes::TComponent* Owner);// version C++ du constructeur Pascal
Create
13-22
Guide du développeur
Gestion des types de données et des concepts du langage Pascal Objet
__fastcall TInCompatible(Classes::TComponent* Owner);// version C++ du constructeur Pascal
CreateNew
Par contre, en utilisant les paramètres par défaut, pour une classe nommée
TCompatible, les constructeurs Pascal Objet sont :
constructor Create(AOwner: TComponent);
constructor CreateNew(AOwner: TComponent; Dummy: Integer = 0);
Qui se traduisent dans le code C++Builder suivant qui n’est pas ambigu :
__fastcall TCompatible(Classes::TComponent* Owner);// version C++ du constructeur Pascal
Create
__fastcall TCompatible(Classes::TComponent* Owner, int Dummy); // version C++ du
// constructeur Pascal
CreateNew
Remarque
L’élément essentiel concernant les paramètres par défaut est que DCC32
supprime la valeur par défaut du paramètre par défaut. Si la valeur par défaut
n’était pas supprimée, on retomberait sur l’ambiguïté qui a lieu en l’absence de
valeur par défaut. Vous devez tenir compte de ce problème quand vous utilisez
des classes VCL ou CLX ou des composants proposés par des tiers.
Informations de type à l’exécution
Le Pascal Objet propose des constructions gérant le RTTI. Certaines ont des
équivalents en C++. Elles sont listées dans le tableau suivant :
Tableau 13.3 Exemples de correspondances de RTTI entre Pascal Objet et C++
RTTI Pascal Objet
RTTI C++
if Sender is TButton...
if (dynamic_cast <TButton*> (Sender)
// dynamic_cast renvoie NULL en cas d’échec.
b := Sender as TButton;
(* déclenche une exception en cas
d’échec *)
TButton& ref_b = dynamic_cast <TButton&> (*Sender)
// déclenche une exception en cas d’échec.
ShowMessage(Sender.ClassName);
ShowMessage(typeid(*Sender).name());
Dans le tableau 13.3, ClassName est une méthode de TObject qui renvoie une
chaîne contenant le nom du type réel de l’objet sans tenir compte du type de la
variable déclarée. D’autres méthodes RTTI définies dans TObject n’ont pas
d’équivalent C++. Ce sont les méthodes publiques suivantes :
• ClassInfo renvoie un pointeur sur la table des informations de type à
l’exécution (RTTI) du type d’objet.
• ClassNameIs détermine si un objet est d’un type donné.
• ClassParent renvoie le type de l’ancêtre immédiat de la classe. Pour TObject,
ClassParent renvoie nil car TObject n’a pas de parent. Elle est utilisée par les
opérateurs is et as et par la méthode InheritsFrom.
Gestion en langage C++ de VCL et CLX
13-23
Gestion des types de données et des concepts du langage Pascal Objet
• ClassType détermine dynamiquement le type réel d’un objet. Elle est utilisée de
manière interne par les opérateurs Pascal Objet is et as.
• FieldAddress utilise le RTTI pour obtenir l’adresse d’un champ publié. Elle est
utilisée de manière interne par le système de flux.
• InheritsFrom détermine la relation entre deux objets. Elle est utilisée de
manière interne par les opérateurs Pascal Objet is et as.
• MethodAddress utilise le RTTI pour trouver l’adresse d’une méthode. Elle est
utilisée de manière interne par le système de flux.
Certaines de ces méthodes de TObject sont conçues pour un usage interne par le
compilateur ou pour le système de flux. Pour davantage d’informations sur ces
méthodes, voir l’aide en ligne.
Types sans correspondants
Type réel sur six octets
L’ancien type Pascal de format à virgule flottante sur 6 octets est maintenant
appelé Real48. L’ancien type Real est maintenant un double. C++ n’a pas d’équivalent
pour le type Real48. Vous ne devez donc pas utiliser de code Pascal Objet
utilisant ce type dans du code C++. Si vous le faites, le générateur de fichier
d’en-tête génère un avertissement.
Tableaux renvoyés par des fonctions
En Pascal Objet, une fonction peut prendre comme argument, ou renvoyer
comme type, un tableau. Par exemple, la syntaxe d’une fonction GetLine
renvoyant un tableau de 80 caractères est :
type
Line_Data = array[0..79] of char;
function GetLine: Line_Data;
Le C++ n’a pas d’équivalent à ce concept. En C++, il n’est pas légal d’utiliser des
tableaux comme type renvoyé par des fonctions. De même, le C++ n’autorise pas
l’utilisation d’un tableau comme type d’un argument de fonction.
Bien qu’aucune propriété de la VCL et CLX ne soit un tableau, le langage Pascal
Objet le permet. Comme les propriétés peuvent utiliser les méthodes de lecture
et d’écriture Get et Set qui prennent et renvoient des valeurs du type de la
propriété, il n’est pas possible d’avoir une propriété de type tableau dans
C++Builder.
Remarque
13-24
Les propriétés tableau qui sont autorisées en Pascal Objet ne posent pas de
problème en C++ car la méthode Get prend une valeur d’indice comme
paramètre et la méthode Set renvoie un objet du type contenu dans le tableau.
Pour davantage d’informations sur les propriétés tableau, voir “Création de
propriétés tableau” à la page 47-9.
Guide du développeur
Gestion des types de données et des concepts du langage Pascal Objet
Extensions des mots clés
Cette section décrit les extensions des mots clés ANSI implémentées dans
C++Builder pour gérer la VCL et la CLX. Pour une liste complète des mots clés
et des extensions de mot clé de C++Builder, voir l’aide en ligne.
__classid
L’opérateur __classid est utilisé par le compilateur pour générer un pointeur sur
la vtable pour le nom de classe spécifié. Cet opérateur est utilisé pour obtenir la
méta classe d’une classe.
Syntaxe
__classid(classname)
Par exemple, __classid est utilisé pour enregistrer des éditeurs de propriété, des
composants ou des classes, et avec la méthode InheritsFrom de TObject. Le code
suivant illustre l’utilisation de __classid pour créer un nouveau composant
dérivé de TWinControl:
namespace Ywndctrl
{
void __fastcall PACKAGE Register()
{
TComponentClass classes[1] = {__classid(MyWndCtrl)};
RegisterComponents("Additional", classes, 0);
}
}
__closure
Le mot clé __closure est utilisé pour déclarer un type particulier de pointeur sur
une fonction membre. En C++ standard,le seul moyen d’obtenir un pointeur sur
une fonction membre est d’utiliser le nom complet du membre, comme indiqué
dans l’exemple suivant :
class base
{
public:
void func(int x) { };
};
typedef void (base::* pBaseMember)(int);
int main(int argc, char* argv[])
{
base
baseObject;
pBaseMember m = &base::func; // Renvoie un pointeur sur le membre ‘func’
// Appelle ‘func’ via la pointeur sur le membre
(baseObject.*m)(17);
return 0;
}
Gestion en langage C++ de VCL et CLX
13-25
Gestion des types de données et des concepts du langage Pascal Objet
Cependant, vous ne pouvez pas affecter un pointeur sur un membre d’une classe
dérivée à un pointeur sur un membre d’une classe de base. Cette règle (appelée
contravariance) est illustrée dans l’exemple suivant :
class derived: public base
{
public:
void new_func(int i) { };
};
int main(int argc, char* argv[])
{
derived
derivedObject;
pBaseMember m = &derived::new_func; // ILLEGAL
return 0;
}
Cependant l’extension __closure permet d’éviter cette limitation et davantage. En
utilisant un closure, vous pouvez obtenir un pointeur sur une fonction membre
d’un objet (c’est-à-dire sur une instance donnée d’une classe). L’objet peut être
quelconque, sans tenir compte de sa hiérarchie d’héritage. Le pointeur this de
l’objet est automatiquement utilisé lors de l’appel de la fonction membre via le
closure. L’exemple suivant montre comment déclarer et utiliser un closure. On
suppose définies les classes base et dérivée spécifiées dans l’exemple précédent.
int main(int argc, char* argv[])
{
derived
derivedObject;
void (__closure *derivedClosure)(int);
derivedClosure = derivedObject.new_func; // Obtient un pointeur sur le membre
‘new_func’.
// Attention, le closure est associé avec
// un objet particulier : ‘derivedObject’.
derivedClosure(3); // Appelle ‘new_func’ via le closure.
return 0;
}
Les closures fonctionnent également avec les pointeurs d’objets comme l’illustre
l’exemple suivant :
void func1(base *pObj)
{
// Un closure prenant un argument int et renvoyant void.
void ( __closure *myClosure )(int);
// Initialise le closure.
myClosure = pObj->func;
// Utilise le closure pour appeler la fonction membre
myClosure(1);
return;
}
int main(int argc, char* argv[])
{
13-26
Guide du développeur
Gestion des types de données et des concepts du langage Pascal Objet
derived
derivedObject;
void (__closure *derivedClosure)(int);
derivedClosure = derivedObject.new_func; // Identique à ce qui précède...
derivedClosure(3);
// On peut aussi utiliser des pointeurs pour initialiser un closure.
// Il est aussi possible d’obtenir un pointeur sur la fonction membre ’func’
// dans la classe de base.
func1(&derivedObject);
return 0;
}
Attention, on transmet ici un pointeur à une instance de la classe dérivée puis
nous l’utilisons pour obtenir un pointeur sur une fonction membre de la classe
de base — ce qui est interdit en C++ standard.
Les closures sont un élément clé de l’environnement RAD C++ Builder. Ils
permettent d’attribuer un gestionnaire d’événément dans l’inspecteur d’objets.
Ainsi, un objet TButton a un événement appelé OnClick. Dans la classe TButton,
l’événement OnClick est une propriété qui utilise l’extension de mot clé __closure
dans sa déclaration. Le mot clé __closure permet d’affecter à la propriété une
fonction membre d’une autre classe (généralement une fonction membre d’un
objet TForm). Quand vous placez un objet TButton dans une fiche, puis créez un
gestionnaire pour l’événement OnClick du bouton, C++ Builder crée une fonction
membre dans le parent TForm du bouton et affecte cette fonction membre à
l’événement OnClick de TButton. Ainsi, le gestionnaire est associé à cette instance
particulière de TButton et pas à une autre.
Pour davantage d’informations sur les événements et les closures, voir
chapitre 48, “Création d’événements”.
__property
Le mot clé __property déclare un attribut d’une classe. Les propriétés
apparaissent au programmeur juste comme un autre attribut (champ) d’une
classe. Néanmoins, comme sa contrepartie en Pascal Objet, le mot clé C++
Builder __property ajoute beaucoup plus de fonctionnalités que la simple
consultation et modification de la valeur de l’attribut. Comme les attributs de
propriété contrôlent complètement l’accès à la propriété, il n’y a pas de limites à
l’implémentation de la propriété dans la classe même.
Syntaxe
__property type NomPropriété[index1Type index1][indexNType indexN] = { attributs };
Où
•
•
•
•
type est un type de donnée intrinsèque ou préalablement déclaré.
NomPropriété est un identificateur correct.
indexNType est un type de donnée intrinsèque ou préalablement déclaré.
indexN est le nom d’un paramètre indice qui sera transmis aux fonctions read
et write de la propriété.
Gestion en langage C++ de VCL et CLX
13-27
Gestion des types de données et des concepts du langage Pascal Objet
• attributs est une liste séparée par des virgules de read, write, stored, default
(ou nodefault), ou index.
Le paramètre entre crochets indexN est facultatif. S’il est présent, il déclare une
propriété tableau. Les paramètres indices sont transmis aux méthodes read et
write de la propriété tableau.
L’exemple suivant illustre des déclarations de propriétés simples :
class PropertyExample {
private:
int Fx,Fy;
float Fcells[100][100];
protected:
{ return(Fx); }
int readX()
void writeX(int newFx) { Fx = newFx; }
double computeZ() {
// Effectue des calculs et renvoie une valeur à virgule flottante...
return(0.0);
}
float cellValue(int row, int col) { return(Fcells[row][col]); }
public:
__property
__property
__property
__property
X = { read=readX, write=writeX };
int
Y = { read=Fy };
int
double Z = { read=computeZ };
float Cells[int row][int col] = { read=cellValue };
};
Cet exemple utilise plusieurs déclarations de propriétés. La propriété X a un
accès en lecture-écriture via, respectivement, les fonctions membres readX et
writeX. La propriété Y correspond directement à la variable membre Fy, elle est
en lecture seule. La propriété Z est une valeur en lecture seule qui est calculée,
elle n’est pas stockée dans une donnée membre de la classe. Enfin, la propriété
Cells propose une propriété tableau avec deux indices. L’exemple suivant illustre
la manière d’accéder ces propriétés depuis votre code :
PropertyExample myPropertyExample;
myPropertyExample.X = 42;
// S’évalue à : myPropertyExample.WriteX(42);
myVal1 = myPropertyExample.Y; // S’évalue à : myVal1 = myPropertyExample.Fy;
int
double myVal2 = myPropertyExample.Z; // S’évalue à : myVal2 = myPropertyExample.ComputeZ();
float cellVal = myPropertyExample[3][7]; // S’évalue à :
// cellVal = myPropertyExample.cellValue(3,7);
Les propriétés ont de nombreuses possibilités et caractéristiques que cet exemple
ne met pas en avant. Les propriétés peuvent
• Associer la même méthode de lecture ou d’écriture à plusieurs propriétés en
utilisant l’attribut index.
• Ont des valeurs par défaut.
13-28
Guide du développeur
Gestion des types de données et des concepts du langage Pascal Objet
• Etre stockées dans le fichier d’une fiche.
• Etendre une propriété définie dans une classe de base
Pour davantage d’informations sur les propriétés, voir chapitre 47, “Création de
propriétés”.
__published
Le mot clé __published spécifie que les propriétés de cette section sont affichées
dans l’inspecteur d’objets si la classe est dans la palette des composants. Seules
les classes dérivées de TObject peuvent avoir une section __published.
Les règles de visibilité pour les membres publiés sont les mêmes que celles
s’appliquant aux membres publics. Il n’y a qu’une seule différence entre membre
public et membre publié : les informations de type à l‘exécution (RTTI) Pascal
Objet sont générées pour les données membres et les propriétés déclarées dans
une section __published. RTTI permet à une application d’interroger
dynamiquement les données membres, les fonctions membres et les propriétés
d’un type de classe qui sinon serait inconnu.
Remarque
Les constructeurs et les destructeurs ne sont pas permis dans une section
__published. Les propriétés, les données membres Pascal ou dérivées de VCL ou
CLX, les fonctions membres et les closures sont autorisés dans une section
__published. Les champs définis dans une section __published doivent être d’un
type classe. Les propriétés définies dans une section __published ne peuvent être
des propriétés tableau. Une propriété définie dans une section __published doit
être de type ordinal, réel, chaîne, ensemble court, classe ou pointeur de méthode.
Extensions du mot clé __declspec
Certains arguments de l’extension du mot clé __declspec permettent la gestion
de la VCL et de la CLX. Ces arguments sont listés ci-dessous. Les macros pour
les arguments de declspec et leurs combinaisons sont définies dans sysmac.h.
Dans la plupart des cas, vous n’avez pas besoin de spécifier ces arguments.
Quand vous avez besoin de les ajouter, vous devez utiliser les macros.
__declspec(delphiclass)
L’argument delphiclass est utilisé pour la déclaration de classes dérivées de
TObject. Ces classes sont créées avec les compatibilités suivantes :
• RTTI compatible Pascal Objet
• Comportement de constructeur-destructeur compatible avec VCL/CLX
• Gestion des exceptions compatible avec VCL/CLX
Une classe compatible avec VCL/CLX présente les limitations suivantes :
• Les classes de base virtuelles sont interdites.
• Pas d’héritage multiple sauf dans le cas décrit dans “Héritage et interfaces” à
la page 13-2.
• Elle doit être allouée dynamiquement en utilisant l’opérateur global new.
Gestion en langage C++ de VCL et CLX
13-29
Gestion des types de données et des concepts du langage Pascal Objet
• Elle doit avoir un destructeur.
• Les constructeurs de copie et les opérateurs d’affectation ne sont pas générés
par le compilateur pour les classes dérivées de VCL/CLX.
Une déclaration de classe traduite depuis le Pascal Objet a besoin de ce
modificateur si le compilateur doit savoir que la classe est dérivée de TObject.
__declspec(delphireturn)
L’argument delphireturn est utilisé en usage interne uniquement par la VCL et
la CLX dans C++Builder. Il est utilisé pour les déclarations de classes créées
dans C++Builder pour gérer les types prédéfinis et les constructions Pascal Objet
n’ayant pas d’équivalent dans un type C++ natif. Ce sont, entre autres, Currency,
AnsiString, Variant, TDateTime et Set. L’argument delphireturn marque les classes
C++ comme compatibles avec la VCL ou la CLX pour la gestion des appels de
fonction et les valeurs renvoyées. Ce modificateur est nécessaire pour transmettre
une structure par valeur à une fonction entre Pascal Objet et le C++.
__declspec(delphirtti)
L’argument delphirtti oblige le compilateur à inclure des informations de type à
l’exécution dans une classe lors de sa compilation. Quand ce modificateur est
utilisé, le compilateur génère des informations de type à l’exécution pour tous les
champs, méthodes, et propriétés déclarés dans une section published. Pour les
interfaces, le compilateur génère des informations de type à l’exécution pour
toutes les méthodes de l’interface. Si une classe est compilée avec des
informations de type à l’exécution, tous ses descendants le sont également.
Comme la classe TPersistent est compilée avec des informations de type à
l’exécution, cela signifie qu’il n’est pas nécessaire d’utiliser ce modificateur avec
toutes les classes que vous créez ayant TPersistent comme ancêtre. Ce
modificateur est surtout utilisé pour les interfaces dans des applications qui
implémentent ou utilisent des services Web.
__declspec(dynamic)
L’argument dynamic est utilisé pour la déclaration de fonctions dynamiques.
Les fonctions dynamiques sont similaires à des fonctions virtuelles mais elles ne
sont stockées que dans les vtables des objets qui les définissent et pas dans les
vtables des descendants. Si vous appelez une fonction dynamique alors que cette
fonction n’est pas définie dans l’objet, les vtables de ces ancêtres sont examinées
jusqu’à trouver la fonction. Les fonctions dynamiques ne sont autorisées que
pour les classes dérivées de TObject.
__declspec(hidesbase)
L’argument hidesbase préserve la sémantique d’un programme Pascal Objet
quand des fonctions virtuelles ou redéfinies sont exportées en C++Builder. En
Pascal Objet, les fonctions virtuelles des classes de base peuvent apparaître dans
la classe dérivée comme une fonction de même nom, mais conçue comme une
fonction entièrement nouvelle sans relation explicite avec la précédente.
13-30
Guide du développeur
Gestion des types de données et des concepts du langage Pascal Objet
Les compilateurs utilisent la macro HIDESBASE, définie dans sysmac.h, pour
spécifier que ce type de déclaration de fonction est entièrement distinct. Par
exemple, si la classe de base T1 déclare une fonction virtuelle, func, sans
arguments et si sa classe dérivée T2 déclare une fonction de même nom et de
même signature, DCC32 -jphn produit un fichier HPP contenant les prototypes
suivants :
virtual void T1::func(void);
HIDESBASE void T2::func(void);
Sans la déclaration HIDESBASE, la sémantique du programme C++ indique que
la fonction virtuelle T1::func() est redéfinie par T2::func().
__declspec(package)
L’argument package indique que le code définissant la classe peut être compilé
dans un paquet. Ce modificateur est généré automatiquement par le compilateur
lors de la création de paquets dans l’EDI. Pour davantage d’informations sur les
paquets, voir chapitre 15, “Utilisation des paquets et des composants”.
__declspec(pascalimplementation)
L’argument pascalimplementation indique que le code définissant la classe a été
implémenté en Pascal Objet. Ce modificateur apparaît dans les fichiers d’en-tête
de portabilité Pascal Objet d’extension .hpp.
__declspec(uuid)
L’argument uuid associe une classe avec un GUID. Il peut s’utiliser dans toute
classe, mais il s’emploie généralement pour les classes qui représentent des
interfaces Pascal Objet (ou des interfaces COM). Vous pouvez obtenir le GUID
d’une classe déclarée en utilisant ce modificateur, en appelant la directive
__uuidof.
Gestion en langage C++ de VCL et CLX
13-31
13-32
Guide du développeur
Chapitre
14
Développement d’applications
multiplates-formes
Chapitre 14
Vous pouvez utiliser C++Builder pour développer des applications 32 bits
multiplates-formes qui s’exécuteront sur les systèmes d’exploitation Windows et
Linux. Les applications multiplates-formes utilisent des composants CLX sans
appels API spécifiques à un système d’exploitation. Pour développer une
application multiplate-forme, créez une nouvelle application CLX ou modifiez
une application Windows existante. Ensuite, compilez-la et déployez-la sur la
plate-forme sur laquelle vous prévoyez de l’exécuter. Pour Windows, utilisez
C++Builder. Pour Linux, aucune solution Borland C++ n’est encore disponible,
mais vous pouvez dès à présent développer l’application avec C++Builder.
Ce chapitre décrit d’une part la manière de modifier les applications C++Builder
pour qu’elles se compilent sous Linux et d’autre part d’écrire un code
indépendant de la plate-forme et portable entre les deux environnements.
Il contient en outre des informations sur les différences entre le développement
d’applications sous Windows et sous Linux.
Création d’applications multiplates-formes
Vous créerez des applications multiplates-formes comme vous créez n’importe
quelle application C++Builder. Vous devez utiliser les composants visuels et non
visuels CLX et éviter les appels API spécifiques au système d’exploitation pour
que votre application soit totalement indépendante de la plate-forme.
(Voir “Ecriture de code portable” à la page 14-18, pour des conseils sur l’écriture
des applications multiplates-formes.)
Pour créer une application multiplate-forme :
1 Dans l’EDI, choisissez Fichier|Nouveau|Application CLX.
Développement d’applications multiplates-formes
14-1
Portage d’applications Windows vers Linux
La palette des composants montre les pages et les composants pouvant être
utilisés dans les applications CLX.
2 Développez votre application dans l’EDI.
3 Compilez et testez l’application. Examinez tous les messages d’erreur pour
repérer les endroits où des modifications supplémentaires seront nécessaires.
Remarque
Avec une solution C++Builder disponible pour Linux, vous pouvez compiler et
tester votre application sous Linux.
Quand vous portez votre application vers Linux, vous devez réinitialiser les
options de votre projet. En effet, le fichier .dof stockant les options du projet est
recréé par Linux avec une extension différente (avec les options par défaut).
Le fichier fiche d’une application multiplate-forme a l’extension .xfm et non pas
.dfm. Cela permet de distinguer les fiches multiplates-formes utilisant des
composants CLX et les fiches utilisant des composants VCL. Un fichier fiche .xfm
fonctionne à la fois sous Windows et sous Linux, mais une fiche .dfm ne
fonctionne que sous Windows.
Pour plus d’informations sur l’écriture des applications de bases de données ou
des applications Internet indépendantes des plates-formes, consultez
“Applications de bases de données multiplates-formes” à la page 14-22 et
“Applications Internet multiplates-formes” à la page 14-30.
Portage d’applications Windows vers Linux
Si vous disposez d’applications C++Builder écrites pour l’environnement
Windows, vous pouvez les préparer pour l’environnement Linux. La facilité de
cette évolution dépend de la nature et de la complexité de l’application, ainsi
que du nombre de dépendances Windows explicites.
Les sections suivantes décrivent quelques-unes des différences majeures entre les
environnements Windows et Linux et fournissent des lignes directrices sur la
manière de démarrer le portage d’une application.
Techniques de portage
Les approches que vous pouvez adopter pour porter une application d’une plateforme vers une autre sont les suivantes :
Tableau 14.1 Techniques de portage
14-2
Technique
Description
Portage propre à une plate-forme
Cible un système d’exploitation et les API sous-jacentes
Portage multiplate-forme
Cible une API multiplate-forme
Emulation Windows
Ne modifie pas le code et porte les API utilisées
Guide du développeur
Portage d’applications Windows vers Linux
Portages propres à une plate-forme
Les portages propres à une plate-forme peuvent être longs et coûteux et ne
produisent qu’un résultat ciblé. Ils créent plusieurs bases de code différentes, ce
qui les rend particulièrement difficiles à maintenir. Toutefois, chaque portage est
conçu pour un système d’exploitation particulier et peut tirer parti des
fonctionnalités propres à la plate-forme. L’application s’exécute donc en règle
générale plus rapidement.
Portages multiplates-formes
Les portages multiplates-formes permettent généralement de gagner du temps
car les applications portées ciblent plusieurs plates-formes. En revanche, la
quantité de travail nécessaire au développement d’applications multiplatesformes dépend beaucoup du code existant. Si le code a été développé sans souci
d’indépendance par rapport à la plate-forme, vous pouvez rencontrer des
scénarios où la logique indépendante de la plate-forme et la mise en œuvre
dépendante de la plate-forme sont mélangées.
L’approche multiplate-forme est préférable, car la logique métier s’exprime en
termes indépendants de la plate-forme. Certains services sont masqués par une
interface interne qui est identique sur toutes les plates-formes mais possède une
mise en œuvre particulière sur chacune. La bibliothèque d’exécution de
C++Builder en est un exemple. L’interface est très similaire sur les deux platesformes, même si la mise en œuvre peut être très différente. Vous devez séparer
les éléments multiplates-formes, puis mettre en œuvre des services particuliers
au niveau supérieur. Cette approche est en fin de compte la solution la moins
coûteuse, grâce à la réduction des coûts de maintenance occasionnée par un
large partage de la base de source et une amélioration de l’architecture
d’application.
Portages d’émulation Windows
L’émulation Windows est la méthode la plus complexe et elle peut être très
coûteuse, mais c’est celle qui offrira le plus de similarité entre l’application Linux
résultante et une application Windows existante. Cette approche consiste à
mettre en œuvre la fonctionnalité Windows sous Linux. D’un point de vue
ingénierie, cette solution est très difficile à maintenir.
Là où vous voulez émuler les API Windows, vous pouvez inclure deux sections
distinctes en utilisant des #ifdef pour indiquer les sections de code qui
s’appliquent spécifiquement à Windows ou à Linux.
Portage de votre application
Si vous portez une application que vous souhaitez exécuter sous Windows et
sous Linux, vous devez modifier votre code ou utiliser des #ifdef pour définir
les sections de code qui s’appliquent spécifiquement à Windows ou à Linux.
Développement d’applications multiplates-formes
14-3
Portage d’applications Windows vers Linux
Les étapes générales pour porter votre application VCL vers CLX sont les
suivantes :
1 Ouvrez le projet contenant l’application que vous voulez modifier dans
C++Builder.
2 Copiez les fichiers .dfm dans des fichiers .xfm de même nom (par exemple,
renommez unit1.dfm en unit1.xfm). Renommez (ou utilisez un #ifdef) la
référence au fichier .dfm dans les fichiers en-tête de #pragma resource "*.dfm" vers
#pragma resource "*.xfm". (Le fichier .xfm fonctionnera dans une application
C++Builder et Linux.)
3 Modifiez (ou spécifiez avec un #ifdef) tous les fichiers en-tête de votre fichier
source pour qu’ils fassent référence aux unités correctes dans la CLX. (Pour
plus d’informations, consultez “Comparaison entre les unités CLX et VCL” à
la page 14-10.)
Par exemple, changez les instructions #include suivantes dans le fichier en-tête
d’une application Windows :
#include
#include
#include
#include
<vcl.h>
<Controls.hpp>
<StdCtrls.hpp>
<Forms.hpp>
en instructions ci-dessous pour une application CLX :
#include
#include
#include
#include
<clx.h>
<QControls.hpp>
<QStdCtrls.hpp>
<QForms.hpp>
4 Enregistrez le projet et rouvrez-le. Désormais, la palette des composants
propose des composants pouvant être utilisés dans les applications CLX.
Remarque
Certains composants non visuels Windows peuvent être utilisés dans les
applications CLX, mais ils comportent des fonctionnalités qui ne
fonctionneront que dans les applications CLX Windows. Si vous prévoyez de
compiler votre application également sous Linux, n’utilisez pas les composants
VCL non visuels dans vos applications, ou utilisez des #ifdef pour marquer
les sections de code qui les utilisent comme étant uniquement destinées à
Windows. Vous ne pouvez pas utiliser la partie visuelle de la VCL avec
VisualCLX dans la même application.
5 Réécrivez le code qui nécessite des dépendances Windows afin de rendre le
code plus indépendant de la plate-forme. Utilisez pour cela les routines et
constantes de la bibliothèque d’exécution. (Pour plus d’informations, consultez
“Ecriture de code portable” à la page 14-18.)
6 Trouvez une fonctionnalité équivalente pour les caractéristiques qui sont
différentes sous Linux. Utilisez des #ifdef (avec modération cependant) pour
délimiter les informations propres à Windows. (Pour plus d’informations,
consultez “Utilisation des directives conditionnelles” à la page 14-19.)
14-4
Guide du développeur
Portage d’applications Windows vers Linux
Par exemple, vous pouvez ajouter le code spécifique à la plate-forme #ifdef
suivant dans vos fichiers source :
#ifdef WINDOWS //Avec le compilateur C++Builder, utilisez __WIN32__
IniFile->LoadfromFile(“c:\\x.txt”);
#endif
#ifdef __linux__
IniFile->LoadfromFile(“/home/name/x.txt”);
#endif
7 Recherchez les références aux noms de chemins dans tous les fichiers du
projet.
• Les noms de chemins dans Linux utilisent une barre oblique droite /
comme délimiteur (par exemple, /usr/lib) et les fichiers peuvent être
installés dans des répertoires différents sur le système Linux. Utilisez la
constante PathDelim (dans SysUtils) pour spécifier le délimiteur de chemin
adapté au système. Déterminez l’emplacement correct pour tous les fichiers
dans Linux.
• Modifiez les références à des lettres de lecteurs (par exemple, C:\) et le
code qui reconnaît les lettres de lecteurs en recherchant un deux-points en
position 2 dans la chaîne. Utilisez la constante DriveDelim (dans SysUtils)
pour spécifier l’emplacement selon des termes adaptés au système.
• Là où vous spécifiez plusieurs chemins, remplacez le séparateur de chemin
point-virgule (;) par un deux-points (:). Utilisez la constante PathSep (dans
SysUtils) pour spécifier le séparateur de chemin adapté au système.
• Comme les noms de fichiers sont sensibles à la casse dans Linux, assurezvous que votre application ne modifie pas la casse des noms de fichiers ou
n’assume pas une certaine casse.
8 Compilez, testez et déboguez votre application.
CLX et VCL
Les applications CLX utilisent la bibliothèque des composants multiplates-formes
(CLX) Borland à la place de la bibliothèque des composants visuels (VCL). Dans
la VCL, de nombreux contrôles permettent d’accéder facilement aux contrôles
Windows avec des appels dans les bibliothèques d’API Windows. De même,
CLX fournit un accès aux widgets (de window + gadget) Qt avec des appels
dans les bibliothèques partagées Qt. C++Builder inclut la CLX et la VCL.
La CLX ressemble beaucoup à la VCL. La plupart des composants et propriétés
ont le même nom. En outre, la CLX et la VCL sont disponibles sous Windows
(selon votre édition de C++Builder).
Développement d’applications multiplates-formes
14-5
Portage d’applications Windows vers Linux
Les composants de la CLX peuvent être groupés en parties comme suit :
Tableau 14.2 Parties de CLX
Parties
Description
VisualCLX
Composants et graphiques d’interface utilisateur multiplates-formes natifs.
DataCLX
Composants d’accès aux données client. Les composants de cette partie
forment un sous-ensemble des ensembles de données locaux, client/
serveur et n niveaux basés sur le client. Le code est identique sous Linux
et Windows.
NetCLX
Composants Internet incluant Apache DSO et CGI WebBroker. Ce sont les
mêmes sous Linux et Windows.
BaseCLX
Bibliothèque d’exécution incluant l’unité Classes. Le code est identique
sous Linux et Windows.
Les widgets de VisualCLX remplacent les contrôles Windows. Par exemple,
TWidgetControl dans la CLX remplace TWinControl dans la VCL. Les autres
composants VCL (comme TScrollingWinControl) ont des noms correspondant dans
la CLX (comme TScrollingWidget). Vous n’avez toutefois pas besoin de remplacer
les occurrences de TWinControl par TWidgetControl. Des déclarations de types,
comme :
TWinControl = TWidgetControl;
sont présentes dans le fichier unité QControls pour simplifier le partage du code
source. TWidgetControl et ses descendants possèdent tous une propriété Handle
qui référence l’objet Qt, et une propriété Hooks qui référence l’objet intercepteur
gérant le mécanisme des événements.
Les noms et les emplacements des unités de certaines classes sont différents pour
CLX. Vous devrez modifier le fichier en-tête intégré à vos fichiers source pour
éliminer les références aux unités qui n’existent pas dans la CLX et pour les
remplacer par les noms d’unités CLX.
Différences de CLX
Bien que la plus grande partie de la CLX soit mise en œuvre de la même façon
que la VCL, certaines fonctionnalités sont mises en œuvre autrement. Cette
section présente les différences entre les mises en œuvre de la CLX et de la VCL
auxquelles vous devez faire attention lorsque vous écrivez des applications
multiplates-formes.
Présentation visuelle
L’environnement visuel de Linux est quelque peu différent de celui de Windows.
L’aspect des boîtes de dialogue peut dépendre du gestionnaire de fenêtres utilisé
(par exemple, selon qu’il s’agisse de KDE ou de Gnome).
14-6
Guide du développeur
Portage d’applications Windows vers Linux
Styles
Il est possible d’utiliser des styles au niveau application en plus des propriétés
OwnerDraw. Vous pouvez utiliser la propriété TApplication::Style pour définir
l’aspect visuel des éléments graphiques d’une application. Avec des styles, un
widget ou une application peut prendre une toute nouvelle apparence. Vous
pouvez toujours utiliser le dessin propriétaire sous Linux mais l’utilisation des
styles est recommandée.
Variants
Tout le code des variants et tableaux sécurisés qui était dans l’unité System se
trouve dans deux nouvelles unités :
• Variants
• VarUtils
Le code dépendant du système d’exploitation est maintenant isolé dans l’unité
VarUtils et il contient des versions génériques de tout ce qui est nécessaire à
l’unité Variants. Si vous convertissez en application CLX une application VCL
qui contient des appels à Windows, vous devez remplacer ces appels par des
appels à l’unité VarUtils.
Si vous souhaitez utiliser des variants, vous devez inclure l’unité Variants au
fichier en-tête de votre fichier source.
VarIsEmpty effectue un test simple sur varEmpty pour vérifier si un variant est
effacé, et sous Linux vous pouvez utiliser la fonction VarIsClear pour savoir si la
valeur du variant est indéfinie.
Registre
Linux n’utilise pas de registre pour stocker des informations de configuration.
Vous devez utiliser des fichiers texte de configuration et des variables
d’environnement à la place du registre. Les fichiers de configuration système
sous Linux sont souvent installés dans /etc, par exemple /etc/hosts. Les autres
profils utilisateur sont installés dans des fichiers cachés (dont le nom commence
par un point), comme .bashrc, qui contient les paramètres du shell bash ou
.XDefaults, qui sert à définir des valeurs par défaut pour les programmes X.
Le code dépendant du registre peut être modifié pour utiliser à la place un
fichier texte de configuration local. Les paramètres modifiables par l’utilisateur
doivent être enregistrés dans son répertoire initial pour accorder l’autorisation
d’écriture. Les options de configuration devant être définies par l’utilisateur root
doivent se trouver dans /etc. Ecrire une unité contenant toutes les fonctions de
registre mais en détournant toutes les sorties vers un fichier de configuration
local est un moyen que vous pouvez utiliser pour gérer une dépendance par
rapport au registre.
Pour mémoriser des informations à un emplacement global sous Linux, vous
pouvez stocker un fichier de configuration global dans le répertoire /etc ou le
répertoire initial de l’utilisateur sous forme de fichier caché. Toutes vos
applications peuvent alors accéder au même fichier de configuration. Vous devez
Développement d’applications multiplates-formes
14-7
Portage d’applications Windows vers Linux
toutefois vous assurer que les permissions du fichier et les droits d’accès sont
correctement définis.
Vous pouvez également utiliser des fichiers .ini dans des applications
multiplates-formes. Toutefois, dans la CLX, vous devez utiliser TMemIniFile au
lieu de TRegIniFile.
Autres différences
Par rapport à la VCL, la mise en œuvre de la CLX possède également d’autres
différences affectant le fonctionnement de vos composants. Cette section décrit
certaines de ces différences.
• Vous pouvez sélectionner un composant CLX dans la palette des composants,
puis utiliser le bouton gauche ou droit de la souris pour l’ajouter à une fiche.
Pour un composant VCL, vous ne pouvez utiliser que le bouton gauche.
• Le contrôle TButton CLX possède une propriété ToggleButton que le contrôle
VCL équivalent n’a pas.
• Dans la CLX, TColorDialog ne possède pas de propriété TColorDialog::Options à
initialiser. Par conséquent, vous ne pouvez pas personnaliser l’apparence et la
fonctionnalité de la boîte de dialogue de sélection de couleur. En outre, selon
le gestionnaire de fenêtres que vous utilisez dans Linux, TColorDialog n’est pas
toujours modale ou non-redimensionnable. Sous Windows, TColorDialog est
toujours modale et non-redimensionnable.
• A l’exécution, les boîtes à options fonctionnent différemment dans la CLX et
dans la VCL. Dans la CLX (mais pas dans la VCL), vous pouvez ajouter un
élément à une liste déroulante en entrant du texte et en appuyant sur Entrée
dans le champ d’édition d’une boîte à options. Vous pouvez désactiver cette
fonctionnalité en définissant InsertMode sur ciNone. Il est également possible
d’ajouter des éléments vides (sans chaîne) à la liste de la boîte à options. De
plus, si vous maintenez la flèche bas enfoncée quand la boîte de saisie est
fermée, vous ne vous arrêtez pas au dernier élément de la liste. Vous refaites
un tour en recommençant au début.
• TCustomEdit ne met pas en œuvre Undo, ClearUndo ou CanUndo. Il n’existe
donc aucune méthode pour annuler les saisies par programme. Mais les
utilisateurs d’une application peuvent annuler leurs saisies dans une boîte de
saisie (TEdit) lors de l’exécution en cliquant avec le bouton droit de la souris
sur cette boîte puis en choisissant la commande Annuler.
• La valeur des touches utilisées dans les événements peut être différente entre
la VCL et la CLX. Par exemple, la touche Entrée a la valeur 13 dans la VCL et
la valeur 4100 dans la CLX. Si vous codez les valeurs de touches dans vos
applications CLX, vous devez changer ces valeurs quand vous les portez de
Windows vers Linux et vice versa.
Il existe d’autres différences. Reportez-vous à la documentation en ligne de la
CLX pour des détails sur tous les objets CLX ou, dans les éditions de
C++Builder qui incluent le code source, situé dans {répertoire d’installation}\
C++Builder6\Source\CLX.
14-8
Guide du développeur
Portage d’applications Windows vers Linux
Fonctionnalités manquantes dans la CLX
Lorsque vous utilisez la CLX à la place de la VCL, beaucoup d’objets sont
identiques. Mais, ces objets peuvent avoir perdu certaines fonctionnalités (comme
des propriétés, des méthodes, ou des événements). Les fonctionnalités générales
suivantes sont absentes de la CLX :
• Propriétés bidirectionnelles (BidiMode) pour les entrées ou les sorties de texte
de la droite vers la gauche
• Propriétés de biseau génériques sur les contrôles courants (remarquez que
certains objets possèdent toujours des propriétés de biseau).
• Propriétés et méthodes d’ancrage.
• Fonctionnalités de compatibilité ascendante comme les composants de l’onglet
Win3.1 et Ctl3D.
• DragCursor et DragKind (mais le glisser-déplacer est inclus).
Fonctionnalités non portées directement
Certaines fonctionnalités propres à Windows, prises en charge par C++Builder,
ne sont pas portées directement vers les environnements Linux. Des
fonctionnalités comme COM, ActiveX, OLE, BDE et ADO sont dépendantes de la
technologie Windows et ne sont pas disponibles pour Linux. Le tableau suivant
présente les fonctionnalités qui sont différentes sur les deux plates-formes, avec
la fonctionnalité Linux ou CLX équivalente lorsqu’elle est disponible.
Tableau 14.3 Fonctionnalités modifiées ou différentes
FonctionnalitéWindows/VCL
Fonctionnalité Linux/CLX
Composants ADO
Composants de bases de données classiques
Serveurs Automation
Non disponible
BDE
dbExpress et composants de bases de données
classiques
Composants COM+ (incluant
ActiveX)
Non disponible
DataSnap
Non disponible
FastNet
Non disponible
Composants existants (comme les
éléments de la page Win 3.1 de la
palette des composants)
Non disponible
L’interface de programmation
d’application de messagerie (MAPI,
Messaging Application Programming
Interface) incluant une bibliothèque
standard des fonctions de messagerie
Windows.
SMTP et POP3 vous permettent d’envoyer, de
recevoir et d’enregistrer des messages électroniques
Rapports rapides
Non disponible
Développement d’applications multiplates-formes
14-9
Portage d’applications Windows vers Linux
Tableau 14.3 Fonctionnalités modifiées ou différentes (suite)
FonctionnalitéWindows/VCL
Fonctionnalité Linux/CLX
Services Web (SOAP)
Non disponible
WebSnap
Non disponible
Appels de l’API Windows
Méthodes CLX, appels Qt, appels libc ou appels à
d’autres bibliothèques système
Messages Windows
Evénements Qt
Winsock
Sockets BSD
Les équivalents Linux des dll Windows sont les bibliothèques d’objets partagés
(fichiers .so), qui contiennent du code indépendant de la position (PIC). Par
conséquent, les références mémoire globales et les appels à des fonctions externes
sont relatifs au registre EBX, qui doit être préservé d’un appel à l’autre.
Vous ne devez vous préoccuper des références mémoire globales et des appels à
des fonctions externes que si vous utilisez l’assembleur —C++Builder génère le
code correct. (Pour plus d’informations, consultez “Inclusion de code assembleur
inline” à la page 14-21.)
Comparaison entre les unités CLX et VCL
Tous les objets de la VCL ou de la CLX sont définis dans des fichiers en-tête. Par
exemple, vous trouverez la mise en œuvre de TObject dans l’unité System, et
l’unité Classes définit la classe de base TComponent. Lorsque vous déposez un
objet sur une fiche ou que vous utilisez un objet dans votre application, le nom
de l’unité est ajouté au fichier en-tête intégré à votre fichier source qui indique
au compilateur quelles unités doivent être liées au projet.
Les trois tableaux de cette section présentent les unités VCL et leurs équivalents
CLX, les unités CLX uniquement et les unités de la VCL uniquement.
Le tableau suivant présente les unités VCL et leurs équivalents CLX. Les unités
identiques dans la VCL et la CLX, ainsi que les unités tierces ne sont pas
répertoriées.
Tableau 14.4 Unités VCL et leurs équivalents CLX
14-10
Unités VCL
Unités CLX
ActnList
QActnList
Buttons
QButtons
CheckLst
QCheckLst
Clipbrd
QClipbrd
ComCtrls
QComCtrls
Consts
Consts, QConsts et RTLConsts
Controls
QControls
DBActns
QDBActns
DBCtrls
QDBCtrls
Guide du développeur
Portage d’applications Windows vers Linux
Tableau 14.4 Unités VCL et leurs équivalents CLX (suite)
Unités VCL
Unités CLX
DBGrids
QDBGrids
Dialogs
QDialogs
ExtCtrls
QExtCtrls
Forms
QForms
Graphics
QGraphics
Grids
QGrids
ImgList
QImgList
Mask
QMask
Menus
QMenus
Printers
QPrinters
Search
QSearch
StdActns
QStdActns
StdCtrls
QStdCtrls
Types
Types et QTypes
VclEditors
ClxEditors
Les unités suivantes existent dans la CLX mais pas dans la VCL :
Tableau 14.5 Unités CLX uniquement
Unité
Description
DirSel
Sélection de répertoire
QStyle
Aspect graphique de l’interface utilisateur
Qt
Interface de la bibliothèque Qt
Les unités VCL Windows suivantes ne sont pas incluses dans la CLX,
principalement parce qu’elles concernent des fonctionnalités propres à Windows
qui ne sont pas disponibles sous Linux comme ADO, COM et BDE.
Tableau 14.6 Unités de la VCL uniquement
Unité
Raison de son exclusion
ADOConst
Pas de fonctionnalité ADO
ADODB
Pas de fonctionnalité ADO
AppEvnts
Pas d’objet TApplicationEvent
AxCtrls
Pas de fonctionnalité COM
BdeConst
Pas de fonctionnalité BDE
Calendar
Pas prise en charge actuellement
Chart
Pas prise en charge actuellement
CmAdmCtl
Pas de fonctionnalité COM
ColorGrd
Pas prise en charge actuellement
ComStrs
Pas de fonctionnalité COM
ConvUtils
Non disponible
Développement d’applications multiplates-formes
14-11
Portage d’applications Windows vers Linux
Tableau 14.6 Unités de la VCL uniquement (suite)
14-12
Unité
Raison de son exclusion
CorbaCon
Pas de fonctionnalité Corba
CorbaStd
Pas de fonctionnalité Corba
CorbaVCL
Pas de fonctionnalité Corba
CtlPanel
Pas de Panneau de configuration Windows
CustomizeDlg
Pas prise en charge actuellement
DataBkr
Pas prise en charge actuellement
DBCGrids
Pas de fonctionnalité BDE
DBExcept
Pas de fonctionnalité BDE
DBInpReq
Pas de fonctionnalité BDE
DBLookup
Obsolète
DbOleCtl
Pas de fonctionnalité COM
DBPWDlg
Pas de fonctionnalité BDE
DBTables
Pas de fonctionnalité BDE
DdeMan
Pas de fonctionnalité DDE
DRTable
Pas de fonctionnalité BDE
ExtActns
Pas prise en charge actuellement
ExtDlgs
Pas de fonctionnalité de boîtes de dialogue d’images
FileCtrl
Obsolète
ListActns
Pas prise en charge actuellement
MConnect
Pas de fonctionnalité COM
Messages
Pas de messages Windows
MidasCon
Obsolète
MPlayer
Pas de lecteur multimédia Windows
Mtsobj
Pas de fonctionnalité COM
MtsRdm
Pas de fonctionnalité COM
Mtx
Pas de fonctionnalité COM
mxConsts
Pas de fonctionnalité COM
ObjBrkr
Pas prise en charge actuellement
OleConstMay
Pas de fonctionnalité COM
OleCtnrs
Pas de fonctionnalité COM
OleCtrls
Pas de fonctionnalité COM
OLEDB
Pas de fonctionnalité COM
OleServer
Pas de fonctionnalité COM
Outline
Obsolète
Registry
Pas de fonctionnalité de registre Windows
ScktCnst
Remplacée par des sockets
ScktComp
Remplacée par des sockets
SConnect
Aucun protocole de connexion pris en charge
SHDocVw_ocx
Pas de fonctionnalité ActiveX
StdConvs
Pas prise en charge actuellement
Guide du développeur
Portage d’applications Windows vers Linux
Tableau 14.6 Unités de la VCL uniquement (suite)
Unité
Raison de son exclusion
SvcMgr
Pas de prise en charge des services Windows NT
TabNotbk
Obsolète
Tabs
Obsolète
ToolWin
Pas de fonctionnalité d’ancrage
ValEdit
Pas prise en charge actuellement
VarCmplx
Pas prise en charge actuellement
VarConv
Pas prise en charge actuellement
VCLCom
Pas de fonctionnalité COM
WebConst
Pas de constantes Windows
Windows
Pas d’appels de l’API Windows
Différences dans les constructeurs d’objets CLX
Lorsqu’un objet CLX est créé, soit implicitement en plaçant l’objet sur la fiche,
soit explicitement dans le code en utilisant le constructeur de l’objet, une
instance du widget sous-jacent associé est également créée. L’objet CLX possède
cette instance du widget. Si l’objet CLX est supprimé, le widget sous-jacent est
également supprimé. C’est le même type de fonctionnalité qu’offre la VCL dans
les applications Windows.
Lorsque vous créez explicitement un objet CLX dans votre code, en faisant des
appels dans la bibliothèque d’interface Qt comme QWidget_Create(), vous créez
une instance d’un widget Qt dont le propriétaire n’est pas un objet CLX. Cela
transmet l’instance d’un widget Qt existant à l’objet CLX à utiliser pendant sa
construction. Cet objet CLX n’est pas propriétaire du widget Qt qui lui est
transmis. Par conséquent, après avoir créé l’objet de cette manière, seul l’objet
CLX est détruit, et non l’instance du widget Qt sous-jacent. Ce comportement est
différent de celui de la VCL.
Certains objets graphiques CLX, tels que TBrush et TPen, vous permettent
d’assumer la propriété du widget sous-jacent en utilisant la méthode OwnHandle.
Après l’appel à OwnHandle, si vous supprimez l’objet CLX, le widget sous-jacent
est aussi supprimé.
Certaines affectations de propriétés dans la CLX ont été déplacées du
constructeur vers InitWidget. Cela permet de différer la construction de l’objet Qt
jusqu’à ce qu’elle soit vraiment nécessaire. Par exemple, supposons que vous
disposiez d’une propriété appelée Color. Dans SetColor, vous pouvez vérifier avec
HandleAllocated si vous disposez d’un Handle de Qt. Si le handle est alloué, vous
pouvez effectuer l’appel Qt pour définir la couleur. Sinon, vous pouvez stocker
la valeur dans une variable de champ privée, et dans InitWidget, vous définissez
la propriété.
Pour plus d’informations sur la construction d’objets, reportez-vous à
“Construction d’objets en C++Builder pour les classes VCL/CLX” à la page 13-8.
Développement d’applications multiplates-formes
14-13
Portage d’applications Windows vers Linux
Gestion des événements widget et système
Les événements widget et système, dont vous devez principalement vous
préoccuper lors de l’écriture des composants, sont gérés différemment par la
VCL et CLX. La principale différence réside dans le fait que les contrôles CLX ne
répondent pas directement aux messages Windows, même lorsqu’ils sont
exécutés sous Windows (voir chapitre 51, “Gestion des messages
et des notifications système”). A la place, ils répondent aux notifications
provenant de la couche du widget sous-jacent. Comme ces notifications utilisent
un système différent, l’ordre et la durée des événements peuvent parfois être
différents entre les objets CLX et VCL correspondants. Ceci se produit même si
votre application CLX s’exécute sous Windows plutôt que sous Linux. Si vous
portez une application VCL vers CLX, vous devrez peut-être modifier la façon
dont les gestionnaires d’événements répondent pour palier ces différences.
Pour plus d’informations sur l’écriture de composants qui répondent aux
événements widget et système (autres que ceux qui sont reflétés dans les
événements publiés des composants CLX), reportez-vous à “Réponse aux
notifications du système à l’aide de CLX” à la page 51-11.
Partage des fichiers source entre Windows et Linux
Si vous voulez que votre application s’exécute sous Windows et sous Linux,
vous pouvez partager les fichiers source en les rendant accessibles aux deux
systèmes d’exploitation. Vous pouvez procéder de plusieurs manières, par
exemple en plaçant les fichiers sur un serveur accessible aux deux ordinateurs ou
en utilisant Samba sur la machine Linux pour permettre d’accéder aux fichiers
au travers d’un partage réseau Microsoft pour Linux et Windows. Vous pouvez
choisir de conserver le source sous Linux et de créer un partage sous Linux. Ou
vous pouvez conserver le source sous Windows et créer un partage sous
Windows pour que la machine Linux puisse y accéder.
Vous pouvez continuer à développer et compiler le fichier dans C++Builder en
utilisant des objets qui sont pris en charge par la VCL et la CLX. Quand vous
avez terminé, vous pouvez effectuer la compilation sous Windows. Si une
solution Linux C++ est disponible, vous pouvez compiler votre application sous
Linux.
Si vous créez une nouvelle application, C++Builder crée un fichier fiche .xfm à la
place d’un fichier .dfm.
Différences d’environnement entre Windows et Linux
Habituellement, multiplate-forme signifie qu’une application peut être compilée
théoriquement sans aucune modification sous les systèmes d’exploitation
Windows et Linux.
14-14
Guide du développeur
Portage d’applications Windows vers Linux
Le tableau suivant présente les différences entre les systèmes d’exploitation
Linux et Windows.
Tableau 14.7 Différences dans les environnements d’exploitation Linux et Windows
Différence
Description
Nom de fichier
sensible à la casse
Dans Linux, les noms de fichiers sont sensibles à la casse. Le fichier
Test.txt n’est pas identique au fichier test.txt. Vous devez faire très
attention aux lettres majuscules dans les noms de fichiers sous Linux.
Caractères de fin
de ligne
Dans Windows, les lignes d’un texte se terminent par CR/LF (c’est-àdire, ASCII 13 + ASCII 10), alors qu’elles se terminent par LF dans
Linux. Bien que l’éditeur de code soit capable de gérer la différence,
vous devez être conscient de cela lorsque vous importez du code
depuis Windows.
Caractère de fin
de fichier
Dans MS-DOS et dans Windows, le caractère de valeur #26 (Ctrl-Z) est
considéré comme la fin du fichier texte, même s’il existe des données
dans le fichier après ce caractère. Linux utilise Ctrl+D comme caractère
de fin de fichier.
Fichiers
de traitement par
lots/scripts shell
L’équivalent Linux du fichier .bat est le script shell. Un script est un
fichier texte contenant des instructions enregistrées et exécutables à
l’aide de la commande chmod +x <fichierscript>. Le langage de script
dépend du shell que vous utilisez sous Linux. Bash est communément
utilisé.
Confirmation
de commande
Sous MS-DOS ou Windows, si vous tentez de détruire un fichier ou un
dossier, une confirmation est demandée (“Etes-vous sûr ?”). Linux ne
demande généralement pas de confirmation ; il effectue simplement
l’opération. Cela accentue le risque de destruction accidentelle d’un
fichier ou de tout un système de fichiers. Il n’existe aucun moyen
d’annuler une suppression sous Linux à moins d’avoir sauvegardé un
fichier sur un autre support.
Retour de
commande
Si une commande réussit sous Linux, son invite est réaffichée sans
message d’état.
Commutateurs
de commande
Linux utilise un tiret simple (-) pour représenter les commutateurs de
commande ou un tiret double (--) pour les options à plusieurs
caractères, là où DOS utilise une barre oblique droite (/) ou un tiret (-).
Fichiers
de configuration
Sous Windows, la configuration s’effectue dans le registre ou dans des
fichiers comme autoexec.bat.
Sous Linux, les fichiers de configuration sont créés sous la forme de
fichiers cachés dans le répertoire home de l’utilisateur. Les fichiers de
configuration du répertoire /etc ne sont généralement pas cachés.
Linux utilise également des variables d’environnement telles que
LD_LIBRARY_PATH (chemin de recherche pour les bibliothèques).
Autres variables d’environnement importantes :
HOMEVotre répertoire home (/home/sam)
TERMType de terminal (xterm, vt100, console)
SHELLChemin vers votre shell (/bin/bash)
USERVotre nom de connexion (sfuller)
PATHListe de recherche pour les programmes
Ces variables sont spécifiées dans le shell ou dans des fichiers tels que
.bashrc.
Développement d’applications multiplates-formes
14-15
Portage d’applications Windows vers Linux
Tableau 14.7 Différences dans les environnements d’exploitation Linux et Windows (suite)
14-16
Différence
Description
DLL
Sous Linux, vous utilisez des fichiers d’objets partagés (.so). Sous
Windows, il s’agit de bibliothèques de liens dynamiques (DLL).
Lettres de lecteurs
Linux ne possède pas de lettres de lecteurs. Voici un exemple de nom
de chemin Linux :
/lib/security. Voir DriveDelim dans la bibliothèque d’exécution.
Exceptions
Les exceptions du système d’exploitation sont appelées signaux sous
Linux.
Fichiers
exécutables
Sous Linux, les fichiers exécutables ne nécessitent pas d’extension. Sous
Windows, les fichiers exécutables possèdent l’extension exe.
Extensions de nom
de fichier
Linux n’utilise pas d’extensions de nom de fichier pour identifier les
types de fichiers ou associer les fichiers à des applications.
Permissions
de fichiers
Sous Linux, des permissions de lecture, d’écriture et d’exécution sont
affectées aux fichiers (et aux répertoires) pour le propriétaire du fichier,
le groupe et les autres. Par exemple,
-rwxr-xr-x signifie, de la gauche vers la droite :
- est le type de fichier (- = fichier ordinaire, d = répertoire, l = lien) ;
rwx représente les permissions pour le propriétaire du fichier (lecture,
écriture, exécution), r-x représente les permissions pour le groupe du
propriétaire du fichier (lecture, exécution) et r-x représente les
permissions pour tous les autres utilisateurs (lecture, exécution).
L’utilisateur root (super-utilisateur) peut outrepasser ces permissions.
Vous devez vous assurer que votre application s’exécute sous
l’utilisateur correct et dispose d’un droit d’accès correct aux fichiers
requis.
Utilitaire Make
L’utilitaire make de Borland n’est pas disponible sur la plate-forme
Linux. A la place, vous pouvez utiliser l’utilitaire make GNU de Linux.
Multitâche
Linux prend totalement en charge le fonctionnement multitâche. Vous
pouvez exécuter en même temps plusieurs programmes (appelés
processus sous Linux). Vous pouvez lancer des processus en arrièreplan (en utilisant & après la commande) et continuer immédiatement à
travailler. Linux vous permet également de disposer de plusieurs
sessions.
Noms de chemins
Linux utilise une barre oblique droite (/) là où DOS utilise une barre
oblique inverse (\). Une constante PathDelim peut être utilisée pour
spécifier le caractère adapté à la plate-forme. Voir PathDelim dans la
bibliothèque d’exécution.
Chemin
de recherche
Lors de l’exécution de programmes, Windows consulte toujours en
premier le répertoire en cours, puis il examine la variable
d’environnement PATH. Linux ne consulte jamais le répertoire en cours
mais recherche uniquement les répertoires énumérés dans PATH. Pour
exécuter un programme du répertoire en cours, vous devez
généralement taper ./ avant.
Vous pouvez également modifier votre PATH pour inclure ./ comme
premier chemin à rechercher.
Guide du développeur
Portage d’applications Windows vers Linux
Tableau 14.7 Différences dans les environnements d’exploitation Linux et Windows (suite)
Différence
Description
Séparateur
de chemin
de recherche
Windows utilise le point-virgule comme séparateur de chemin de
recherche. Linux utilise deux points. Voir PathDelim dans la
bibliothèque d’exécution.
Liens symboliques
Sous Linux, un lien symbolique est un fichier spécial qui pointe sur un
autre fichier du disque. En plaçant les liens symboliques dans le
répertoire global bin qui contient les principaux fichiers de votre
application, vous n’aurez pas à modifier le chemin de recherche
système. Un lien symbolique se crée à l’aide de la commande ln (link).
Windows possède des raccourcis pour le bureau de l’interface
utilisateur graphique. Pour mettre un programme à la disposition de la
ligne de commande, les programmes d’installation Windows modifient
généralement le chemin de recherche système.
Structure de répertoires sous Linux
Les répertoires sont différents sous Linux. N’importe quel fichier ou périphérique
peut être monté n’importe où dans le système de fichiers.
Remarque
Les noms de chemins Linux utilisent des barres obliques droites contrairement à
Windows qui utilise des barres obliques inverses. La barre oblique initiale
représente le répertoire root.
Voici quelques répertoires couramment utilisés dans Linux.
Tableau 14.8 Répertoires Linux courants
Répertoire
Contenu
/
Le répertoire root ou de niveau supérieur de tout le système de
fichiers Linux
/root
Le système de fichiers root ; le répertoire home du super-utilisateur
/bin
Commandes, utilitaires
/sbin
Utilitaires système
/dev
Périphériques représentés comme des fichiers
/lib
Bibliothèques
/home/username
Fichiers appartenant à l’utilisateur où nom_utilisateur est le nom de
connexion de l’utilisateur
/opt
Facultatif
/boot
Noyau appelé lorsque le système démarre
/etc
Fichiers de configuration
/usr
Applications, programmes. Inclut généralement des répertoires
comme /usr/spool, /usr/man, /usr/include, /usr/local
/mnt
Autre support monté sur le système, comme un CD-ROM ou un
lecteur de disquettes
/var
Journaux, messages, fichiers de spool
/proc
Système de fichiers virtuels et statistiques du système
/tmp
Fichiers temporaires
Développement d’applications multiplates-formes
14-17
Portage d’applications Windows vers Linux
Remarque
Les différentes distributions de Linux installent parfois les fichiers à différents
emplacements. Un programme utilitaire peut être installé dans /bin avec une
distribution Red Hat et dans /usr/local/bin avec une distribution Debian.
Reportez-vous à www.pathname.com pour des détails supplémentaires sur
l’organisation du système de fichiers hiérarchique UNIX/Linux. Vous pourrez
également consulter le document Filesystem Hierarchy Standard.
Ecriture de code portable
Si vous écrivez des applications multiplates-formes destinées à être exécutées
sous Windows et Linux, vous pouvez écrire du code qui se compilera sous
différentes conditions. En utilisant la compilation conditionnelle, vous pouvez
conserver votre codage Windows, en prenant toujours en considération les
différences du système d’exploitation Linux.
Pour créer des applications facilement portables entre Windows et Linux,
pensez à :
• Réduire ou isoler les appels aux API propres à une plate-forme (Win32 ou
Linux) ; utilisez les méthodes CLX ou les appels à la bibliothèque Qt.
• Eliminer les constructions de messagerie Windows (PostMessage,
SendMessage) dans une application. Dans la CLX, appelez les méthodes
QApplication_postEvent et QApplication_sendEvent à la place. Pour plus
d’informations sur l’écriture de composants qui répondent aux événements
widget et système, reportez-vous à “Réponse aux notifications du système à
l’aide de CLX” à la page 51-11.
• Utiliser TMemIniFile à la place de TRegIniFile.
• Respecter et conserver la distinction minuscule/majuscule pour les noms de
fichiers et de répertoires.
• Porter tout code TASM assembleur externe. L’assembleur GNU “as” ne prend
pas en charge la syntaxe TASM. (Consultez “Inclusion de code assembleur
inline” à la page 14-21.)
Essayez d’écrire le code de manière à utiliser les routines de la bibliothèque
d’exécution indépendantes de la plate-forme et utilisez les constantes de System,
SysUtils et des autres unités de la bibliothèque d’exécution. Par exemple, utilisez
la constante PathDelim pour que votre code ne soit pas affecté par les différences
de plate-forme de ’/’ par rapport à ’\’.
Un autre exemple concerne l’utilisation de caractères multi-octets sur les deux
plates-formes. Traditionnellement, le code Windows attend uniquement 2 octets
par caractère multi-octet. Sous Linux, les caractères multi-octets peuvent
comporter beaucoup plus d’octets (jusqu’à 6 octets pour UTF-8). Les deux platesformes peuvent être adaptées en utilisant la fonction StrNextChar dans SysUtils.
Le code Windows existant suivant
while(*p != 0)
{
if(LeadBytes.Contains(*p))
14-18
Guide du développeur
Portage d’applications Windows vers Linux
p++;
p++;
}
peut être remplacé par un code indépendant de la plate-forme comme celui-ci :
while(*p != 0)
{
if(LeadBytes.Contains(*p))
p = StrNextchar(p);
else
p++;
}
Cet exemple est portable d’une plate-forme à l’autre et évite toujours de nuire
aux performances en appelant une procédure pour les environnement régionaux
non multi-octets.
Si l’utilisation de fonctions de bibliothèque d’exécution n’est pas une solution
envisageable, essayez d’isoler le code propre à une plate-forme dans une partie
de votre routine ou une sous-routine. Essayez de limiter le nombre de blocs
#ifdef pour conserver la lisibilité et la portabilité du code source. Le symbole
conditionnel WIN32 n’est pas défini sous Linux. Le symbole conditionnel LINUX
est défini, pour indiquer que le code source est compilé pour la plate-forme
Linux.
Utilisation des directives conditionnelles
L’utilisation des directives de compilation #ifdef est une méthode recommandée
pour introduire des conditions dans votre code pour les plates-formes Windows
et Linux. Toutefois, comme les #ifdef rendent le code source plus difficile à
comprendre et à maintenir, vous devez savoir à quelles occasions il est
raisonnable d’utiliser des #ifdef. En envisageant l’utilisation des #ifdef, les
meilleures questions à se poser devraient être “Pourquoi ce code a-t-il besoin
d’un #ifdef ?” et “Ce traitement peut-il s’écrire sans #ifdef ?”
Suivez ces lignes directrices pour utiliser des #ifdef dans des applications
multiplates-formes :
• N’essayez pas d’utiliser de #ifdef sans absolue nécessité. Les #ifdef dans un
fichier source sont uniquement évalués lorsque le code source est compilé. C/
C++ a besoin des sources d’unités (fichiers en-tête) pour compiler un projet.
La reconstruction totale de tout le code source est un événement rare pour la
plupart des projets C++Builder.
• N’utilisez pas de #ifdef dans les fichiers paquet (.bpk). Limitez leur utilisation
aux fichiers source. Les créateurs de composants doivent créer deux paquets
de conception lors du développement multiplate-forme, et non un seul paquet
avec des #ifdef.
• En général, utilisez #ifdef WINDOWS pour tester n’importe quelle plate-forme
Windows, y compris WIN32. Réservez l’utilisation de #ifdef WIN32 à la
distinction entre des plates-formes Windows spécifiques, comme Windows
32 bits et 64 bits. Ne limitez pas votre code à WIN32 à moins d’être sûr qu’il
ne fonctionne pas sous WIN64.
Développement d’applications multiplates-formes
14-19
Portage d’applications Windows vers Linux
• Evitez les tests négatifs du type #ifndef si ce n’est pas absolument nécessaire.
#ifndef __linux__ n’est pas équivalente à #ifdef WINDOWS.
• Evitez les combinaisons #ifndef/#else. Utilisez plutôt un test positif (#ifdef)
pour une meilleure lisibilité.
• Evitez les clauses #else sur des #ifdef sensibles à la plate-forme. Utilisez des
blocs #ifdef distincts pour Linux et du code spécifique à Windows au lieu de
#ifdef __linux__/#else ou #ifdef WINDOWS/#else.
Par exemple, un ancien code peut contenir
#ifdef WIN32
(Code Windows 32 bits)
#else
(Code Windows 16 bits)
#endif
//!! Linux pourra tomber par erreur dans ce code.
Pour tout code non portable dans des #ifdef, il est préférable que le code
source échoue à la compilation que de voir la plate-forme tomber dans une
clause #else et échouer mystérieusement à l’exécution. Les échecs de
compilation sont plus faciles à résoudre que les erreurs à l’exécution.
• Utilisez la syntaxe #if pour les tests compliqués. Remplacez les #ifdef
imbriqués par une expression booléenne dans une directive #if. Terminez la
directive #if par #endif. Cela vous permet de placer des expressions #if dans
des #ifdef pour dissimuler la nouvelle syntaxe #if aux compilateurs
précédents.
Toutes les directives conditionnelles sont documentées dans l’aide en ligne. Pour
plus d’informations, consultez également la rubrique “Compilation
conditionnelle” dans l’aide.
Emission de messages
La directive de compilation #pragma message permet au code source d’émettre
des avertissements et des erreurs exactement comme le compilateur. Utilisez-la
pour spécifier un message défini par l’utilisateur dans votre code de
programmation, dans l’un des formats suivants.
Si vous avez un nombre variable de constantes chaîne, utilisez :
#pragma message( "bonjour vous" )
#pragma message( "bonjour" " vous" )
Pour écrire un texte après un message, utilisez :
#pragma message texte
Pour développer une valeur précédemment définie, utilisez :
#pragma message (texte)
#define text "une chaîne de test"
#pragma message (texte)
Par exemple, pour afficher ces messages sur un bouton, utilisez :
void __fastcall TForm1::Button1Click(TObject *Sender)
14-20
Guide du développeur
Portage d’applications Windows vers Linux
{
#pragma message( "bonjour vous 1" )
#pragma message( "bonjour vous 2" )
#pragma message bonjour vous 3
#define text "une chaîne de test"
#pragma message (texte)
}
Pour afficher les messages dans l’EDI, choisissez Projets|Options|Compilateur,
cliquez sur l’onglet Compilateur, puis cochez la case Afficher les messages
généraux.
Inclusion de code assembleur inline
Si vous incluez du code assembleur inline dans vos applications Windows, il est
possible que vous ne puissiez pas utiliser le même code sous Linux à cause des
exigences du code indépendant de la position (PIC) sous Linux. Les bibliothèques
d’objets partagés Linux (équivalentes aux DLL) nécessitent que tout le code soit
relogeable en mémoire sans modification. Cela affecte principalement les routines
assembleur inline qui utilisent des variables globales ou d’autres adresses
absolues, ou qui appellent des fonctions externes.
Pour les unités qui contiennent uniquement du code C++, le compilateur génère
automatiquement un PIC lorsque c’est nécessaire. Il est recommandé de compiler
chaque fichier source aux formats PIC et non PIC ; utilisez le commutateur de
compilation -VP pour générer du PIC.
Il est possible que vous souhaitiez coder différemment des routines assembleur
selon que vous compilez un exécutable ou une bibliothèque partagée ; utilisez
#ifdef__PIC__ pour fusionner les deux versions de votre code assembleur. Vous
pouvez aussi réécrire la routine en C++ pour éviter le problème.
Les règles PIC pour le code assembleur inline sont les suivantes :
• PIC nécessite que toutes les références mémoire soient relatives au registre
EBX, qui contient le pointeur d’adresse de base du module en cours (dans
Linux, ce pointeur s’appelle Global Offset Table ou GOT). Ainsi, plutôt que
MOV EAX,GlobalVar
utilisez
MOV EAX,[EBX].GlobalVar
• PIC impose de préserver le registre EBX d’un appel à l’autre dans votre code
assembleur (comme sous Win32), et de restaurer également le registre EBX
avant d’effectuer des appels à des fonctions externes (à l’inverse de Win32).
• Même si le code PIC fonctionnera dans des exécutables de base, il peut
ralentir les performances et générer plus de code. Vous n’avez pas le choix
pour les objets partagés, mais dans les exécutables, vous souhaiterez
probablement toujours obtenir le plus haut niveau de performances possible.
Développement d’applications multiplates-formes
14-21
Applications de bases de données multiplates-formes
Différences de programmation sous Linux
Le type widechar Linux wchar_t comporte 32 bits par caractère. Le standard
Unicode sur 16 bits géré par la VCL et la CLX est un sous-ensemble du standard
UCS sur 32 bits supporté par Linux et les bibliothèques GNU. Les types
WideString doivent être traduits en 32 bits par caractère avant d’être transmis à
une fonction du système d’exploitation en tant que wchar_t.
Sous Linux, les widestrings sont comptées par référence comme les chaînes
longues (alors que ce n’est pas le cas sous Windows).
Sous Windows, les caractères multi-octets (MBCS) sont représentés sous la forme
de codes de caractères de 1 et 2 octets. Sous Linux, ils sont représentés par 1 à
6 octets.
Les AnsiStrings peut transporter des séquences de caractères multi-octets, en
fonction des paramètres régionaux de l’utilisateur. Le codage Linux pour les
caractères multi-octets comme le japonais, le chinois, l’hébreu et l’arabe peuvent
être incompatibles avec le codage Windows pour ces mêmes paramètres
régionaux. Unicode est portable, alors que le multi-octet ne l’est pas. Pour plus
d’informations sur la gestion des chaînes de plusieurs paramètres régionaux dans
des applications internationales, reportez-vous à “Codage de l’application” à la
page 16-2.
Applications de bases de données multiplates-formes
Sous Windows, C++Builder fournit plusieurs méthodes pour accéder aux
informations de bases de données. Cela inclut l’utilisation d’ADO, du moteur de
bases de données Borland (BDE) et d’InterBase Express. Sous Windows et Linux,
vous pouvez utiliser dbExpress, une nouvelle technologie d’accès aux données
multiplates-formes, selon l’édition de C++Builder que vous possédez.
Avant de porter une application de base de données vers dbExpress pour qu’elle
s’exécute sous Linux, vous devez comprendre les différences entre l’utilisation de
dbExpress et le mécanisme d’accès aux données que vous utilisiez. Ces différences
se situent à différents niveaux.
• Au niveau le plus bas, il existe une couche qui communique entre votre
application et le serveur de base de données. Cela peut être ADO, le moteur
BDE, ou le logiciel client InterBase. Cette couche est remplacée par dbExpress,
qui est un ensemble de pilotes légers pour le traitement SQL dynamique.
• L’accès aux données de bas niveau est enveloppé dans un ensemble de
composants que vous ajoutez à des modules ou des fiches de données. Ces
composants incluent des composants de connexion à la base de données, qui
représentent la connexion à un serveur de base de données, et des ensembles
de données, qui représentent les données obtenues à partir du serveur. Bien
qu’il existe des différences très importantes, en raison de la nature
unidirectionnelle des curseurs dbExpress, elles sont moins prononcées à ce
14-22
Guide du développeur
Applications de bases de données multiplates-formes
niveau, car les ensembles de données partagent tous un ancêtre commun,
comme les composants de connexion de base de données.
• Au niveau de l’interface utilisateur, il y a moins de différences. Les contrôles
CLX orientés données sont conçus pour être autant que possible similaires aux
contrôles Windows correspondants. Les différences les plus importantes au
niveau de l’interface utilisateur résultent des modifications nécessaires à
l’utilisation des mises à jour en mémoire cache.
Pour des informations sur le portage d’applications de bases de données
existantes vers dbExpress, reportez-vous à “Portage d’applications de bases de
données vers Linux” à la page 14-26. Pour des informations sur la conception de
nouvelles applications dbExpress, voir chapitre 18, “Conception d’applications
de bases de données”.
Différences de dbExpress
Sous Linux, dbExpress gère la communication avec les serveurs de bases de
données. dbExpress se compose d’un ensemble de pilotes légers qui mettent en
œuvre un ensemble d’interfaces communes. Chaque pilote est un objet partagé
(fichier .so) qui doit être lié à votre application. Comme dbExpress est conçu pour
être multiplate-forme, il est également disponible sous Windows sous la forme
d’un ensemble de bibliothèques de liens dynamiques (.dll).
Comme avec n’importe quelle couche d’accès aux données, dbExpress a besoin du
logiciel client du fournisseur de base de données. De plus, il utilise un pilote
propre à la base de données, plus deux fichiers de configuration, dbxconnections
et dbxdrivers. C’est beaucoup moins que, par exemple, pour le moteur BDE, qui
nécessite la bibliothèque principale du moteur de bases de données Borland
(Idapi32.dll) plus un pilote propre à la base de données et un certain nombre
d’autres bibliothèques de gestion.
En outre, dbExpress :
• Fournit un chemin plus simple et plus rapide aux bases de données distantes.
Par conséquent, vous pouvez vous attendre à une amélioration sensible des
performances pour un accès aux données simple et direct.
• Traite les requêtes et les procédures stockées, mais il ne prend pas en charge
l’ouverture des tables.
• Renvoie uniquement des curseurs unidirectionnels.
• Ne dispose pas d’autre possibilité de mise à jour intégrée que l’exécution
d’une requête INSERT, DELETE ou UPDATE.
• N’a pas de mémoire cache pour les métadonnées ; l’interface d’accès aux
métadonnées lors de la conception est mise en œuvre à l’aide de l’interface
centrale d’accès aux données.
• Exécute uniquement des requêtes transmises par l’utilisateur, optimisant ainsi
l’accès à la base de données sans introduire de requêtes supplémentaires.
Développement d’applications multiplates-formes
14-23
Applications de bases de données multiplates-formes
• Gère un tampon d’enregistrement ou un bloc de tampons d’enregistrement de
manière interne. Il diffère en cela du moteur BDE, avec lequel les clients
doivent allouer la mémoire utilisée pour les enregistrements du tampon.
• Prend uniquement en charge les tables locales qui sont basées sur SQL
(comme InterBase et Oracle).
• Utilise des pilotes pour DB2, Informix, InterBase, MySQL, Oracle. Si vous
utilisez un serveur de base de données différent, vous devrez convertir vos
données sur l’une de ces bases de données, écrire un pilote dbExpress pour le
serveur que vous utilisez ou obtenir un pilote dbExpress tierce partie pour
votre serveur de base de données.
Différences au niveau composant
Lorsque vous écrivez une application dbExpress, celle-ci a besoin d’un ensemble
de composants d’accès aux données différent de ceux utilisés dans vos
applications de bases de données existantes. Les composants dbExpress partagent
les mêmes classes de base que d’autres composants d’accès aux données
(TDataSet et TCustomConnection), ce qui signifie qu’un grand nombre de
propriétés, de méthodes et d’événements sont les mêmes que pour les
composants utilisés dans vos applications existantes.
Le tableau suivant présente la liste des composants de bases de données
importants utilisés dans InterBase Express, le BDE et ADO dans l’environnement
Windows, et montre les composants dbExpress comparables pour une utilisation
sous Linux et dans des applications multiplates-formes.
Tableau 14.9 Composants d’accès aux données comparables
Composants
InterBase Express
Composants BDE
Composants ADO
Composants
dbExpress
TIBDatabase
TDatabase
TADOConnection
TSQLConnection
TIBTable
TTable
TADOTable
TSQLTable
TIBQuery
TQuery
TADOQuery
TSQLQuery
TIBStoredProc
TStoredProc
TADOStoredProc
TSQLStoredProc
TADODataSet
TSQLDataSet
TIBDataSet
Les ensembles de données de dbExpress (TSQLTable, TSQLQuery, TSQLStoredProc
et TSQLDataSet) sont toutefois plus limités que leurs équivalents, car ils ne
prennent pas en charge les modifications et permettent uniquement la navigation
vers l’avant. Pour plus de détails sur les différences entre les ensembles de
données dbExpress et les autres ensembles de données disponibles sous Windows,
voir chapitre 26, “Utilisation d’ensembles de données unidirectionnels”.
A cause de l’absence de prise en charge des modifications et de la navigation, la
plupart des applications dbExpress ne fonctionnent pas directement avec les
ensembles de données dbExpress. Elles connectent plutôt l’ensemble de données
dbExpress à un ensemble de données client, qui conserve les enregistrements dans
une mémoire tampon et assure une prise en charge des modifications et de la
14-24
Guide du développeur
Applications de bases de données multiplates-formes
navigation. Pour plus d’informations sur cette architecture, consultez
“Architecture des bases de données” à la page 18-6.
Remarque
Pour les applications très simples, vous pouvez utiliser TSQLClientDataSet à la
place d’un ensemble de données dbExpress connecté à un ensemble de données
client. Cela a pour avantage la simplicité, car il existe une correspondance
1 pour 1 entre l’ensemble de données de l’application que vous portez et
l’ensemble de données de l’application portée, mais cette solution est moins
souple qu’une connexion explicite entre un ensemble de données dbExpress et un
ensemble de données client. Pour la plupart des applications, il est recommandé
d’utiliser un ensemble de données dbExpress connecté à un composant
TClientDataSet.
Différences au niveau de l’interface utilisateur
Les contrôles CLX orientés données sont conçus pour être aussi similaires que
possible aux contrôles Windows correspondants. Par conséquent, le portage de la
partie interface utilisateur de vos applications de bases de données introduit
quelques considérations supplémentaires par rapport à celles du portage d’une
application Windows quelconque vers CLX.
Les principales différences au niveau de l’interface utilisateur proviennent des
différences dans la façon dont les ensembles de données dbExpress ou les
ensembles de données client fournissent les données.
Si vous utilisez uniquement des ensembles de données dbExpress, vous devez
ajuster votre interface utilisateur à cause du fait que les ensembles de données
ne prennent pas en charge la modification mais uniquement la navigation vers
l’avant. Il peut, par exemple, être nécessaire de supprimer des contrôles
permettant aux utilisateurs de se positionner sur un enregistrement précédent.
Comme les ensembles de données dbExpress ne gèrent pas de mémoire tampon
des données, vous ne pouvez pas afficher les données dans une grille orientée
données : vous pouvez seulement afficher un enregistrement à la fois.
Si vous avez connecté l’ensemble de données dbExpress à un ensemble de
données client, les éléments de l’interface utilisateur associés à la modification et
à la navigation devraient toujours fonctionner. Vous devez uniquement les
reconnecter à l’ensemble de données client. Dans ce cas, l’attention doit
principalement être mise sur la gestion de l’écriture des mises à jour dans la base
de données. Par défaut, la plupart des ensembles de données sous Windows
écrivent automatiquement les mises à jour sur le serveur de base de données
lorsqu’elles sont transmises (par exemple, lorsque l’utilisateur se déplace vers un
nouvel enregistrement). Les ensembles de données client, par contre, conservent
toujours les mises à jour en mémoire cache. Pour des informations sur la
manière de gérer ces différences, voir “Mise à jour des données dans les
applications dbExpress” à la page 14-28.
Développement d’applications multiplates-formes
14-25
Applications de bases de données multiplates-formes
Portage d’applications de bases de données vers Linux
Le portage de votre application de bases de données vers dbExpress vous permet
de créer une application multiplates-formes qui s’exécutera à la fois sous
Windows et sous Linux. La procédure de portage implique d’apporter des
modifications à votre application, car la technologie est différente. La difficulté
du portage dépend du type de l’application, de sa complexité et de ce qu’il est
nécessaire d’accomplir. Une application qui utilise largement les technologies
propres à Windows comme ADO sera plus difficile à porter qu’une autre
utilisant la technologie de base de données C++Builder.
Suivez ces étapes générales pour porter votre application de base de données
Windows/VCL vers Linux/CLX :
1 Assurez-vous que vos données sont stockées dans une base de données prise
en charge par dbExpress, comme par exemple DB2, Informix, InterBase,
MySQL, Oracle. Les données doivent résider sur l’un de ces serveurs SQL. Si
vos données ne se trouvent pas déjà dans l’une de ces bases de données,
transférez-les à l’aide d’un outil prévu à cet effet.
Par exemple, utilisez l’outil Data Pump de C++Builder (non disponible dans
toutes les éditions) pour convertir certaines bases de données (comme dBase,
FoxPro et Paradox) en bases de données prises en charge par dbExpress.
(Consultez le fichier datapump.hlp dans Program Files\Common Files\
Borland\Shared\BDE pour des informations sur l’emploi de cet utilitaire.)
2 Créez des modules de données contenant les ensembles de données et les
composants de connexion de façon à ce qu’ils soient séparés des fiches et
composants de votre interface utilisateur. Vous isolerez ainsi les parties de
votre application qui nécessitent un ensemble de composants totalement
nouveau dans les modules de données. Les fiches représentant l’interface
utilisateur pourront alors être portées comme n’importe quelle autre
application. Pour les détails, voir “Portage de votre application” à la page 14-3.
Les étapes suivantes supposent que vos ensembles de données et composants
de connexion sont isolés dans leurs propres modules de données.
3 Créez un nouveau module de données qui contiendra les versions CLX de vos
ensembles de données et composants de connexion.
4 Pour chaque ensemble de données de l’application d’origine, ajoutez un
ensemble de données dbExpress, un composant TDataSetProvider et un
composant TClientDataSet. Utilisez les correspondances du tableau 14.9 pour
décider de l’ensemble de données dbExpress à utiliser. Donnez à ces
composants des noms significatifs.
• Initialisez la propriété ProviderName du composant TClientDataSet avec le
nom du composant TDataSetProvider.
• Initialisez la propriété DataSet du composant TDataSetProvider avec
l’ensemble de données dbExpress.
14-26
Guide du développeur
Applications de bases de données multiplates-formes
• Modifiez la propriété DataSet de tous les composants sources de données
qui faisaient référence à l’ensemble de données d’origine afin qu’ils fassent
à présent référence à l’ensemble de données client.
5 Définissez les propriétés du nouvel ensemble de données pour qu’elles
correspondent à celles de l’ensemble de données d’origine :
• Si l’ensemble de données d’origine était un composant TTable, TADOTable
ou TIBTable, initialisez la propriété TableName du nouveau composant
TSQLTable avec la propriété TableName de l’ensemble de données d’origine.
Copiez également toutes les propriétés utilisées pour définir les liens
maître-détail ou spécifier des index. Les propriétés spécifiant des plages et
des filtres devraient être initialisées sur l’ensemble de données client plutôt
que sur le nouveau composant TSQLTable.
• Si l’ensemble de données d’origine est un composant TQuery, TADOQuery
ou TIBQuery, initialisez la propriété SQL du nouveau composant
TSQLQuery avec la propriété SQL de l’ensemble de données d’origine.
Initialisez la propriété Params du nouveau TSQLQuery conformément à la
valeur de la propriété Params ou Parameters de l’ensemble de données
d’origine. Si vous avez initialisé la propriété DataSource pour établir un lien
maître-détail, copiez-la également.
• Si l’ensemble de données d’origine était un composant TStoredProc,
TADOStoredProc ou TIBStoredProc, initialisez la propriété StoredProcName du
nouveau composant TSQLStoredProc avec la propriété StoredProcName ou
ProcedureName de l’ensemble de données d’origine. Initialisez la propriété
Params du nouveau composant TSQLStoredProc conformément à la valeur
de la propriété Params ou Parameters de l’ensemble de données d’origine.
6 Pour tout composant de connexion de base de données dans l’application
d’origine (TDatabase, TIBDatabase ou TADOConnection), ajoutez un composant
TSQLConnection au nouveau module de données. Vous devez également
ajouter un composant TSQLConnection pour chaque serveur de base de
données auquel vous vous connectiez sans composant de connexion (par
exemple, en utilisant la propriété ConnectionString sur un ensemble de données
ADO ou en initialisant la propriété DatabaseName de l’ensemble de données
BDE avec un alias BDE).
7 Pour chaque ensemble de données dbExpress positionné à l’étape 4, initialisez
sa propriété SQLConnection avec le composant TSQLConnection correspondant
à la connexion de base de données appropriée.
8 Sur chaque composant TSQLConnection, spécifiez les informations nécessaires à
l’établissement d’une connexion de base de données. Pour cela, double-cliquez
sur le composant TSQLConnection pour afficher l’éditeur de connexion et
affectez les valeurs correspondant aux paramètres appropriés. Si vous avez dû
transférer des données vers un nouveau serveur de base de données à
l’étape 1, spécifiez les paramètres appropriés pour ce nouveau serveur. Si vous
Développement d’applications multiplates-formes
14-27
Applications de bases de données multiplates-formes
utilisez le même serveur qu’auparavant, vous pouvez obtenir certaines de ces
informations sur le composant de connexion d’origine :
• Si l’application d’origine utilisait TDatabase, vous devez transférer les
informations qui apparaissent dans les propriétés Params et TransIsolation.
• Si l’application d’origine utilisait TADOConnection, vous devez transférer les
informations qui apparaissent dans les propriétés ConnectionString et
IsolationLevel.
• Si l’application d’origine utilisait TIBDatabase, vous devez transférer les
informations qui apparaissent dans les propriétés DatabaseName et Params.
• S’il n’y avait pas de composant de connexion d’origine, vous devez
transférer les informations associées à l’alias BDE ou qui apparaissaient
dans la propriété ConnectionString de l’ensemble de données.
Vous pouvez enregistrer cet ensemble de paramètres sous un nouveau nom de
connexion. Pour plus de détails sur cette procédure, voir “Contrôles des
connexions” à la page 21-3.
Mise à jour des données dans les applications dbExpress
Les applications dbExpress utilisent des ensembles de données client pour
prendre en charge la modification. Lorsque vous transmettez des modifications à
un ensemble de données client, celles-ci sont écrites dans le cliché en mémoire
des données de l’ensemble de données client, mais elles ne sont pas
automatiquement écrites sur le serveur de base de données. Si votre application
d’origine utilisait un ensemble de données client pour les mises à jour en
mémoire cache, vous n’avez rien à modifier pour prendre en charge la
modification sous Linux. Par contre, si vous vous basiez sur le comportement
par défaut de la plupart des ensembles de données sous Windows, qui consiste à
écrire les modifications sur le serveur de base de données lors de la transmission
des enregistrements, vous devrez apporter des changements pour prendre en
charge l’utilisation d’un ensemble de données client.
Deux méthodes sont possibles pour convertir une application qui ne conservait
pas auparavant les mises à jour en mémoire cache :
• Vous pouvez reproduire le comportement de l’ensemble de données sous
Windows en écrivant du code pour appliquer chaque enregistrement mis à
jour au serveur de base de données dès qu’il est transmis. Pour cela,
fournissez à l’ensemble de données client un gestionnaire d’événement
AfterPost qui appliquera la mise à jour au serveur de base de données :
void __fastcall TForm1::ClientDataSet1AfterPost(TDataSet *DataSet)
{
TClientDataSet *pCDS = dynamic_cast<TClientDataSet *>(DataSet);
if (pCDS)
pCDS->ApplyUpdates(1);
}
14-28
Guide du développeur
Applications de bases de données multiplates-formes
• Vous pouvez ajuster votre interface utilisateur pour traiter les mises à jour en
mémoire cache. Cette approche possède certains avantages, comme la
réduction du trafic réseau et de la durée des transactions. Toutefois, si vous
adoptez l’enregistrement des mises à jour en mémoire cache, vous devrez
décider du moment où ces mises à jour devront être ré-appliquées au serveur
de base de données, et apporter probablement des modifications à l’interface
utilisateur pour laisser les utilisateurs spécifier l’application des mises à jour
ou leur indiquer si leurs modifications ont été écrites dans la base de données.
En outre, comme les erreurs de mise à jour ne sont pas détectées quand
l’utilisateur transmet un enregistrement, vous devrez changer la manière dont
vous signalez de telles erreurs à l’utilisateur, afin qu’il puisse voir quelle mise
à jour a causé un problème, ainsi que le type de problème.
Si votre application d’origine utilisait la prise en charge fournie par le BDE ou
ADO pour conserver les mises à jour en mémoire cache, vous aurez besoin
d’apporter des modifications à votre code pour passer à l’utilisation d’un
ensemble de données client. Le tableau ci-dessous présente les propriétés, les
événements et les méthodes qui prennent en charge les mises à jour en mémoire
cache sur les ensembles de données BDE et ADO, ainsi que les propriétés,
méthodes et événements correspondants sur TClientDataSet.
Tableau 14.10
Propriétés, méthodes et événements pour les mises à jour en mémoire cache
Sur les ensembles
de données
BDE (ou
TDatabase)
Sur les
ensembles de
données ADO
Sur TClientDataSet
Utilisation
CachedUpdates
LockType
Non obligatoires,
les ensembles de
données client
conservent
toujours les mises
à jour en mémoire
cache.
Détermine si les mises à jour en
mémoire cache sont effectives.
Non géré
CursorType
Non géré
Indique à quel point l’ensemble
de données est isolé des
modifications sur le serveur.
UpdatesPending
Non géré
ChangeCount
Indique si la mémoire cache
locale contient des
enregistrements mis à jour qui
doivent être transmis à la base
de données.
UpdateRecordTypes
FilterGroup
StatusFilter
Indique le type
d’enregistrements mis à jour à
rendre visibles lors de la
transmission de mises à jour en
mémoire cache.
UpdateStatus
RecordStatus
UpdateStatus
Indique si un enregistrement est
inchangé, modifié, inséré ou
supprimé.
Développement d’applications multiplates-formes
14-29
Applications Internet multiplates-formes
Tableau 14.10
Propriétés, méthodes et événements pour les mises à jour en mémoire cache (suite)
Sur les ensembles
de données
BDE (ou
TDatabase)
Sur les
ensembles de
données ADO
Sur TClientDataSet
Utilisation
OnUpdateError
Non géré
OnReconcileError
Evénement pour traiter les
erreurs de mise à jour
enregistrement par
enregistrement.
ApplyUpdates
(sur un ensemble
de données ou
une base de
données)
UpdateBatch
ApplyUpdates
Transmet les enregistrements de
la mémoire cache locale à la
base de données.
CancelUpdates
CancelUpdates ou
CancelBatch
CancelUpdates
Efface les mises à jour en cours
dans la mémoire cache sans les
transmettre.
CommitUpdates
Gérés
automatiquement
Reconcile
Réinitialise la mémoire cache de
mise à jour après la transmission
réussie des mises à jour.
FetchAll
Non géré
GetNextPacket
(et PacketRecords)
Copie des enregistrements de
bases de données dans la
mémoire cache locale à des fins
de modification et de mise à
jour.
RevertRecord
CancelBatch
RevertRecord
Annule les mises à jour de
l’enregistrement en cours si elles
ne sont pas encore effectuées.
Applications Internet multiplates-formes
Une application Internet est une application client/serveur qui utilise des
protocoles Internet standard pour connecter le client au serveur. Comme vos
applications utilisent des protocoles Internet standard pour les communications
client/serveur, vous pouvez rendre ces applications multiplates-formes. Par
exemple, un programme côté serveur pour une application Internet communique
avec le client par l’intermédiaire du logiciel serveur Web de la machine.
L’application serveur est généralement écrite pour Linux ou Windows, mais elle
peut aussi être multiplates-formes. Les clients peuvent se trouver sur n’importe
quelle plate-forme.
C++Builder vous permet de créer des applications de serveur Web sous la forme
d’applications CGI ou Apache pour un déploiement futur sous Linux. Sous
Windows, vous pouvez créer d’autres types de serveurs Web comme des DLL
Microsoft Server (ISAPI), des DLL Netscape Server (NSAPI) et des applications
CGI Windows. Les applications strictement CGI et quelques applications utilisant
WebBroker seront les seules à s’exécuter à la fois sous Windows et sous Linux.
14-30
Guide du développeur
Applications Internet multiplates-formes
Portage d’applications Internet vers Linux
Si vous disposez d’applications Internet existantes que vous souhaitez rendre
indépendantes des plates-formes, vous pouvez porter votre application de
serveur Web vers Linux ou créer une application nouvelle sous Linux quand une
solution Borland C++ est disponible. Voir chapitre 32, “Création d’applications
serveur Internet”, pour des informations sur l’écriture de serveurs Web. Si votre
application utilise WebBroker, écrit dans l’interface WebBroker et n’utilise pas
d’appels API natifs, elle ne sera pas aussi difficile à porter vers Linux.
Si votre application écrit dans ISAPI, NSAPI, CGI Windows ou d’autres API
Web, elle sera plus difficile à porter. Vous devrez effectuer des recherches dans
vos fichiers source et convertir ces appels API en appels Apache (voir .\Include\
Vcl\httpd.hpp pour des prototypes de fonctions pour les API Apache) ou CGI.
Vous devez également apporter les autres modifications suggérées dans “Portage
d’applications Windows vers Linux” à la page 14-2.
Développement d’applications multiplates-formes
14-31
14-32
Guide du développeur
Chapitre
15
Utilisation des paquets
et des composants
Chapitre 15
Un paquet est une bibliothèque liée dynamiquement spéciale, utilisée par les
applications C++Builder, l’EDI ou les deux. Les paquets d’exécution fournissent
des fonctionnalités lorsqu’un utilisateur exécute une application. Les paquets de
conception sont utilisés pour installer des composants dans l’EDI et pour créer des
éditeurs de propriétés particuliers pour des composants personnalisés. Un même
paquet peut fonctionner à la fois en conception et en exécution, les paquets de
conception faisant souvent appel à des paquets d’exécution. Pour les distinguer
des autres DLL, les paquets sont stockés dans des fichiers dont l’extension est
.bpl (Borland Package Library).
Comme les autres bibliothèques d’exécution, les paquets contiennent du code
pouvant être partagé par plusieurs applications. Par exemple, les composants
C++Builder les plus couramment utilisés se trouvent dans un paquet appelé vcl.
Chaque fois que vous créez une nouvelle application par défaut, elle utilise
automatiquement vcl. Lorsque vous compilez une application créée de cette
manière, l’image exécutable de l’application ne contient que son propre code et
ses propres données, le code commun se trouvant dans le paquet d’exécution
vcl60.bpl. Un ordinateur sur lequel sont installées plusieurs applications utilisant
des paquets n’a besoin que d’une seule copie de vcl60.bpl, partagée par toutes
les applications et par l’EDI de C++Builder lui-même.
C++Builder est livré avec plusieurs paquets d’exécution qui encapsulent les
composants VCL et CLX. C++Builder utilise également des paquets de
conception pour manipuler des composants dans l’EDI.
Vous pouvez construire des applications avec ou sans paquets. Mais si vous
voulez ajouter à l’EDI des composants personnalisés, vous devez les installer en
tant que paquets de conception.
Vous pouvez créer vos propres paquets d’exécution afin de les partager entre
plusieurs applications. Si vous écrivez des composants C++Builder, vous pouvez
Utilisation des paquets et des composants
15-1
Pourquoi utiliser des paquets ?
construire vos composants pour produire des paquets de conception avant de
les installer.
Pourquoi utiliser des paquets ?
Les paquets de conception simplifient la distribution et l’installation de
composants personnalisés. L’utilisation, optionnelle, des paquets d’exécution offre
plusieurs avantages par rapport à la programmation conventionnelle. En
construisant sous forme de bibliothèque d’exécution du code réutilisé, vous
pouvez le partager entre plusieurs applications. Par exemple, toutes vos
applications, y compris C++Builder lui-même, peuvent accéder aux composants
standard par le biais des paquets. Comme les applications n’intègrent pas de
copies séparées de la bibliothèque des composants dans leur exécutable, ces
derniers sont plus petits, ce qui économise à la fois de l’espace disque et des
ressources système. De plus, les paquets permettent d’accélérer la compilation,
car seul le code spécifique à l’application est compilé à chaque génération.
Les paquets et les DLL standard
Créez un paquet lorsque vous voulez qu’un composant personnalisé soit
utilisable dans l’EDI. Créez une DLL standard lorsque vous voulez générer une
bibliothèque utilisable par n’importe quelle application, quel que soit l’outil de
développement utilisé pour la créer.
Le tableau suivant énumère les types de fichier associés aux paquets :
Tableau 15.1 Fichiers paquet
Extension
de fichier
15-2
Contenu
bpk
Le fichier source des options du projet. Ce fichier représente la partie XML
du projet paquet. Les fichiers NomProjet.bpk et NomProjet.cpp combinés sont
utilisés pour gérer des paramètres, des options et des fichiers utilisés par le
projet de paquet.
bpl
Le paquet d’exécution. Ce fichier est un fichier .dll Windows avec des
caractéristiques propres à C++Builder. Le nom de base du .bpl est le nom
de base du fichier source bpk.
cpp
NomProjet.cpp contient le point d’entrée du paquet. De plus, chaque
composant contenu dans le paquet se trouve généralement dans un fichier
.cpp.
h
Le fichier d’en-tête ou d’interface pour le composant. NomComposant.h
accompagne NomComposant.cpp.
lib
Bibliothèque statique ou collection de fichiers .obj utilisée à la place d’un
fichier .bpi lorsque l’application n’utilise pas de paquets d’exécution.
Générée uniquement si -Gl (Générer fichier .LIB) est sélectionné.
Guide du développeur
Paquets d’exécution
Tableau 15.1 Fichiers paquet (suite)
Extension
de fichier
Contenu
obj
Image binaire d’un fichier d’unité contenu dans un paquet. Si nécessaire, un
fichier obj est créé pour chaque fichier cpp.
bpi
Bibliothèque d’importation de paquet Borland. Un fichier .bpi est créé pour
chaque paquet. Les fichiers bpi sont pour les fichiers bpl ce que les
bibliothèques d’importation sont pour les dll. Ce fichier est transmis au lieur
par les applications utilisant le paquet pour résoudre les références aux
fonctions du paquet. Le nom de base du bpi est le même que celui du
fichier source du paquet.
Vous pouvez inclure les types de composants VCL et/ou CLX dans un paquet.
Les paquets destinés à être multi-plates-formes ne doivent inclure que des
composants CLX.
Remarque
Les paquets partagent leurs données globales avec les autres modules d’une
application.
Paquets d’exécution
Les paquets d’exécution sont déployés avec les applications C++Builder. Ils
fournissent des fonctionnalités lorsqu’un utilisateur exécute une application.
Pour exécuter une application utilisant des paquets, le fichier exécutable de
l’application et tous les fichiers paquet (fichiers .bpl) qu’elle utilise doivent se
trouver sur l’ordinateur. Les fichiers .bpl doivent être dans le chemin du système
pour qu’une application puisse les utiliser. Quand vous déployez une
application, vérifiez que les utilisateurs possèdent la version correcte de chaque
bpl nécessaire.
Utilisation des paquets dans une application
Pour utiliser des paquets dans une application :
1 Chargez ou créez un projet dans l’EDI.
2 Choisissez Projet|Options.
3 Choisissez l’onglet Paquets.
4 Cochez la case “Construire avec les paquets d’exécution” et saisissez un ou
plusieurs noms de paquets dans la boîte de saisie placée en dessous. Les
paquets d’exécution associés aux paquets de conception déjà installés
apparaissent déjà dans la boîte de saisie.
5 Pour ajouter un paquet à une liste existante, cliquez sur le bouton Ajouter
puis entrez le nom du nouveau paquet dans la boîte de dialogue Ajout de
paquet d’exécution. Pour parcourir la liste des paquets disponibles, cliquez sur
Utilisation des paquets et des composants
15-3
Paquets d’exécution
le bouton Ajouter puis sur le bouton Parcourir placé à côté de la boîte de
saisie Nom de paquet dans la boîte de dialogue Ajout de paquet d’exécution.
Si vous modifiez le contenu de la boîte de saisie Chemin de recherche dans la
boîte de dialogue Ajout de paquet d’exécution, le chemin d’accès global à la
bibliothèque C++Builder est modifié.
Il n’est pas nécessaire d’inclure l’extension de fichier dans les noms de paquet
(ni le numéro de version représentant la version de C++Builder) ; autrement
dit, vcl60.bpl s’écrit vcl. Si vous tapez les noms directement dans la boîte de
saisie Paquets d’exécution, séparez-les par des points-virgules. Par exemple :
rtl;vcl;vcldb;vclado;vclx;vclbde;
Les paquets énumérés dans la boîte de saisie Paquets d’exécution sont
automatiquement liés à votre application. Les paquets en double sont ignorés et
si la case à cocher Construire avec les paquets d’exécution est désactivée,
l’application est liée sans paquet.
Les paquets d’exécution sont sélectionnés uniquement pour le projet en cours.
Pour que les choix en cours deviennent les choix par défaut pour les projets
futurs, cochez la case Défaut, en bas de la boîte de dialogue.
Une application utilisant des paquets doit toujours inclure les fichiers d’en-tête
des unités empaquetées qu’elle utilise. Ainsi, une application utilisant les
contrôles de base de données nécessite l’instruction :
#include "vcldb.h"
même si elle utilise le paquet vcldb. Dans les fichiers source générés, C++Builder
crée automatiquement ces instructions #include.
Paquets chargés dynamiquement
Pour charger un paquet à l’exécution, appelez la fonction LoadPackage.
LoadPackage charge le paquet, recherche les unités dupliquées et appelle les blocs
d’initialisation de toutes les unités contenues dans le paquet. Par exemple, le
code suivant est exécuté lorsqu’un fichier est choisi dans la boîte de dialogue de
sélection de fichiers.
if (OpenDialog1->Execute())
PackageList->Items->AddObject(OpenDialog1->FileName, (TObject *)LoadPackage(OpenDialog1>FileName));
Pour décharger un paquet dynamiquement, appelez UnloadPackage. Soyez
prudent en détruisant toute instance de classe définie dans le paquet et en
dérecensant les classes précédemment recensées.
Choix des paquets d’exécution à utiliser
C++Builder est livré avec plusieurs paquets d’exécution précompilés, dont rtl et
vcl, qui assurent la prise en charge de base du langage et des composants.
15-4
Guide du développeur
Paquets de conception
Le paquet vcl contient les composants les plus couramment utilisés, et le paquet
rtl contient toutes les fonctions système qui ne sont pas des composants ainsi
que les éléments de l’interface Windows. Il ne contient pas les composants de
base de données ni les autres composants spéciaux, qui se trouvent dans des
paquets distincts.
Pour créer une application de base de données client/serveur utilisant des
paquets, vous avez besoin de plusieurs paquets d’exécution, parmi lesquels vcl,
vcldb, rtl et dbrtl. Si vous voulez utiliser dans votre application des composants
arborescence (Outline), vous avez besoin en plus de vclx. Pour utiliser ces
paquets, choisissez Projet|Options, sélectionnez la page Paquets et entrez la liste
suivante dans la boîte de saisie Paquets d’exécution.
rtl;vcl;vcldb;vclx;
Il n’est en fait pas nécessaire d’inclure vcl et rtl, car ils sont référencés dans la
liste Requires de vcldb. (Voir “Liste Requires” à la page 15-10.) Votre application
se compile peut être construite de la même façon que vcl et rtl figurent ou non
dans la liste des paquets d’exécution.
Paquets personnalisés
Un paquet personnalisé est soit un bpl que vous programmez et construisez
vous-même, soit un paquet existant développé par un fournisseur tiers. Pour
utiliser dans une application un paquet d’exécution personnalisé, choisissez
Projet|Options et ajoutez le nom du paquet à la boîte de saisie Paquets
d’exécution de la page Paquets. Par exemple, si vous avez créé un paquet
effectuant des statistiques, nommé stats.bpl, que vous souhaitez utiliser dans une
application, la boîte de saisie Paquets d’exécution doit être de la forme :
rtl;vcl;vcldb;stats
Si vous créez vos propres paquets, vous pouvez les ajouter selon vos besoins à
la liste.
Paquets de conception
Les paquets de conception sont utilisés pour installer des composants dans la
palette des composants de l’EDI ou pour créer les éditeurs de propriétés
spéciaux de composants personnalisés.
C++Builder est livré avec de nombreux paquets de conception déjà installés dans
l’EDI. Ils dépendent de la version de C++Builder que vous utilisez et de la
personnalisation à laquelle vous avez pu la soumettre. Vous pouvez voir la liste
des paquets installés sur votre système en choisissant la commande Composant|
Installer des paquets.
Les paquets de conception fonctionnent en appelant des paquets d’exécution
référencés dans leur liste Requires. (Voir “Liste Requires” à la page 15-10.)
Par exemple, dclstd référence vcl. Dclstd contient lui-même des fonctionnalités
Utilisation des paquets et des composants
15-5
Paquets de conception
supplémentaires qui rendent la plupart des composants standard disponibles
dans la palette des composants.
Remarque
Par convention, les paquets de conception de l’EDI commencent par dcl et se
trouvent dans le répertoire bin. Pour les paquets de conception comme ..\bin\
Dclstd, il existe des équivalents pour le lieur, comme ..\lib\vcl.lib, ..\lib\vcl.bpi
et le paquet de conception lui-même, Windows\System\vcl60.bpl.
Outre les paquets pré-installés, vous pouvez installer dans l’EDI vos propres
paquets de composants ou ceux développés par des tiers. Le paquet de
conception dclusr est fourni en guise de conteneur par défaut des nouveaux
composants.
Installation de paquets de composants
Tous les composants installés dans l’EDI le sont sous la forme de paquets. Si
vous écrivez vos propres composants, créez et construisez un paquet les
contenant. (Voir “Création et modification de paquets” à la page 15-7.) Le code
source des composants doit respecter le modèle décrit dans la partie V, “Création
de composants personnalisés”. Si vous ajoutez plusieurs unités à un seul paquet,
contenant chacune des composants, vous devez utiliser une seule fonction
Register pour tous les composants dans le domaine d’appellation portant le nom
du paquet.
Pour installer ou désinstaller vos propres composants ou les composants fournis
par un tiers, procédez de la manière suivante :
1 Si vous installez un nouveau paquet, copiez ou déplacez les fichiers paquet
dans un répertoire local. Si le paquet est livré avec des fichiers .bpl, .bpi, .lib
et .obj, vous devez tous les copier. (Pour davantage d’informations sur ces
fichiers, consultez “Les paquets et les DLL standard” à la page 15-2.)
Le répertoire dans lequel sont stockés les fichiers bpi et d’en-tête (ainsi que les
fichiers .lib et .obj), s’ils font partie de la distribution, doit être dans le chemin
de la bibliothèque C++Builder.
2 Choisissez Composant|Installer des paquets dans le menu de l’EDI ou
choisissez Projet|Options et cliquez sur l’onglet Paquets.
3 Une liste des paquets disponibles apparaît sous “Paquets de conception”.
• Pour installer un paquet dans l’EDI, activez la case à cocher placée à côté
du nom de paquet.
• Pour désinstaller un paquet, désactivez la case à cocher.
• Pour voir une liste des composants inclus dans un paquet installé,
sélectionnez le paquet et cliquez sur Composants.
• Pour ajouter un paquet à la liste, cliquez sur le bouton Ajouter puis
recherchez dans la boîte de dialogue Ajout d’un paquet de conception le
répertoire dans lequel se trouve le fichier .bpl (voir l’étape 1). Sélectionnez
un fichier .bpl et cliquez sur Ouvrir.
15-6
Guide du développeur
Création et modification de paquets
• Pour retirer un paquet de la liste, sélectionnez le paquet, puis cliquez sur
Supprimer.
4 Cliquez sur OK.
Les composants du paquet sont installés sur la page de la palette des
composants spécifiée dans la procédure RegisterComponents des composants, avec
les noms qu’ils ont reçus dans cette même procédure.
Si vous utilisez les options par défaut, les nouveaux projets sont créés avec tous
les paquets disponibles installés. Si vous voulez que vos choix d’installation
deviennent les options par défaut pour les nouveaux projets, cochez Défaut, en
bas de la page Paquets de la boîte de dialogue Options du projet.
Pour supprimer des composants de la palette des composants sans désinstaller
de paquet, sélectionnez Composant|Configurer la palette ou bien Outils|Options
d’environnement et cliquez sur l’onglet Palette. La page Palette contient la liste
de tous les composants installés avec le nom de la page où chacun apparaît.
Sélectionnez le composant à supprimer de la palette et cliquez sur Cacher.
Création et modification de paquets
Pour créer un paquet, il faut :
• Un nom pour le paquet.
• Une liste des autres paquets requis (liés) par le nouveau paquet.
• Une liste des fichiers d’unité devant être contenus (ou liés) dans le paquet lors
de sa construction. Le paquet est essentiellement une enveloppe pour ces
unités de code source. C’est dans la liste Contains que vous placez les unités
de code source des composants personnalisés que vous voulez construire dans
un paquet.
Un paquet est défini par un fichier source C++ (.cpp) et un fichier d’options de
projet spécial d’extension .bpk. Ces fichiers sont générés par l’éditeur de paquets.
Création d’un paquet
Pour créer un paquet, effectuez les étapes suivantes. Pour davantage
d’informations sur les étapes suivantes, reportez-vous à “Présentation de la
structure d’un paquet” à la page 15-10.
Remarque
N’utilisez pas de ifdef dans un fichier de paquet (.bpk) comme dans le
développement multiplate-forme. Mais vous pouvez les utiliser dans le code
source.
1 Choisissez Fichier|Nouveau|Autre, sélectionnez l’icône Paquet et cliquez
sur OK.
2 Le paquet généré est affiché dans l’éditeur de paquet.
Utilisation des paquets et des composants
15-7
Création et modification de paquets
3 L’éditeur de paquet affiche pour le nouveau paquet un nœud Requires et un
nœud Contains.
4 Pour ajouter une unité à la liste Contains, cliquez sur le turbobouton Ajouter
au paquet. Dans la page Ajouter unité, tapez un nom de fichier .cpp dans la
boîte de saisie Nom de fichier unité, ou cliquez sur Parcourir... pour
rechercher le fichier, puis cliquez sur OK. L’unité sélectionnée apparaît sous le
nœud Contains de l’éditeur de paquet. Vous pouvez ajouter des unités
supplémentaires en répétant cette étape.
5 Pour ajouter un paquet à la liste Requires, cliquez sur le turbobouton Ajouter
au paquet. Dans la page Nécessite, tapez un nom de fichier .bpi dans la boîte
de saisie Nom de paquet, ou cliquez sur Parcourir... pour rechercher le fichier,
puis cliquez sur OK. Le paquet sélectionné apparaît sous le nœud Requires
dans l’éditeur de paquet. Vous pouvez ajouter des paquets supplémentaires en
répétant cette étape.
6 Cliquez sur le turbobouton Options, et sélectionnez le type de paquet à
générer.
• Pour créer un paquet de conception uniquement (un paquet ne pouvant pas
s’utiliser à l’exécution), sélectionnez le bouton radio Seulement en
conception. (Ou ajoutez le commutateur de liaison -Gpd à votre fichier
bpk : LFLAGS = ... -Gpd ....)
• Pour créer un paquet d’exécution uniquement (un paquet ne pouvant pas
être installé), sélectionnez le bouton radio d’exécution seule. (Ou ajoutez le
commutateur de liaison -Gpr à votre fichier bpk : LFLAGS = ... -Gpr ....)
• Pour créer un paquet utilisable à l’exécution et à la conception, sélectionnez
le bouton radio de conception et d’exécution.
7 Dans l’éditeur de paquet, cliquez sur le turbobouton Compiler le paquet pour
compiler votre paquet.
Remarque
Vous pouvez également cliquer sur le bouton Installer pour forcer une
génération.
Modification d’un paquet existant
Il y a plusieurs manières d’ouvrir un paquet existant afin de le modifier :
• Choisissez Fichier|Ouvrir (ou Fichier|Réouvrir) et sélectionnez un fichier cpp
ou bpk.
• Choisissez Composant|Installer des paquets, sélectionnez un paquet dans la
liste Paquets de conception et cliquez sur le bouton Modifier.
• Quand l’éditeur de paquet est ouvert, sélectionnez un des paquets du nœud
Requires, cliquez avec le bouton droit de la souris et choisissez Ouvrir.
Pour modifier la description d’un paquet ou définir les options d’utilisation,
cliquez sur le turbobouton Options dans l’éditeur de paquet et sélectionnez
l’onglet Description.
15-8
Guide du développeur
Création et modification de paquets
La boîte de dialogue Options du projet possède une case à cocher Défaut dans le
coin inférieur gauche. Si vous cliquez sur OK quand cette case est cochée, les
options choisies sont enregistrées comme paramètres par défaut pour les
nouveaux projets de paquet. Pour restaurer les valeurs par défaut originales,
supprimez ou renommez le fichier default.bpk.
Fichiers source de paquet et fichiers d’options de projet
Les fichiers source de paquet ont l’extension .cpp. Les fichiers d’options de projet
de paquet sont créés en utilisant le format XML et possèdent l’extension .bpk
(Borland package). Affichez le fichier des options de projet d’un paquet dans
l’éditeur de paquet en cliquant avec le bouton droit de la souris sur la clause
Contains ou Requires et en choisissant Modifier le fichier d’options.
Remarque
C++Builder gère le fichier .bpk. Il n’est normalement pas nécessaire de modifier
ce fichier manuellement. Vous pouvez effectuer les modifications en utilisant la
page Paquets de la boîte de dialogue des options du projet.
Le fichier des options du projet d’un paquet appelé MyPack peut contenir, entre
autres :
<MACROS>
<VERSION value="BCB.05.02"/>
<PROJECT value="MyPack.bpl"/>
<OBJFILES value="MyPack.obj Unit2.obj Unit3.obj"/>
<RESFILES value="MyPack.res"/>
<IDLFILES value=""/>
<IDLGENFILES value=""/>
<DEFFILE value=""/>
<RESDEPEN value="$(RESFILES)"/>
<LIBFILES value=""/>
<LIBRARIES value=""/>
<SPARELIBS value="Vcl60.lib"/>
<PACKAGES value="Vcl60.bpi vcldbx60.bpi"/>
.
.
.
Dans ce cas, MYPACK.cpp contiendrait le code suivant :
USERES("MyPack.res");
USEPACKAGE("vcl60.bpi");
USEPACKAGE("vcldbx60.bpi");
USEUNIT("Unit2.cpp");
USEUNIT("Unit3.cpp");
La liste Contains de MyPack contient trois unités : MyPack lui-même, Unit2 et
Unit3. La liste Requires de MyPack contient VCL et VCLDBX.
Empaquetage des composants
Si vous utilisez l’expert Nouveau composant pour créer des composants (en
choisissant Composant|Nouveau composant), C++Builder insère la macro
PACKAGE là où c’est nécessaire. Mais si vous utilisez des composants
Utilisation des paquets et des composants
15-9
Création et modification de paquets
personnalisés créés avec d’anciennes versions de C++Builder, vous devez ajouter
manuellement PACKAGE à deux endroits.
La déclaration de fichier d’en-tête d’un composant C++Builder doit inclure la
macro prédéfinie PACKAGE après le mot class :
class PACKAGE MonComposant : ...
En outre, dans le fichier cpp où le composant est défini, incluez la macro
PACKAGE dans la déclaration de la fonction Register :
void __fastcall PACKAGE Register()
La macro PACKAGE développe une instruction qui permet aux classes d’être
importées et exportées dans le fichier bpl résultant.
Présentation de la structure d’un paquet
Les paquets incluent les parties suivantes :
• Nom de paquet
• Liste Requires
• Liste Contains
Nom de paquets
Les noms de paquets doivent être uniques dans un projet. Si vous nommez un
paquet Stats, l’éditeur de paquets générera un fichier source et un fichier des
options du projet nommés respectivement Stats.cpp et Stats.bpk ; le compilateur
et le lieur généreront un exécutable, un fichier image binaire et (en option) une
bibliothèque statique, appelés respectivement Stats.bpl, Stats.bpi et Stats.lib. Pour
utiliser le paquet dans une application, ajoutez Stats à la boîte de saisie Paquets
d’exécution (après avoir choisi Projet|Options puis avoir cliqué sur l’onglet
Paquets).
Vous pouvez également ajouter un préfixe, un suffixe et un numéro de version à
votre nom de paquet. Alors que l’éditeur de paquet est ouvert, cliquez sur le
bouton Options. Sur la Page Description de la boîte de dialogue Options du
projet, entrez le texte ou une valeur pour Suffixe LIB, Préfixe LIB ou Version
LIB. Par exemple, pour ajouter un numéro de version à votre projet de paquet,
entrez 6 après Version LIB pour que Paquet1 génère Paquet1.bpl.6.
Liste Requires
La liste Requires spécifie les autres paquets externes qui sont utilisés par le
paquet en cours. Un paquet externe inclus dans la liste Requires est
automatiquement lié lors de la compilation dans toute application utilisant le
paquet en cours ou l’une des unités contenues dans le paquet externe.
Si les fichiers d’unité contenus dans votre paquet font référence à d’autres unités
empaquetées, les autres paquets doivent apparaître dans la liste Requires de
votre paquet, sans quoi vous devrez les ajouter. Si les autres paquets sont
absents de la liste Requires, le compilateur les importera dans votre paquet
comme ‘unités contenues implicitement’.
15-10
Guide du développeur
Création et modification de paquets
Remarque
La plupart des paquets que vous créez nécessitent rtl. Si vous utilisez des
composants VCL, vous devrez inclure également le paquet vcl. Si vous utilisez
des composants CLX en programmation multi-plate-forme, vous devrez inclure
VisualCLX.
Eviter les références de paquet circulaires
Les paquets ne doivent pas contenir de référence circulaire dans leur liste
Requires. Par conséquent :
• Un paquet ne doit pas se référencer lui-même dans sa liste Requires.
• Une chaîne de références doit se terminer sans référencer un paquet de la
chaîne. Si le paquet A requiert le paquet B, alors le paquet B ne doit pas
requérir le paquet A ; si le paquet A requiert le paquet B qui requiert le
paquet C, alors le paquet C ne doit pas requérir le paquet A.
Gestions des références de paquets en double
Les références en double dans la liste Requires d’un paquet, ou dans la boîte de
saisie Paquet d’exécution, sont ignorées par le lieur. Mais, pour la lisibilité du
programme, il vaut mieux les supprimer.
Liste Contains
La liste Containsidentifie les fichiers d’unité qui doivent être liés dans le paquet.
Si vous écrivez votre propre paquet, placez votre code source dans des fichiers
cpp et incluez-les dans la liste liste Contains.
Eviter l’utilisation de code source redondant
Un paquet ne peut pas apparaître dans la liste Contains d’un autre paquet.
Toutes les unités incluses directement dans la liste Containsd’un paquet, ou
indirectement dans l’une de ces unités, sont liées dans le paquet au moment de
la liaison.
Une unité ne peut pas être contenue (directement ou indirectement) dans
plusieurs des paquets utilisés par une même application, y compris l’EDI
C++Builder. Cela signifie que si vous créez un paquet contenant l’une des unités
de vcl, vous ne pourrez pas installer ce paquet dans l’EDI. Pour utiliser une
unité déjà empaquetée dans un autre paquet, placez le premier paquet dans la
liste Requires.du deuxième paquet.
Construction des paquets
Vous pouvez construire un paquet dans l’EDI ou depuis la ligne de commande.
Pour reconstruire un paquet directement dans l’EDI :
1 Choisissez Fichier|Ouvrir, sélectionnez un fichier source de paquet ou un
fichier d’options de projet et cliquez sur Ouvrir.
2 Quand l’éditeur s’ouvre, choisissez Projet|Make ou Projet|Construire.
Utilisation des paquets et des composants
15-11
Création et modification de paquets
Remarque
Vous pouvez également choisir Fichier|Nouveau|Autre et double-cliquer sur
l’éditeur de paquet. Cliquez sur le bouton Installer pour construire le projet de
paquet. Cliquez avec le bouton droit de la souris sur les nœuds du projet de
paquet pour les options d’installation, de génération ou de construction.
Si vous ajoutez un fichier .cpp non généré, ajoutez l’une des directives du
compilateur dans votre code source du paquet. Pour plus d’informations, voir
“Directives de compilation propres aux paquets” ci-dessous.
En effectuant une édition de liens depuis la ligne de commande, vous pouvez
utiliser plusieurs commutateurs du lieur propres aux paquets. Pour plus
d’informations, reportez-vous à “Utilisation du compilateur et du lieur en ligne
de commande” à la page 15-13.
Directives de compilation propres aux paquets
Le tableau suivant liste les directives de compilation propres aux paquets qu’il
est possible d’insérer dans le code source.
Tableau 15.2 Directives de compilation propres aux paquets
Directive
Utilisation
#pragma package(smart_init)
Vérifie que les unités empaquetées sont initialisées dans
l’ordre déterminé par leurs dépendances. Incluse par
défaut dans le fichier source d’un paquet.
#pragma package(smart_init, weak)
Réalise un empaquetage “faible” des unités. Voir
“Paquets faibles” ci-dessous.(Placez cette directive dans
le fichier source d’une unité.)
Reportez-vous à “Création de paquets et de DLL” à la page 7-10, pour d’autres
directives pouvant être utilisées dans toutes les bibliothèques.
Paquets faibles
La directive #pragma package(smart_init, weak) affecte la manière dont un
fichier .obj est stocké dans les fichiers .bpi et .bpl d’un paquet. (Pour davantage
d’informations sur les fichiers générés par le compilateur et le lieur, voir
“Fichiers de paquets créés lors d’une construction” à la page 15-14.) Si #pragma
package(smart_init, weak) apparaît dans un fichier d’unité, le lieur ne place pas
l’unité dans les fichiers bpl si c’est possible, et il crée une copie locale non
empaquetée de l’unité si elle est nécessaire à une autre application ou un autre
paquet. Une unité compilée avec cette directive est dite faiblement empaquetée.
Supposons, par exemple, que vous avez créé un paquet appelé PACK ne
contenant qu’une seule unité UNIT1, et que UNIT1 n’utilise aucune autre unité
mais effectue des appels à RARE.dll. Si vous placez #pragma package(smart_init,
weak) dans UNIT1.cpp lorsque vous construisez le paquet, UNIT1 ne sera pas
incluse dans PACK.bpl ; vous n’aurez pas à distribuer de copies de RARE.dll
avec PACK. Cependant, UNIT1 sera toujours incluse dans PACK.bpi. Si un autre
paquet ou une autre application utilisant PACK fait référence à UNIT1, celle-ci
sera copiée à partir de PACK.bpi et liée directement dans le projet.
15-12
Guide du développeur
Création et modification de paquets
Supposons maintenant que vous ajoutiez à PACK une deuxième unité, UNIT2, et
que UNIT2 utilise UNIT1. Cette fois, même si vous compilez PACK avec
#pragma package(smart_init, weak) dans UNIT1.cpp, le lieur inclura UNIT1
dans PACK.bpl. Mais d’autres paquets ou applications faisant référence à UNIT1
utiliseront la copie (non empaquetée) extraite de PACK.bpi.
Remarque
Les fichiers d’unités contenant la directive #pragma package(smart_init, weak)
ne doivent pas contenir de variables globales.
#pragma package(smart_init, weak) est une caractéristique avancée prévue pour
les programmeurs qui distribuent leur fichiers bpl à d’autres programmeurs
C++Builder. Elle vous permet d’éviter la distribution de DLL rarement utilisées
et supprime les conflits entre des paquets qui peuvent dépendre de la même
bibliothèque externe.
Ainsi, l’unité PenWin de C++Builder référence PenWin.dll. La plupart des projets
n’utilisent pas PenWin et la plupart des ordinateurs n’ont pas de fichier
PenWin.dll installé. C’est pour cela que l’unité PenWin est faiblement
empaquetée dans vcl. Quand vous liez un projet utilisant PenWin et le paquet
vcl, PenWin est copié depuis vcl60.bpi et lié directement à votre projet ;
l’exécutable résultant est lié statiquement à PenWin.dll.
Si PenWin n’avait pas été faiblement empaquetée, deux problèmes se seraient
produits. Tout d’abord, il aurait fallu que vcl soit lié de manière statique à
PenWin.dll et vous n’auriez donc pas pu le charger sur un système ne disposant
pas de PenWin.dll. De plus, si vous aviez tenté de créer un paquet contenant
PenWin, une erreur de construction aurait eu lieu, puisque l’unité PenWin aurait
été contenue à la fois dans vcl et dans votre paquet. Ainsi, sans “empaquetage
faible”, l’unité PenWin n’aurait pas pu être incluse dans les distributions
standard de vcl.
Utilisation du compilateur et du lieur en ligne de commande
Quand vous liez depuis la ligne de commande, utilisez le commutateur -Tpp du
lieur pour que le projet soit construit sous la forme d’un paquet. Le tableau
suivant énumère d’autres commutateurs propres aux paquets.
Tableau 15.3 Commutateurs du lieur en ligne de commande propres aux paquets
Commutateur
Utilisation
-Tpp
Construit le projet sous la forme d’un paquet. Placé par défaut dans le
fichier d’options de projet d’un paquet.
-Gi
Enregistre le fichier bpi généré. Placé par défaut dans le fichier d’options
de projet d’un paquet.
-Gpr
Construit un paquet d’exécution.
-Gpd
Construit un paquet de conception.
-Gl
Crée un fichier .lib.
-D “description”
Enregistre la description spécifiée avec le paquet.
Les commutateurs -Gpr et -Gpd correspondent aux cases à cocher Paquet
d’exécution et Paquet de conception de la page Description (proposée
Utilisation des paquets et des composants
15-13
Déploiement de paquets
uniquement pour les projets de paquet) dans la boîte de dialogue Options de
projet. Si -Gpr et -Gpd ne sont pas utilisés, le paquet résultant fonctionne à
l’exécution et à la conception. Le commutateur -D correspond à la boîte de saisie
Description dans la même page. Le commutateur -Gl correspond à la case à
cocher Créer le fichier .lib dans la page Lieur de la boîte de dialogue Options de
projet.
Remarque
Vous pouvez créer un makefile à utiliser sur la ligne de commande en utilisant
Projet|Exporter le fichier Makefile.
Fichiers de paquets créés lors d’une construction
Pour créer un paquet, compilez le fichier source (.cpp) en utilisant un fichier
d’options de projet d’extension .bpk. Le nom de base du fichier source doit
correspondre à celui des fichiers générés par le compilateur ; donc si le fichier
source s’appelle TRAYPAK.cpp, le fichier d’options de projet (TRAYPAK.bpk)
doit contenir :
<PROJECT value="Traypak.bpl"/>
Dans ce cas, la compilation et la liaison du projet génèrent un paquet appelé
TRAYPAK.bpl.
Lorsque vous compilez et liez un paquet, vous créez des fichiers bpi, bpl, obj et
lib. Les fichiers bpi, bpl et lib sont générés par défaut dans les répertoires
spécifiés dans la page Bibliothèque de la boîte de dialogue Outils|Options
d’environnement. Vous pouvez redéfinir les options par défaut en cliquant sur le
turbobouton Options de l’éditeur de paquets afin d’afficher la boîte de dialogue
des options du projet ; effectuez alors les modifications nécessaires dans la page
Répertoires/Conditions.
Déploiement de paquets
Vous déploierez les paquets de la même façon que les applications. Les fichiers
que vous distribuez avec un paquet déployé peuvent varier. Le fichier bpl et
tous les paquets et fichiers dll requis par le fichier bpl doivent être distribués.
Le tableau suivant énumère les fichiers qui peuvent s’avérer nécessaire, en
fonction de l’utilisation du paquet.
Tableau 15.4 Fichiers déployés avec un paquet
Fichier
Description
NomComposant.h
Permet aux utilisateurs finaux d’accéder aux interfaces de classe.
NomComposant.cpp
Permet aux utilisateurs finaux d’accéder au composant source.
bpi, obj et lib
Permet aux utilisateurs finaux de lier des applications.
Pour plus d’informations sur le déploiement en général, voir chapitre 17,
“Déploiement des applications”.
15-14
Guide du développeur
Déploiement de paquets
Déploiement d’applications utilisant des paquets
Pour distribuer une application utilisant des paquets d’exécution, vérifiez que
l’utilisateur dispose du fichier .exe de l’application, ainsi que de toutes les
bibliothèques (.bpl ou .dll) appelées par l’application. Si les fichiers bibliothèque
sont dans un répertoire différent de celui du fichier .exe, ils doivent être
accessibles via les chemins d’accès de l’utilisateur. Vous pouvez suivre la
convention consistant à placer les fichiers des bibliothèques dans le répertoire
Windows\System. Si vous utilisez InstallShield Express, le script d’installation
peut vérifier la présence des paquets nécessaires sur le système de l’utilisateur
avant de les réinstaller aveuglément.
Distribution de paquets à d’autres développeurs
Si vous distribuez des paquets de conception ou d’exécution à d’autres
développeurs C++Builder, n’oubliez pas de fournir les fichiers .bpi et .bpl ainsi
que les fichiers d’en-tête nécessaires. Pour lier des composants de manière
statique dans leurs applications (c’est-à-dire pour générer des applications
n’utilisant pas de paquets d’exécution), les développeurs ont également besoin
des fichiers .lib (ou .obj) pour tous les paquets que vous distribuez.
Fichiers de collection de paquets
Les collections de paquets (fichiers .dpc) offrent un moyen pratique pour
distribuer des paquets à d’autres développeurs. Chaque collection de paquets
contient un ou plusieurs paquets, comprenant les bpl et les fichiers
supplémentaires que vous souhaitez distribuer avec. Lorsqu’une collection de
paquets est sélectionnée pour une installation de l’EDI, les fichiers qui la
constituent sont automatiquement extraits du fichier conteneur .pce ; la boîte de
dialogue Installation offre la possibilité d’installer tous les paquets de la
collection ou ceux sélectionnés.
Pour créer une collection de paquets :
1 Choisissez Outils|Editeur de collection de paquets pour ouvrir l’éditeur de
collection de paquets.
2 Cliquez sur le turbobouton Ajouter un paquet, sélectionnez un bpl dans la
boîte de dialogue Sélectionner le paquet et choisissez Ouvrir. Pour ajouter
d’autres bpl à la collection, choisissez à nouveau le turbobouton Ajouter un
paquet. Sur le côté gauche de l’éditeur de paquets, un diagramme
d’arborescence affiche les bpl que vous avez ajoutés. Pour supprimer un
paquet, sélectionnez-le et choisissez le turbobouton Retirer le paquet.
3 Sélectionnez le nœud Collection au sommet du diagramme d’arborescence. Sur
la partie droite de l’éditeur de collection de paquets, deux champs
apparaissent :
• Dans la boîte de saisie Nom auteur/vendeur, vous pouvez saisir des
informations facultatives à propos de votre collection de paquets, qui
Utilisation des paquets et des composants
15-15
Déploiement de paquets
apparaîtront dans la boîte de dialogue Installation lorsque les utilisateurs
installeront les paquets.
• Dans la liste Nom de répertoire, énumérez les répertoires par défaut dans
lesquels vous voulez installer les fichiers de la collection de paquets.
Utilisez les boutons Ajouter, Edition et Supprimer pour modifier cette liste.
Par exemple, supposons que vous vouliez copier tous les fichiers de code
source dans un même répertoire. Dans ce cas, vous pouvez saisir Source
comme nom de répertoire avec C:\MyPackage\Source comme chemin suggéré.
La boîte de dialogue Installation affichera C:\MyPackage\Source comme chemin
suggéré pour le répertoire.
4 En plus des bpl, la collection de paquets peut contenir des fichiers .bpi, .obj et
des unités .cpp, de la documentation et d’autres fichiers que vous souhaitez
inclure pour la distribution. Les fichiers annexes sont placés dans des groupes
de fichiers associés aux paquets spécifiques (bpl). Les fichiers d’un groupe ne
sont installés que lorsque le bpl associé est installé. Pour mettre les fichiers
annexes dans la collection de paquets, sélectionnez un bpl dans le diagramme
d’arborescence et choisissez le turbobouton Ajouter un groupe de fichiers ;
saisissez un nom pour le groupe de fichiers. Ajoutez d’autres fichiers si vous
le désirez en procédant de la même manière. Lorsque vous sélectionnez un
groupe de fichiers, de nouveaux champs apparaissent sur la droite de l’éditeur
de collection de paquets.
• Dans la boîte liste Répertoire d’installation, sélectionnez le répertoire dans
lequel vous voulez installer les fichiers de ce groupe. La liste déroulante
comprend les répertoires saisis dans Liste de répertoires à l’étape 3 cidessus.
• Activez la case à cocher Groupe optionnel si vous voulez que l’installation
des fichiers de ce groupe soit facultative.
• Sous Fichiers inclus, énumérez les fichiers que vous voulez inclure dans ce
groupe. Utilisez les boutons Ajouter, Supprimer et Auto pour modifier la
liste. Le bouton Auto permet de sélectionner tous les fichiers avec les
extensions spécifiées dont la liste se trouve dans la liste Contains du
paquet ; l’éditeur de collection de paquets utilise le chemin de la
bibliothèque de C++Builder pour rechercher ces fichiers.
5 Vous pouvez sélectionner des répertoires d’installation pour les paquets
énumérés dans la liste Requires de n’importe quel paquet de votre collection.
Lorsque vous sélectionnez un bpl dans la liste arborescente, quatre nouveaux
champs apparaissent sur la partie droite de l’éditeur de collection de paquets :
• Dans la boîte liste Fichiers exécutables requis, sélectionnez le répertoire
dans lequel vous voulez installer les fichiers .bpl pour les paquets énumérés
dans la liste Requires. La liste déroulante comprend les répertoires spécifiés
sous Nom répertoire à l’étape 3 ci-dessus. L’éditeur de collection de paquets
recherche ces fichiers en utilisant le chemin global de la bibliothèque
C++Builder et donne leur liste sous Fichiers exécutables requis.
• Dans la boîte liste Répertoire des bibliothèques requises, sélectionnez le
répertoire d’installation des fichiers .obj et .bpi pour les paquets de la liste
15-16
Guide du développeur
Déploiement de paquets
Requires. La liste déroulante comprend les répertoires spécifiés sous Nom
répertoire à l’étape 3 ci-dessus. L’éditeur de collection de paquets recherche
ces fichiers en utilisant le chemin global de la bibliothèque C++Builder et
donne la liste sous Fichiers bibliothèque requis.
6 Pour enregistrer votre fichier source de collection de paquets, choisissez
Fichier|Enregistrer. Les fichiers source de collection de paquets doivent être
enregistrés avec l’extension .pce.
7 Pour construire votre collection de paquets, choisissez le turbobouton
Compiler. L’éditeur de collection de paquets génère un fichier .dpc avec le
nom de votre fichier source (.pce). Si vous n’avez pas encore enregistré le
fichier source, l’éditeur vous demande un nom de fichier avant la
construction.
Pour éditer ou reconstruire un fichier .pce existant, sélectionnez Fichier|Ouvrir
dans l’éditeur de collection de paquets et localisez le fichier sur lequel vous
voulez travailler.
Utilisation des paquets et des composants
15-17
15-18
Guide du développeur
Chapitre
16
Création d’applications
internationales
Chapitre 16
Ce chapitre présente les règles d’écriture d’applications qui seront distribuées sur
les marchés internationaux. En planifiant le processus, il est possible de réduire
le temps et le code nécessaires pour que vos applications puissent fonctionner
parfaitement à l’étranger comme sur le marché domestique.
Internationalisation et localisation
Pour créer une application distribuable sur les marchés étrangers, vous devez
accomplir deux étapes :
• Internationalisation
• Localisation
Si votre édition de C++Builder comprend les Outils de traduction, vous pouvez
les utiliser pour gérer la localisation. Pour plus d’informations, voir l’aide en
ligne des outils de traduction (ETM.hlp).
Internationalisation
L’internationalisation est le processus permettant à votre application de
fonctionner selon divers paramètres régionaux. Les paramètres régionaux sont
l’environnement de l’utilisateur qui inclut les conventions culturelles du pays
cible aussi bien que sa langue. Windows gère un grand nombre de paramètres
régionaux, chacun d’eux décrits par l’association d’une langue et d’un pays.
Création d’applications internationales
16-1
Internationalisation des applications
Localisation
La localisation est le processus de traduction d’une application pour qu’elle
fonctionne pour des paramètres régionaux spécifiques. Outre la traduction de
l’interface utilisateur, la localisation peut également consister à personnaliser les
fonctionnalités. Par exemple, une application financière peut être modifiée afin
de respecter les règles fiscales dans différents pays.
Internationalisation des applications
Vous devez effectuer les étapes suivantes pour créer des applications
internationalisées :
• Le code de votre application doit être capable de gérer des jeux de caractères
internationaux.
• Vous devez concevoir l’interface utilisateur de l’application afin de l’adapter
aux modifications résultant de la localisation.
• Vous devez isoler toutes les ressources qui ont besoin d’être localisées.
Codage de l’application
Vous vous assurerez que le code de l’application peut gérer les chaînes qu’elle
rencontrera dans les divers environnements régionaux cible.
Jeux de caractères
Les versions occidentales de Windows (dont les versions anglaise, allemande et
française) utilisent le jeu de caractères ANSI Latin-1 (1252). Mais d’autres
éditions de Windows utilisent des jeux de caractères différents. Ainsi, la version
japonaise de Windows utilise le jeu de caractères Shift-JIS (page de code 932) qui
représente les caractères japonais avec des codes sur plusieurs octets.
Il y a, en général, trois types de jeux de caractères :
• Uni-octet (codé sur un seul octet)
• Multi-octet (codé sur plusieurs octets)
• Caractères étendus
Windows et Linux supportent tous les deux les jeux de caractères sur un seul
octet et sur plusieurs octets, comme le jeu Unicode. Dans un jeu sur un seul
octet, chaque octet d’une chaîne représente un caractère. Le jeu de caractères
ANSI utilisé par de nombreux systèmes d’exploitation occidentaux utilise un
seul octet.
Dans un jeu sur plusieurs octets, certains caractères sont représentés par un octet
et d’autres par plusieurs octets. Le premier octet d’un caractère sur plusieurs
octets est appelé l’octet de poids fort. En général, les 128 premiers caractères
d’un jeu multi-octet correspondent aux caractères ASII sur 7 bits, et tout octet
16-2
Guide du développeur
Internationalisation des applications
dont la valeur ordinale est supérieure à 127 est l’octet de poids fort d’un
caractère multi-octet. Seuls les caractères sur un octet peuvent contenir la valeur
null (#0). Les jeux de caractères multi-octets — en particulier les jeux sur deux
octets (DBCS) — sont employés pour les langues asatiatiques, tandis que le jeu
UTF-8 utilisé par Linux est un encodage sur plusieurs octets du jeu Unicode.
Jeux de caractères OEM et ANSI
Il est parfois nécessaire de faire des conversions entre le jeu de caractères
Windows (ANSI) et le jeu de caractères spécifié par la page de code de la
machine de l’utilisateur (appelé jeu de caractères OEM).
Jeux de caractères sur plusieurs octets
Les idéogrammes utilisés en Asie ne peuvent se satisfaire de la correspondance
1 pour 1 existant entre les caractères d’une langue et le type char qui occupe un
seul octet (8 bits). Ces langues ont trop de caractères pour qu’ils soient
représentés sur un seul octet comme le fait le type char. En revanche, une chaîne
multi-octet peut contenir un ou plusieurs octets par caractère. AnsiStrings peut
contenir un mélange de caractères sur un et plusieurs octets.
L’octet de poids fort de tous les codes utilisant plusieurs octets appartient à un
intervalle réservé et spécifique du jeu de caractères concerné. Le second octet et
les octets suivants peuvent être le code d’un autre caractère s’il s’agit d’un
caractère codé sur un seul octet, ou il peut appartenir à l’intervalle réservé
indiqué par le premier octet s’il s’agit d’un caractère codé sur plusieurs octets.
Aussi, la seule façon de savoir si un octet particulier d’une chaîne représente
seul un caractère ou fait partie d’un groupe d’octets est de lire la chaîne à partir
de l’origine, en la décomposant en caractères de deux ou plusieurs octets chaque
fois qu’est rencontré un octet de poids fort appartenant à l’intervalle réservé.
Lors de l’écriture de code destiné aux pays asiatiques, vous devez traiter toutes les
manipulations de chaînes avec des fonctions capables de décomposer les chaînes
en caractères de plusieurs octets.Voir “API internationale” et “Utilitaires
MBCS”dans l’aide en ligne pour une liste des fonctions RTL gérant les caractères
sur plusieurs octets.
N’oubliez pas que la longueur de la chaîne en octets ne correspond pas
nécessairement à la longueur de la chaîne en caractères. Faites attention à ne pas
tronquer les chaînes en coupant en deux un caractère codé sur plusieurs octets.
Vous ne pouvez pas passer des caractères comme paramètres aux fonctions ou
aux procédures puisque la taille d’un caractère n’est pas connue directement.
Vous devez passer un pointeur sur le caractère ou sur la chaîne.
Caractères larges
Une autre approche de l’utilisation des jeux de caractères pour idéogrammes est
de convertir tous les caractères dans un système de caractères larges, comme
Unicode. Les caractères et les chaînes Unicode sont également appelés caractères
larges et chaînes de caractères larges. Dans le jeu Unicode, chaque caractère est
représenté par deux octets. Ainis, une chaîne Unicode est une suite non d’octets
séparés mais de mots de deux octets.
Création d’applications internationales
16-3
Internationalisation des applications
Les 256 premiers caractères Unicode correspondent au jeu de caractères ANSI. Le
système d’exploitation Windows supporte Unicode (UCS-2). Le système
d’exploitation Linux supporte UCS-4, super-ensemble de UCS-2. C++Builder
supporte UCS-2 sur les deux plates-formes. Les caractères larges utilisant deux
octets et non un, le jeu de caractères peut représenter beaucoup plus de
caractères différents.
En outre, les caractères larges présentent un avantage sur les caractères MBCS :
ils vous permettent de conserver vos habitudes car il existe une relation directe
entre le nombre d’octets d’une chaîne et son nombre de caractères. Et, vous ne
risquez plus de couper un caractère en deux, ni de confondre la seconde moitié
d’un caractère avec la première d’un autre.
L’inconvénient majeur des caractères larges est que Windows n’en reconnaît
qu’un petit nombre dans les appels aux fonctions API. C’est pourquoi, les
composants VCL représentent toutes les valeurs chaînes par des chaînes à
caractères d’un seul octet ou par des chaînes MBCS. Vous devrez passer du
système caractères larges au système MBCS à chaque fois que définir la propriété
d’une chaîne ou en lire la valeur exigerait un supplément de code et ralentirait
votre application. Cependant, vous pouvez souhaiter traduire en caractères larges
certains algorithmes de traitement des chaînes pour profiter de la correspondance
1 pour 1 entre caractères et WideChars.
Voir “API Internationale” dans l’aide en ligne pour une liste des fonctions RTL
gérant les caractères Unicode.
Inclure des fonctionnalités bi-directionnelles dans les applications
Certaines langues ne se lisent pas de gauche à droite comme la plupart des
langues occidentales, mais elles lisent les mots de droite à gauche et comptent de
gauche à droite. Ces langues sont dites bi-directionnelles (BiDi) du fait de cette
séparation. Les langues bi-directionnelles les plus courantes sont l’Arabe et
l’Hébreu, sans parler d’autres langues de l’Est.
TApplication dispose de deux propriétés, BiDiKeyboard et NonBiDiKeyboard, vous
permettant de spécifier la disposition clavier. En outre, la VCL gère la
localisation bi-directionnelle via les propriétés BiDiMode et ParentBiDiMode.
Le tableau suivant énumère les objets de la VCL possédant ces propriétés.
Tableau 16.1 Objets de la VCL supportant les BiDi
Page de la palette
de composants
Standard
Objet de la VCL
TButton
TCheckBox
TComboBox
TEdit
TGroupBox
TLabel
TListBox
TMainMenu
16-4
Guide du développeur
Internationalisation des applications
Tableau 16.1 Objets de la VCL supportant les BiDi (suite)
Page de la palette
de composants
Objet de la VCL
TMemo
TPanel
TPopupMenu
TRadioButton
TRadioGroup
TScrollBar
Supplément
TActionMainMenuBar
TActionToolBar
TBitBtn
TCheckListBox
TColorBox
TDrawGrid
TLabeledEdit
TMaskEdit
TScrollBox
TSpeedButton
TStaticLabel
TStaticText
TStringGrid
TValueListEditor
Win32
TComboBoxEx
TDateTimePicker
THeaderControl
THotKey
TListView
TMonthCalendar
TPageControl
TRichEdit
TStatusBar
TTabControl
TTreeView
Contrôles de données
TDBCheckBox
TDBComboBox
TDBEdit
TDBGrid
TDBListBox
TDBLookupComboBox
TDBLookupListBox
TDBMemo
Création d’applications internationales
16-5
Internationalisation des applications
Tableau 16.1 Objets de la VCL supportant les BiDi (suite)
Page de la palette
de composants
Objet de la VCL
TDBRadioGroup
TDBRichEdit
TDBText
QReport
TQRDBText
TQRExpr
TQRLabel
TQRMemo
TQRPreview
TQRSysData
Autres classes
TApplication (sans ParentBiDiMode)
TBoundLabel
TControl (sans ParentBiDiMode)
TCustomHeaderControl (sans ParentBiDiMode)
TForm
TFrame
THeaderSection
THintWindow (sans ParentBiDiMode)
TMenu
TStatusPanel
Remarque
THintWindow capte la valeur de BiDiMode du contrôle qui a activé le conseil.
Propriétés bi-directionnelles
Les objets dont la liste est donnée dans le tableau 16.1, “Objets de la VCL
supportant les BiDi”, ont les propriétés : BiDiMode et ParentBiDiMode. Ces
propriétés, ainsi que BiDiKeyboard et NonBiDiKeyboard de TApplication gèrent la
localisation bi-directionnelle.
Remarque
Les propriétés bi-directionnelles ne sont pas disponibles dans CLX pour la
programmation multiplate-forme.
Propriété BiDiMode
La propriété BiDiMode est un nouveau type d’énuméré, TBiDiMode, qui possède
quatre états : bdLeftToRight, bdRightToLeft, bdRightToLeftNoAlign et
bdRightToLeftReadingOnly.
bdLeftToRight
bdLeftToRight dessine le texte en utilisant le sens de lecture de gauche à droite,
l’alignement et la barre de défilement étant inchangés. Par exemple, lors de la
saisie de texte de droite à gauche, comme pour l’Arabe ou l’Hébreu, le curseur
passe en mode poussoir et le texte est saisi de droite à gauche. Pour du texte
16-6
Guide du développeur
Internationalisation des applications
latin, comme l’Anglais ou le Français, il est saisi de gauche à droite.
bdLeftToRight est la valeur par défaut.
Figure 16.1 Contrôles initialisés à bdLeftToRight
bdRightToLeft
bdRightToLeft dessine le texte en utilisant le sens de lecture de droite à gauche,
l’alignement étant modifié et la barre de défilement déplacée. Le texte est saisi
normalement pour les langues allant de droite à gauche comme l’Arabe ou
l’Hébreu. Lorsque le clavier est modifié pour une langue latine, le curseur passe
en mode poussoir et le texte est saisi de gauche à droite.
Figure 16.2 Contrôles initialisés à bdRightToLeft
bdRightToLeftNoAlign
bdRightToLeftNoAlign dessine le texte en utilisant le sens de lecture de droite à
gauche, l’alignement étant inchangé et la barre de défilement déplacée.
Figure 16.3 Contrôles initialisés à bdRightToLeftNoAlign
bdRightToLeftReadingOnly
bdRightToLeftReadingOnly dessine le texte en utilisant le sens de lecture de droite
à gauche, l’alignement et la barre de défilement étant inchangés.
Figure 16.4 Contrôles initialisés à bdRightToLeftReadingOnly
Propriété ParentBiDiMode
ParentBiDiMode est une propriété booléenne. Lorsqu’elle est à true (la valeur par
défaut), le contrôle regarde la propriété de son parent pour connaître la valeur à
utiliser pour BiDiMode. Si le contrôle est un objet TForm, la fiche utilise la valeur
BiDiMode de Application. Si toutes les propriétés ParentBiDiMode sont à true,
lorsque la propriété BiDiMode de Application est modifiée, toutes les fiches et tous
les contrôles du projet sont initialisés avec la nouvelle valeur.
Méthode FlipChildren
La méthode FlipChildren vous permet de faire basculer la position des enfants
d’un contrôle conteneur. Les contrôles conteneur sont des contrôles qui
Création d’applications internationales
16-7
Internationalisation des applications
contiennent d’autres contrôles, comme TForm, TPanel et TGroupBox. FlipChildren
possède un seul paramètre booléen, AllLevels. Lorsqu’il est à false, seuls les
enfants directs du contrôle conteneur sont basculés de position. Lorsqu’il est à
true, tous les enfants du contrôle conteneur sont basculés de position.
C++Builder fait basculer la position des contrôles en modifiant la propriété Left
et l’alignement du contrôle. Si le côté gauche d’un contrôle est à cinq pixels de la
limite gauche de son parent, le basculement provoque l’affichage du côté droit
du contrôle de saisie à cinq pixels de la limite droite de son parent. Si le contrôle
de saisie est aligné à gauche, un appel à FlipChildren provoquera un alignement à
droite.
Pour basculer la position d’un contrôle lors de la conception, il faut sélectionner
Edition|Transposer les enfants et sélectionner Tous ou Sélectionnés suivant que
vous voulez basculer la position de tous les contrôles ou seulement les enfants
du contrôle sélectionné. Il est aussi possible de basculer la position d’un contrôle
en sélectionnant le contrôle sur la fiche, en cliquant sur le bouton droit de la
souris pour sélectionner le choix Transposer les enfants dans le menu contextuel.
Remarque
La sélection d’un contrôle de saisie suivi de la commande Transposer les
enfants|Sélectionnés ne fait rien. Cela est du au fait que les contrôles de saisie
ne sont pas des conteneurs.
Autres méthodes
Il existe d’autres méthodes utiles afin de développer des applications pour des
utilisateurs bi-directionnels.
Tableau 16.2 Méthodes de la VCL supportant les BiDi
16-8
Méthode
Description
OkToChangeFieldAlignment
Utilisée avec les contrôles base de données. Vérifie si
l’alignement d’un contrôle peut être modifié.
DBUseRightToLeftAlignment
Utilisée pour vérifier l’alignement des contrôles base
de données.
ChangeBiDiModeAlignment
Modifie le paramètre d’alignement qui lui est
transmis. Aucune vérification n’est faite pour
l’initialisation de BiDiMode, car il y a juste conversion
de l’alignement à gauche vers l’alignement à droite et
vice-versa, en laissant centré les contrôles seuls.
IsRightToLeft
Renvoie true si une des options allant de droite à
gauche est sélectionnée. Renvoie false si le contrôle
est dans un mode allant de gauche à droite.
UseRightToLeftReading
Renvoie true si le contrôle utilise le sens de lecture
allant de droite à gauche.
UseRightToLeftAlignment
Renvoie true si le contrôle utilise le sens
d’alignement allant de droite à gauche. Il peut être
surchagé pour être personnalisé.
UseRightToLeftScrollBar
Renvoie true si le contrôle utilise une barre de
défilement à gauche.
DrawTextBiDiModeFlags
Renvoie les bons paramètres pour le mode BiDi du
contrôle.
Guide du développeur
Internationalisation des applications
Tableau 16.2 Méthodes de la VCL supportant les BiDi (suite)
Méthode
Description
DrawTextBiDiModeFlagsReadingOnly
Renvoie les bons paramètres pour le mode BiDi du
contrôle, en les limitant à la lecture.
AddBiDiModeExStyle
Ajoute le paramètre ExStyle flags approprié au
contrôle créé.
Fonctionnalités spécifiques aux cibles locales
Vous pouvez ajouter à votre application des fonctionnalités supplémentaires pour
des cibles locales spécifiques. En particulier, pour les langues asiatiques, il peut
être nécessaire à votre application de contrôler l’IME (Input Method Editor)
utilisé pour convertir en chaînes de caractères les touches frappées au clavier par
l’utilisateur.
Les composants VCL et CLX supportent la programmation de l’IME. La plupart
des contrôles fenêtrés autorisant directement la saisie de texte possèdent une
propriété ImeName qui permet de spécifier l’IME à utiliser lorsque le contrôle
reçoit la saisie. Ces contrôles possèdent également une propriété ImeMode qui
permet de spécifier en quoi l’IME doit convertir ce qui est frappé au clavier.
TWinControl introduit plusieurs méthodes protégées que vous pouvez utiliser
pour contrôler l’IME depuis les classes que vous avez définies. De plus, la
variable globale Screen vous fournit des informations concernant les IME
disponibles sur le système de l’utilisateur.
La variable globale Screen (disponible dans la VCL et dans CLX) fournit
également des informations concernant l’affectation des touches utilisée sur le
système de l’utilisateur. Vous pouvez l’utiliser pour obtenir des informations sur
les paramètres régionaux de l’environnement dans lequel tourne votre
application.
Conception de l’interface utilisateur
Lorsque vous créez une application pour plusieurs marchés étrangers, il est
important de concevoir son interface utilisateur afin qu’elle s’adapte aux
modifications effectuées lors de sa traduction.
Texte
Tout le texte apparaissant dans l’interface utilisateur doit être traduit. Le texte
anglais étant presque toujours plus court que les traductions, vous devez
concevoir les éléments de votre interface utilisateur qui affiche du texte en
réservant de l’espace pour l’expansion de ce texte. Concevez également les boîtes
de dialogue, les menus, les barres d’état et les autres éléments de l’interface
utilisateur affichant du texte de telle sorte qu’ils puissent facilement afficher des
chaînes plus longues. Evitez les abréviations qui ne peuvent exister dans les
langues utilisant des idéogrammes.
Création d’applications internationales
16-9
Internationalisation des applications
Les chaînes courtes grandissent plus que les phrases longues. Le tableau suivant
fournit une approximation des taux de foisonnement selon la longueur de la
chaîne initiale (en anglais) :
Tableau 16.3 Estimation des longueurs de chaîne
Longueur de la chaîne anglaise (en caractères)
Augmentation prévisible
1-5
100%
6-12
80%
13-20
60%
21-30
40%
31-50
20%
over 50
10%
Images graphiques
Le mieux est d’utiliser des images qui ne nécessitent pas de traduction, c’est-àdire des images qui ne contiennent pas de texte. Si vous devez inclure du texte
dans vos images, il est préférable d’utiliser un objet libellé avec arrière-plan
transparent par dessus l’image, plutôt que d’inclure le texte dans l’image ellemême.
Voici quelques autres considérations à prendre en compte lors de la création des
images graphiques. Essayez d’éviter les images spécifiques à une culture. Par
exemple, les boîtes à lettres sont très différentes selon les pays. Les symboles
religieux ne conviennent pas aux pays où il existe plusieurs religions
dominantes. Même les couleurs ont des connotations symboliques différentes
selon les cultures.
Formats et ordre de tri
Les formats de date, formats horaires, numériques et monétaires utilisés dans
votre application doivent être localisés selon les paramètres régionaux. Si vous
utilisez uniquement les formats de Windows, vous n’avez rien à traduire puisque
Windows les lit dans la base de registres de l’utilisateur. Cependant, si vous
spécifiez vos propres chaînes de format, déclarez-les comme constantes de
ressource afin de pouvoir les localiser.
L’ordre dans lequel les chaînes sont classées dépend également du pays. De
nombreuses langues européennes utilisent des caractères accentués et sont
classées différemment selon les paramètres régionaux. En outre, certaines
combinaisons de deux caractères peuvent être traitées par le tri comme un seul
caractère. Par exemple, en espagnol, la combinaison ch est triée comme étant un
caractère unique compris entre le c et le d. Parfois, un caractère est trié comme
s’il s’agissait de deux caractères séparés, par exemple le eszett allemand.
Correspondances entre claviers
Faites attention aux combinaisons de touches utilisées comme raccourcis. Les
caractères disponibles sur le clavier américain ne sont pas tous accessibles
facilement sur les autres claviers. Lorsque cela est possible, utilisez les touches
16-10
Guide du développeur
Internationalisation des applications
numériques et les touches de fonction comme raccourcis, puisqu’elles sont
aisément accessibles sur tous les claviers.
Isolement des ressources
La partie la plus évidente de la localisation d’une application consiste à traduire
les chaînes apparaissant dans l’interface utilisateur. Pour créer une application
pouvant être traduite sans modifier le moindre code, les chaînes de l’interface
utilisateur doivent être toutes placées dans un seul module. C++Builder crée
automatiquement un fichier a .dfm (.xfm dans les applications CLX) contenant
les ressources des menus, boîtes de dialogue et des bitmaps.
Outre les éléments d’interface apparents, vous devez isoler toutes les chaînes,
comme les messages d’erreur proposés à l’utilisateur. Les ressources chaîne ne
sont pas incluses dans le fichier fiche mais vous pouvez les isoler dans un fichier
ressource.
Création de DLL de ressource
L’isolement des ressources simplifie le processus de traduction. Le niveau
suivant d’isolement des ressources consiste à créer un module DLL. Un module
.DLL contient toutes les ressources et uniquement les ressources d’un
programme. Les DLL de ressource permettent de créer un programme gérant
plusieurs localisations en changeant simplement de DLL de ressource.
Utilisez l’expert Ressource DLL pour créer un module de ressource pour une
application. Vous devez avoir ouvert un projet compilé et enregistré pour utiliser
l’expert module de ressource. Cela crée un fichier RC contenant les tables de
chaîne à partir des fichiers RC utilisés et des chaînes resourcestring du projet, et
génère un projet pour une DLL de ressource qui contient les fiches et le fichier
RES créé. Le fichier RES est compilé à partir du nouveau fichier RC.
Vous devez créer un module de ressource pour chaque traduction que vous
voulez gérer. Chaque module de ressource doit avoir une extension du nom de
fichier spécifique à la localisation cible. Les deux premiers caractères indiquent la
langue cible et le troisième le pays pour la localisation. Si vous utilisez l’expert
Ressource DLL, cela est géré pour vous. Sinon, utilisez le code suivant pour
obtenir le code local de la traduction cible :
/* Ce rappel remplit une boîte liste avec les chaînes et les langues et pays associés*/
BOOL __stdcall EnumLocalesProc(char* lpLocaleString)
{
AnsiString LocaleName, LanguageName, CountryName;
LCID lcid;
lcid = StrToInt("$" + AnsiString(lpLocaleString));
LocaleName = GetLocaleStr(lcid, LOCALE_SABBREVLANGNAME, "");
LanguageName = GetLocaleStr(lcid, LOCALE_SNATIVELANGNAME, "");
CountryName = GetLocaleStr(lcid, LOCALE_SNATIVECTRYNAME, "");
if (lstrlen(LocaleName.c_str()) > 0)
Form1->ListBox1->Items->Add(LocaleName + ":" + LanguageName + "-" + CountryName);
return TRUE;
Création d’applications internationales
16-11
Internationalisation des applications
}
/* Cet appel provoque l’exécution de la fonction de rappel pour chaque localisation */
EnumSystemLocales((LOCALE_ENUMPROC)EnumLocalesProc, LCID_SUPPORTED);
Utilisation des DLL de ressource
L’exécutable, les DLL et les paquets (bpl) constituant l’application contiennent
toutes les ressources nécessaires. Cependant, pour remplacer ces ressources par
leurs versions localisées, il suffit simplement de fournir à l’application les DLL
de ressource localisées portant le même nom que les fichiers exécutables, DLL ou
paquets.
Lorsque votre application démarre, elle vérifie les paramètres régionaux du
système. Si elle trouve des DLL de ressource ayant les mêmes noms que les
fichiers EXE, DLL ou BPL qu’elle utilise, elle examine l’extension de ces DLL. Si
l’extension d’un module ressource correspond à la langue et au pays des
paramètres régionaux du système, votre application utilise les ressources de ce
module plutôt que les ressources de l’exécutable, de la DLL ou du paquet. S’il
n’y a pas de module ressource correspondant à la fois à la langue et au pays,
votre application essaie de trouver un module ressource correspondant à la
langue seule. S’il n’y a pas de module ressource correspondant à la langue, votre
application utilise les ressources compilées avec l’exécutable, la DLL ou le
paquet.
Si vous voulez que votre application utilise un module de ressource différent de
celui correspondant aux paramètres régionaux du système sur lequel elle
s’exécute, vous pouvez redéfinir l’entrée spécifiant la localisation dans les
registres Windows. Sous l’entrée HKEY_CURRENT_USER\Software\Borland\
Locales, ajoutez le chemin d’accès de l’application et le nom de fichier sous la
forme d’une chaîne et définissez comme valeur de la donnée l’extension du DLL
de ressource. Au démarrage, l’application recherche les DLL de ressource portant
cette extension avant de rechercher la localisation du système. L’affectation de
cette entrée de registre permet de tester des versions localisées de l’application
sans modifier la localisation de votre système.
Par exemple, la procédure suivante peut être utilisée dans le programme
d’installation ou de configuration afin de définir la localisation à utiliser au
moment de charger des applications C++Builder :
void SetLocalOverrides(char* FileName, char* LocaleOverride)
{
HKEY Key;
const char* LocaleOverrideKey = "Software\\Borland\\Locales";
if (RegOpenKeyEx(HKEY_CURRENT_USER, LocaleOverrideKey, 0, KEY_ALL_ACCESS, &Key)
== ERROR_SUCCESS) {
if (lstrlen(LocaleOverride) == 3)
RegSetValueEx(Key, FileName, 0, REG_SZ, (const BYTE*)LocaleOverride, 4);
RegCloseKey(Key);
}
}
16-12
Guide du développeur
Localisation des applications
Dans votre application, utilisez la fonction globale FindResourceHInstance pour
obtenir le handle du module de ressource en cours. Par exemple :
LoadString(FindResourceHInstance(HInstance), IDS_AmountDueName, szQuery, sizeof(szQuery));
Vous pouvez ainsi distribuer une seule application qui s’adapte automatiquement
à la localisation du système sur laquelle elle s’exécute en fournissant simplement
les DLL de ressource.
Basculement dynamique de DLL de ressource
En plus de la localisation d’une DLL de ressource au démarrage de l’application,
il est possible de basculer de DLL de ressource dynamiquement lors de
l’exécution. Pour ajouter cette fonctionnalité à vos propres applications, vous
devez inclure l’unité ReInit dans votre projet. ReInit se trouve dans l’exemple
Richedit du répertoire Examples. Pour basculer de langue, vous devez appeler
LoadResourceModule, en passant le LCID du nouveau langage, puis appeler
ReinitializeForms.
Par exemple, le code suivant bascule la langue en Français :
const FRENCH = (SUBLANG_FRENCH << 10) | LANG_FRENCH;
if (LoadNewResourceModule(FRENCH))
ReinitializeForms();
L’avantage de cette technique est que l’instance en cours de l’application et de
toutes ses fiches est utilisée. Il n’est pas nécessaire de mettre à jour les
paramètres du registre et de redémarrer l’application ou de recharger les
ressources nécessaires à l’application, comme la connexion aux serveurs base de
données.
Lorsqu’il y a basculement de la DLL de ressource, les propriétés spécifiées dans
la nouvelle DLL écrasent celles des instances en cours d’exécution des fiches.
Remarque
Toute modification effectuée dans les propriétés d’une fiche lors de l’exécution
est perdue. Une fois que la nouvelle DLL est chargée, les valeurs par défaut ne
sont pas initialisées. Evitez le code qui réinitialise les objets fiche dans leur état
de démarrage, mise à part les différences dues à la localisation.
Localisation des applications
Lorsque votre application est internationalisée, vous devez créer les versions
localisées pour les différents marchés étrangers sur lesquels vous souhaitez la
distribuer.
Localisation des ressources
Idéalement, vos ressources ont été isolées dans une DLL de ressource qui
contient les fichiers fiche (.dfm dans la VCL ou .xfm dans la CLX) et un fichier
Création d’applications internationales
16-13
Localisation des applications
ressource. Vous pouvez ouvrir vos fiches dans l’EDI et traduire les propriétés
importantes.
Remarque
Dans un projet DLL de ressource, vous ne pouvez pas ajouter ou supprimer de
composant. Pourtant, il est possible de modifier les propriétés, au risque de
générer des erreurs d’exécution. Veillez donc à ne modifier que les propriétés
qui requièrent une traduction. Pour éviter les erreurs, vous pouvez configurer
l’inspecteur d’objets pour n’afficher que les propriétés localisables ; pour ce faire,
cliquez avec le bouton droit sur l’inspecteur d’objets et utilisez le menu Voir
pour filtrer les catégories de propriétés non souhaitées.
Vous pouvez ouvrir le fichier RC et traduire des chaînes appropriées. Utilisez
l’éditeur StringTable en ouvrant le fichier RC à partir du gestionnaire de projet.
16-14
Guide du développeur
Chapitre
17
Déploiement des applications
Chapitre 17
Une fois que votre application C++Builder est terminée et qu’elle fonctionne,
vous pouvez la déployer. C’est-à-dire que vous la rendez disponible pour que
d’autres l’exécutent. Il est nécessaire d’effectuer un certain nombre d’opérations
pour déployer une application sur un autre ordinateur afin que l’application soit
entièrement opérationnelle. Les étapes nécessaires pour une application donnée
varient suivant le type de l’application. Les sections suivantes décrivent les
points à prendre en compte pour déployer les différentes applications :
•
•
•
•
•
•
Remarque
Déploiement d’applications généralistes
Déploiement d’applications CLX
Déploiement d’applications de bases de données
Déploiement d’applications Web
Programmation pour des environnements hôtes hétérogènes
Termes du contrat de licence logicielle
Les informations présentées dans ces sections concernent le déploiement
d’applications sous Windows. Si vous écrivez des applications multiplates-formes
à déployer sous Linux, vous devez vous reporter aux informations sur le
déploiement de la documentation Kylix.
Déploiement d’applications généralistes
En dehors du fichier exécutable, une application peut nécessiter des fichiers
complémentaires, par exemple des DLL, des fichiers paquets ou des applications
complémentaires. De plus, l’application peut nécessiter des entrées dans les
registres Windows que ce soit pour spécifier l’emplacement de fichiers auxiliaires
ou le paramétrage de l’application. Il est possible d’automatiser avec un
programme d’installation, comme InstallShield Express, le processus de copie des
fichiers d’une application sur un ordinateur, ainsi que le paramétrage des entrées
Déploiement des applications
17-1
Déploiement d’applications généralistes
de registre. Les étapes suivantes sont les principales étapes d’un déploiement et
concernent quasiment tous les types d’application :
• Utilisation des programmes d’installation
• Identification des fichiers de l’application
Les applications C++Builder qui accèdent à des bases de données ou qui
fonctionnent sur le Web nécessitent des étapes complémentaires d’installation en
plus de celles s’appliquant aux applications générales. Pour davantage
d’informations sur l’installation d’applications de bases de données, voir
“Déploiement d’applications de bases de données” à la page 17-7. Pour
davantage d’informations sur l’installation d’applications Web, voir “Déploiement
d’applications Web” à la page 17-11. Pour davantage d’informations sur
l’installation de contrôles ActiveX, voir “Déploiement d’un contrôle ActiveX sur
le Web” à la page 43-18. Pour davantage d’informations sur le déploiement
d’applications CORBA, voir le VisiBroker Installation and Administration Guide.
Utilisation des programmes d’installation
Les applications C++Builder simples, constituées d’un seul fichier exécutable,
s’installent facilement sur un ordinateur cible. Il suffit de copier le fichier sur
l’ordinateur. Mais les applications plus complexes composées de plusieurs
fichiers exigent une procédure d’installation plus sophistiquée. De telles
applications nécessitent un programme d’installation spécifique.
Les boîtes à outils d’installation automatisent le processus de création d’un
programme d’installation, le plus souvent sans avoir besoin d’écrire une seule
ligne de code. Les programmes d’installation créés avec les boîtes à outils
d’installation effectuent différentes tâches inhérentes à l’installation des
applications C++Builder, notamment : copier les fichiers exécutables et les fichiers
annexes sur l’ordinateur hôte, établir les entrées dans le registre Windows et
installer le moteur BDE pour les applications de bases de données basées
sur BDE.
InstallShield Express est une boîte à outils d’installation fournie avec C++Builder.
InstallShield Express est spécialement adapté à l’utilisation de C++Builder et du
moteur de bases de données Borland. Il est basé sur la technologie MSI,
l’installateur de Windows.
InstallShield Express n’est pas installé automatiquement lors de l’installation de
C++Builder, et doit être installé manuellement si vous voulez l’utiliser pour créer
des programmes d’installation. Exécutez le programme d’installation du CD
C++Builder pour installer InstallShield Express. Pour davantage d’informations
sur l’utilisation de InstallShield Express, voir son aide en ligne.
D’autres boîtes à outils d’installation sont disponibles. Cependant, si vous
déployez des applications de bases de données, vous ne devez utiliser que celles
basées sur la technologie MSI et celles qui sont certifiées pour déployer le
moteur de bases de données Borland (BDE).
17-2
Guide du développeur
Déploiement d’applications généralistes
Identification des fichiers de l’application
En plus du fichier exécutable, il peut être nécessaire de distribuer de nombreux
autres fichiers avec une application.
•
•
•
•
Fichiers de l’application
Fichiers paquet
Modules de fusion
Contrôles ActiveX
Fichiers de l’application
Il peut être nécessaire de distribuer les types suivants de fichiers avec une
application :
Tableau 17.1 Fichiers de l’application
Type
Extension de nom de fichier
Fichiers
programme
.exe et .dll
Fichiers paquet
.bpl, .bpi et .lib
Fichiers d’aide
.hlp, .cnt et .toc (s’il est utilisé) ou d’autres fichiers d’aide supportés par
votre application
Fichiers
ActiveX
.ocx (parfois supportés par une DLL)
Tables des
fichiers locaux
.dbf, .mdx, .dbt, .ndx, .db, .px, .y*, .x*, .mb, .val, .qbe, .gd*
Fichiers paquet
Si l’application utilise des paquets d’exécution, il faut distribuer les fichiers
paquet avec l’application. InstallShield Express gère l’installation des fichiers
paquet de la même manière que les DLL, copie ces fichiers et crée les entrées
nécessaires dans les registres Windows. Vous pouvez aussi utiliser des modules
de fusion pour déployer des paquets d’exécution avec des outils d’installation
basés sur MSI, comme InstallShield Express. Voir la section suivante pour plus
de détails.
Borland recommande l’installation des fichiers paquet d’exécution d’origine
Borland dans le répertoire Windows\System. Cela sert d’emplacement commun
afin que plusieurs applications puissent accéder à une seule instance de ces
fichiers. Pour les paquets que vous avez créés, il est recommandé de les installer
dans le répertoire de l’application. Seuls les fichiers .bpl doivent être distribués.
CLX
Si vous déployez des paquets avec des applications CLX, vous devez inclure
clx60.bpl au lieu de vcl60.bpl.
Si vous distribuez des paquets à d’autres développeurs, fournissez les fichiers
.bpl, .bpi, et .lib (si vous autorisez la liaison dynamique à vos paquets).
Déploiement des applications
17-3
Déploiement d’applications généralistes
Modules de fusion
InstallShield Express 3.0 est basé sur la technologie MSI, l’installateur de
Windows. C’est pour cette raison que C++Builder inclut les modules de fusion.
Les modules de fusion fournissent une méthode standard que vous pouvez
utiliser pour offrir aux applications du code partagé, des fichiers, des ressources,
des entrées du registre et la logique d’installation sous forme d’un seul fichier
composé. Vous pouvez utiliser des modules de fusion pour déployer des paquets
d’exécution avec des outils d’installation basés sur MSI, comme InstallShield
Express.
Les bibliothèques d’exécution ont certaines interdépendances en raison de la
façon dont elles ont été regroupées. Le résultat est que lorsqu’un paquet est
ajouté à un projet d’installation, l’outil d’installation ajoute automatiquement un
ou plusieurs autres paquets ou signale une dépendance sur eux. Par exemple, si
vous ajoutez le module de fusion VCLInternet à un projet d’installation, l’outil
d’installation va aussi ajouter automatiquement les modules VCLDatabase et
StandardVCL ou signaler une dépendance sur ces modules.
Les dépendances de chaque module de fusion sont énumérées dans le tableau
suivant. Les divers outils d’installation peuvent réagir différemment à ces
dépendances. InstallShield pour l’installateur Windows ajoute automatiquement
les modules requis s’il peut les trouver. Les autres outils peuvent se contenter de
signaler une dépendance ou peuvent générer un échec de construction si les
modules requis ne sont pas tous inclus dans le projet.
Tableau 17.2 Modules de fusion et leurs dépendances
17-4
Module de fusion
BPL inclus
Dépendances
ADO
adortl60.bpl
DatabaseRTL, BaseRTL
BaseClientDataSet
cds60.bpl
DatabaseRTL, BaseRTL, DataSnap,
dbExpress
BaseRTL
rtl60.bpl
No dependencies
BaseVCL
vcl60.bpl, vclx60.bpl
BaseRTL
BDEClientDataSet
bdecds60.bpl
BaseClientDataSet, DataBaseRTL,
BaseRTL, DataSnap, dbExpress, BDERTL
BDEInternet
inetdbbde60.bpl
Internet, DatabaseRTL, BaseRTL, BDERTL
BDERTL
bdertl60.bpl
DatabaseRTL, BaseRTL
DatabaseRTL
dbrtl60.bpl
BaseRTL
DatabaseVCL
vcldb60.bpl
BaseVCL, DatabaseRTL, BaseRTL
DataSnap
dsnap60.bpl
DatabaseRTL, BaseRTL
DataSnapConnection
dsnapcon60.bpl
DataSnap, DatabaseRTL, BaseRTL
DataSnapCorba
dsnapcrba60.bpl
DataSnapConnection, DataSnap,
DatabaseRTL, BaseRTL, BaseVCL
DataSnapEntera
dsnapent60.bpl
DataSnap, DatabaseRTL, BaseRTL,
BaseVCL
DBCompatVCL
vcldbx60.bpl
DatabaseVCL, BaseVCL, BaseRTL,
DatabaseRTL
dbExpress
dbexpress60.bpl
DatabaseRTL, BaseRTL
Guide du développeur
Déploiement d’applications généralistes
Tableau 17.2 Modules de fusion et leurs dépendances (suite)
Module de fusion
BPL inclus
Dépendances
dbExpressClientDataSet
dbxcds60.bpl
BaseClientDataSet, DataBaseRTL,
BaseRTL, DataSnap, dbExpress
DBXInternet
inetdbxpress60.bpl
Internet, DatabaseRTL, BaseRTL,
dbExpress, DatabaseVCL, BaseVCL
DecisionCube
dss60.bpl
TeeChart, BaseVCL, BaseRTL,
DatabaseVCL, DatabaseRTL, BDERTL
FastNet
nmfast60.bpl
BaseVCL, BaseRTL
InterbaseVCL
ibxpress60.bpl
BaseClientDataSet, BaseRTL, BaseVCL,
DatabaseRTL, DatabaseVCL, DataSnap,
dbExpress
Internet
inet60.bpl, inetdb60.bpl
DatabaseRTL, BaseRTL
InternetDirect
indy60.bpl
BaseVCL, BaseRTL
Office2000Components
dcloffice2k60.bpl
DatabaseVCL, BaseVCL, DatabaseRTL,
BaseRTL
QuickReport
qrpt60.bpl
BaseVCL, BaseRTL, BDERTL,
DatabaseRTL
SampleVCL
vclsmp60.bpl
BaseVCL, BaseRTL
TeeChart
tee60.bpl, teedb60.bpl,
teeqr60.bpl, teeui60.bpl
BaseVCL, BaseRTL
VCLIE
vclie60.bpl
BaseVCL, BaseRTL
VisualCLX
visualclx60.bpl
BaseRTL
VisualDBCLX
visualdbclx60.bpl
BaseRTL, DatabaseRTL, VisualCLX
WebDataSnap
webdsnap60.bpl
XMLRTL, Internet, DataSnapConnection,
DataSnap, DatabaseRTL, BaseRTL
WebSnap
websnap61.bpl,
vcljpg60.bpl
WebDataSnap, XMLRTL, Internet,
DataSnapConnection, DataSnap,
DatabaseRTL, BaseRTL, BaseVCL
XMLRTL
xmlrtl60.bpl
Internet, DatabaseRTL, BaseRTL
Contrôles ActiveX
Certains composants fournis avec C++Builder sont des contrôles ActiveX. Le
conteneur du composant est lié au fichier exécutable de l’application (ou à un
paquet d’exécution), mais le fichier .ocx du composant doit également être
distribué avec l’application. Ces composants sont :
•
•
•
•
•
Chart FX, copyright par SoftwareFX Inc.
VisualSpeller Control, copyright par Visual Components, Inc.
Formula One (tableur), copyright par Visual Components, Inc.
First Impression (VtChart), copyright par Visual Components, Inc.
Graph Custom Control, copyright par Bits Per Second Ltd.
Les contrôles ActiveX que vous créez doivent également être enregistrés sur
l’ordinateur cible avant d’être utilisés. Les programmes d’installation comme
InstallShield Express automatisent le processus d’enregistrement. Pour enregistrer
manuellement un contrôle ActiveX, choisissez Exécuter|Serveur ActiveX dans
Déploiement des applications
17-5
Déploiement d’applications CLX
l’EDI, utilisez l’application exempleTRegSvr ou l’utilitaire Microsoft
REGSRV32.EXE (qui n’est pas inclus dans les versions de Windows 9x).
Les fichiers DLL gérant un contrôle ActiveX doivent également être distribués
avec une application.
Applications complémentaires
Les applications complémentaires sont des programmes distincts en l’absence
desquels votre application C++Builder fonctionnerait de manière incomplète ou
ne fonctionnerait pas du tout. Les applications complémentaires peuvent être
celles fournies avec le système d’exploitation, par Borland ou des produits tiers.
Le programme utilitaire Server Manager de InterBase est un exemple de
programme complémentaire qui permet de gérer les utilisateurs et la sécurité des
bases de données InterBase.
Si une application dépend d’un programme complémentaire, assurez-vous de le
déployer avec votre application, si c’est possible. La distribution des programmes
complémentaires peut être limitée par des accords de licence de distribution.
Consultez la documentation d’un programme complémentaire pour des
informations spécifiques.
Emplacement des DLL
Vous pouvez installer les fichiers .DLL utilisés par une seule application dans le
même répertoire que l’application. Les DLL utilisées par plusieurs applications
doivent être installées de manière à être partagées par ces applications. La
convention courante veut qu’on installe ces fichiers DLL dans les répertoires
Windows ou Windows\System. Une autre méthode consiste à créer un répertoire
spécifique pour un groupe de fichiers DLL associés comme le fait l’installation
du moteur de bases de données Borland.
Déploiement d’applications CLX
Les étapes du déploiement des applications CLX sous Windows sont les mêmes
que pour les applications généralistes. Pour des informations sur le déploiement
d’applications généralistes, voir “Déploiement d’applications généralistes” à la
page 17-1. Pour davantage d’informations sur l’installation d’applications CLX de
bases de données, voir “Déploiement d’applications de bases de données” à la
page 17-7.
Remarque
Quand vous déployez des applications CLX sous Windows, vous devez inclure
qtintf.dll avec l’application pour inclure le runtime CLX. Si vous déployez des
paquets avec des applications CLX, vous devez inclure clx60.bpl au lieu de
vcl60.bpl.
Voir chapitre 14, “Développement d’applications multiplates-formes”, pour des
informations sur l’écriture des applications CLX.
17-6
Guide du développeur
Déploiement d’applications de bases de données
Déploiement d’applications de bases de données
Les applications accédant à des bases de données présentent des caractéristiques
d’installation propres au-delà de la copie du fichier exécutable de l’application
sur l’ordinateur cible. Le plus souvent, l’accès aux bases de données est géré par
un moteur de bases de données distinct dont les fichiers ne peuvent être liés au
fichier exécutable de l’application. Les fichiers de données, lorsqu’ils n’ont pas
été créés au préalable, doivent être rendus accessibles à l’application. Les
applications de bases de données multiniveaux nécessitent une gestion encore
plus spécialisée de l’installation, car les fichiers constituant l’application doivent
être installés sur plusieurs ordinateurs.
Différentes technologies de bases de données (ADO, BDE, dbExpress et InterBase
Express) sont supportées et les exigences du déploiement diffèrent pour chacune.
Indépendamment de celle que vous utilisez, vous devez vous assurer que le
logiciel client est installé sur le système où vous prévoyez d’exécuter
l’application de bases de données. ADO, BDE, dbExpress et InterBase Express
nécessitent également des pilotes pour interagir avec le logiciel client de la base
de données.
Des informations spécifiques sur la façon de déployer les applications de bases
de données dbExpress, BDE et multiniveaux sont décrites dans les sections
suivantes :
• Déploiement d’applications de bases de données dbExpress
• Déploiement d’applications BDE
• Déploiement d’applications de bases de données multiniveaux (DataSnap)
Les applications de bases de données qui utilisent des ensembles de données
client, comme TClientDataSet ou des fournisseurs d’ensembles de données
nécessitent d’inclure midaslib.dcu et crtl.dcu (pour la liaison statique quand un
exécutable autonome est fourni) ; si vous conditionnez votre application (avec
l’exécutable et les éventuelles DLL requises), vous devez inclure Midas.dll.
Si vous déployez des applications de bases de données qui utilisent ADO, vous
devez vous assurer que MDAC version 2.1 ou ultérieure est installé sur le
système où vous prévoyez d’exécuter l’application. MDAC est installé
automatiquement avec des logiciels comme Windows 2000 et Internet Explorer
version 5 ou ultérieure. Vous devez aussi vous assurer que les pilotes pour le
serveur de base de données auquel vous voulez vous connecter sont installés sur
le client. Aucune autre étape de déploiement n’est requise.
Si vous déployez des applications de bases de données qui utilisent InterBase
Express, vous devez vous assurer que le client InterBase est installé sur le
système où vous prévoyez d’exécuter l’application. InterBase nécessite que
gd32.dll et interbase.msg soient situés dans un répertoire accessible. Aucune
autre étape de déploiement n’est requise. Les composants InterBase Express
communiquant directement avec l’API InterBase Client, aucun pilote
supplémentaire n’est requis. Pour plus d’informations, reportez-vous au
Embedded Installation Guide situé sur le site web de Borland.
Déploiement des applications
17-7
Déploiement d’applications de bases de données
Outre les technologies décrites ici, vous pouvez utiliser des moteurs de bases de
données fournis par des tiers pour gérer l’accès aux bases de données des
applications C++Builder. Consultez la documentation ou le vendeur du moteur
de bases de données pour ce qui concerne les problèmes de droit, d’installation
et de configuration du moteur.
Déploiement d’applications de bases de données dbExpress
dbExpress est un ensemble de pilotes natifs légers qui offrent un accès rapide
aux informations des bases de données. Etant également disponible sous Linux,
dbExpress autorise le développement multiplate-forme. Pour plus d’informations
sur l’utilisation des composants dbExpress, voir chapitre 26, “Utilisation
d’ensembles de données unidirectionnels”.
Vous pouvez déployer des applications dbExpress sous forme d’un fichier
exécutable autonome ou sous forme d’un fichier exécutable contenant les DLL
des pilotes dbExpress associés.
Pour déployer les applications dbExpress sous forme de fichiers exécutables
autonomes, les fichiers objet dbExpress doivent être liés de façon statique dans
votre exécutable. Vous faites cela en incluant les DCU suivantes, situées dans le
répertoire lib :
Tableau 17.3 Déploiement dbExpress sous forme d’exécutable autonome
Unité de base
de données
Remarque
Quand l’inclure
dbExpINT
Applications se connectant aux bases de données InterBase
dbExpORA
Applications se connectant aux bases de données Oracle
dbExpDB2
Applications se connectant aux bases de données DB2
dbExpMYS
Applications se connectant aux bases de données MySQL 3.22.x
dbExpMYSQL
Applications se connectant aux bases de données MySQL 3.23.x
crtl
Requises par tous les exécutables utilisant dbExpress
MidasLib
Requises par les exécutables dbExpress utilisant des ensembles de données
client comme TSQLClientDataSet
Pour les applications de bases de données utilisant Informix, vous ne pouvez pas
déployer d’exécutable autonome. Déployez à la place un fichier exécutable avec
la DLL de pilote dbexpinf.dll (listée dans le tableau suivant).
Si vous ne déployez pas un exécutable autonome, vous pouvez déployer avec
votre exécutable les pilotes dbExpress et les DLL DataSnap associés. Le tableau
suivant énumère les DLL appropriées et indique quand il faut les inclure:
Tableau 17.4 Déploiement dbExpress avec les DLL des pilotes
17-8
DLL de bases
de données
Quand les déployer
dbexpinf.dll
Applications se connectant aux bases de données Informix
dbexpint.dll
Applications se connectant aux bases de données InterBase
Guide du développeur
Déploiement d’applications de bases de données
Tableau 17.4 Déploiement dbExpress avec les DLL des pilotes (suite)
DLL de bases
de données
Quand les déployer
dbexpora.dll
Applications se connectant aux bases de données Oracle
dbexpdb2.dll.
Applications se connectant aux bases de données DB2
dbexpmys.dll
Applications se connectant aux bases de données MySQL 3.22.x
dbexpmysql.dll
Applications se connectant aux bases de données MySQL 3.23.x
Midas.dll
Requis par les applications de bases de données utilisant des ensembles
de données client
Déploiement d’applications BDE
Le moteur de bases de données Borland (BDE) définit une API importante pour
l’interaction avec les bases de données. De tous les mécanismes d’accès aux
données, le BDE gère le plus large éventail de fonctions et offre les meilleurs
utilitaires. Il représente le meilleur support de manipulation des données dans
les tables Paradox ou dBASE.
L’accès aux bases de données dans une application se fait par le biais de divers
moteurs de bases de données. Une application peut utiliser le BDE ou un moteur
fourni par un tiers. SQL Links est fourni (mais pas avec toutes les éditions) pour
permettre un accès natif aux systèmes de bases de données SQL. Les sections
suivantes décrivent l’installation des éléments d’accès aux bases de données
d’une application :
• Le moteur de bases de données Borland
• SQL Links
Le moteur de bases de données Borland
Pour utiliser les composants de données standard C++Builder ayant un accès aux
bases de données, le moteur de bases de données Borland (BDE) doit être
présent et disponible. Voir le document BDEDEPLOY pour connaître les droits et
restrictions s’appliquant à la distribution du BDE.
Borland recommande l’utilisation de InstallShield Express (ou d’un autre
programme d’installation certifié) pour l’installation du BDE. InstallShield
Express crée les entrées de registre nécessaires et définit les alias nécessaires à
l’application. Il est important d’utiliser un programme certifié pour déployer les
fichiers BDE car :
• Une installation incorrecte du BDE ou des sous-ensembles BDE peut empêcher
le fonctionnement d’autres applications utilisant le BDE. Ces applications sont
des produits Borland, mais également des programmes tiers utilisant le BDE.
• Sous Windows 32 bits 95/NT ou plus, les informations de configuration BDE
sont stockées dans les registres Windows et non pas dans des fichiers .ini,
comme c’était le cas avec Windows 16 bits. La création ou la suppression de
ces entrées lors de l’installation ou de la désinstallation est une tâche
complexe.
Déploiement des applications
17-9
Déploiement d’applications de bases de données
Il est possible de n’installer que la partie du BDE nécessaire à une application.
Si, par exemple, une application n’utilise que des tables Paradox, il est seulement
nécessaire d’installer la partie du BDE indispensable à l’accès aux tables Paradox.
Cela réduit l’espace disque nécessaire à une application. Les programmes
d’installation certifiés, comme InstallShield Express, sont capables d’effectuer une
installation partielle du BDE. Il faut prendre garde à laisser intacts les fichiers
système BDE inutilisés par l’application installée mais nécessaires à d’autres
programmes déjà installés.
SQL Links
SQL Links propose les pilotes permettant de connecter une application au
logiciel client d’une base de données SQL (via le moteur de bases de données
Borland). Voir le document DEPLOY pour connaître les droits et restrictions
s’appliquant à la distribution de SQL Links. Comme pour le moteur de bases de
données Borland, SQL Links doit être déployé en utilisant InstallShield Express
(ou tout autre programme certifié).
Remarque
SQL Links connecte le BDE au logiciel client et pas directement à la base de
données SQL même. Il est donc toujours nécessaire d’installer le programme
client du système de bases de données SQL utilisé. Reportez-vous à la
documentation de votre système SQL ou consultez le vendeur pour davantage
d’informations sur l’installation et la configuration du logiciel client.
Le tableau suivant présente les noms des fichiers de pilote et de configuration
utilisés par SQL Links pour se connecter aux différents systèmes de base de
données SQL. Ces fichiers sont fournis avec SQL Links et sont redistribuables en
accord avec la licence C++Builder.
Tableau 17.5 Fichiers des logiciels client des bases de données SQL
Vendeur
Fichiers redistribuables
Oracle 7
SQLORA32.DLL et SQL_ORA.CNF
Oracle 8
SQLORA8.DLL et SQL_ORA8.CNF
Sybase Db-Lib
SQLSYB32.DLL et SQL_SYB.CNF
Sybase Ct-Lib
SQLSSC32.DLL et SQL_SSC.CNF
Microsoft SQL Server
SQLMSS32.DLL et SQL_MSS.CNF
Informix 7
SQLINF32.DLL et SQL_INF.CNF
Informix 9
SQLINF9.DLL et SQL_INF9.CNF
DB/2 2
SQLDB232.DLL et SQL_DB2.CNF
DB/2 5
SQLDB2V5.DLL et SQL_DB2V5.CNF
InterBase
SQLINT32.DLL et SQL_INT.CNF
Installez SQL Links en utilisant InstallShield Express ou tout autre programme
d’installation certifié. Pour des informations spécifiques concernant l’installation
et la configuration de SQL Links, voir le fichier d’aide SQLLNK32.HLP qui est
installé, par défaut, dans le répertoire principal du BDE.
17-10
Guide du développeur
Déploiement d’applications Web
Déploiement d’applications de bases de données multiniveaux
(DataSnap)
DataSnap fournit des fonctionnalités de bases de données multiniveaux aux
applications C++Builder en permettant aux applications client de se connecter
aux fournisseurs d’un serveur d’applications.
Installez DataSnap avec une application multiniveau au moyen d’InstallShield
Express (ou d’un autre utilitaire de script d’installation certifié Borland).
Consultez le document DEPLOY (situé dans le répertoire C++Builder principal)
pour de plus amples détails sur les fichiers qui nécessitent d’être redistribués
avec une application. Reportez-vous aussi au document REMOTE pour obtenir
des informations sur les fichiers DataSnap qui peuvent être redistribués et de
quelle façon.
Déploiement d’applications Web
Certaines applications C++Builder sont conçues pour être exécutées sur le Web,
sous la forme de DLL d’extension côté serveur (ISAPI et Apache), d’applications
CGI ou de fiches ActiveForm.
Les étapes du déploiement d’applications Web sont identiques à celles des
applications généralistes à cette différence que les fichiers de l’application sont
déployés sur le serveur Web. Pour des informations sur l’installation de
programmes standard, voir “Déploiement d’applications généralistes” à la
page 17-1. Pour davantage d’informations sur le déploiement d’applications de
bases de données Web, voir “Déploiement d’applications de bases de données” à
la page 17-7.
Les considérations suivantes sont spécifiques au déploiement d’applications Web :
• Pour les applications de bases de données BDE, le moteur de bases de
données Borland (ou tout autre moteur de bases de données) est installé avec
les fichiers de l’application sur le serveur Web.
• Pour les applications dbExpress, les DLL dbExpress doivent être incluses dans
le chemin d’accès. S’il est inclus, le pilote dbExpress doit être installé avec les
fichiers de l’application sur le serveur Web.
• La sécurité pour les répertoires doit être définie de sorte que l’application
puisse accéder à tous les fichiers nécessaires de la base de données.
• Le répertoire contenant une application doit avoir des attributs de lecture et
d’exécution.
• L’application ne doit pas utiliser de chemins d’accès codés “en dur” pour
accéder aux bases de données et aux autres fichiers.
• L’emplacement d’un contrôle ActiveX est indiqué par le paramètre
CODEBASE de la balise HTML <OBJECT>.
Le déploiement sur Apache est décrit dans la section suivante.
Déploiement des applications
17-11
Déploiement d’applications Web
Déploiement sur serveur Apache
WebBroker supporte Apache version 1.3.9 et ultérieure pour les DLL et les
applications CGI.
Vous activez et configurez les modules et les applications en modifiant le fichier
httpd.conf d’Apache (situé en principe dans le répertoire \conf du répertoire
d’installation d’Apache).
Activation des modules
Vos DLL doivent être situées physiquement dans le sous-répertoire Modules
d’Apache.
Deux modifications à httpd.conf sont nécessaires pour activer un module.
1 Ajoutez une entrée LoadModule pour permettre à Apache de localiser et de
charger votre DLL. Exemple :
LoadModule MyApache_module modules/Project1.dll
2 Ajoutez une entrée localisateur de ressource (n’importe où dans httpd.conf
après l’entrée LoadModule). Exemple :
# Exemple de spécification d’emplacement pour un projet nommé project1.
<Location /project1>
SetHandler project1-handler
</Location>
Cela permet de transmettre au module Apache toutes les requêtes à
http://www.undomaine.com/project1.
La directive SetHandler spécifie l’application serveur Web qui gère la requête.
L’argument SetHandler doit être défini par la valeur de la variable globale
ContentType.
Applications CGI
Quand vous créez des applications CGI, l’option ExecCGI et la clause SetHandler
du répertoire physique (spécifié dans la directive Directory du fichier httpd.conf)
doivent être définis pour permettre l’exécution de programmes, afin que le script
CGI puisse être exécuté. Pour vous assurer que les permissions ont été
correctement définies, utilisez la directive Alias en activant à la fois Options
ExecCGI et SetHandler.
Remarque
Une autre approche consiste à utiliser la directive ScriptAlias (sans Options
ExecCGI), mais l’utilisation de cette approche peut empêcher votre application
CGI de lire des fichiers du répertoire ScriptAlias.
La ligne suivante de httpd.conf est un exemple d’utilisation de la directive Alias
pour créer un répertoire virtuel sur votre serveur et marquer l’emplacement
exact de votre script CGI :
Alias/MyWeb/"c:/httpd/docs/MyWeb/"
17-12
Guide du développeur
Programmation pour des environnements hôtes hétérogènes
Cela permettrait de satisfaire des requêtes comme /MyWeb/mycgi.exe en exécutant
le script c:\httpd\docs\MyWeb\mycgi.exe.
Vous pouvez aussi définir Options par All ou par ExecCGI en utilisant la
directive Directory dans httpd.conf. La directive Options contrôle les
fonctionnalités du serveur qui seront disponibles dans un répertoire donné.
Les directives Directory sont utilisées pour englober un ensemble de directives
s’appliquant au répertoire nommé et à ses sous-répertoires. Voici un exemple de
directive Directory :
<Directory "c:/httpd/docs/MyWeb">
AllowOverride None
Options ExecCGI
Order allow,deny
Allow from all
AddHandler cgi-script exe cgi
</Directory>
Dans cet exemple, Options est définie par ExecCGI ce qui permet l’exécution des
scripts CGI dans le répertoire MyWeb. la clause AddHandler permet à Apache
de savoir que les fichiers ayant l’extension exe ou cgi sont des scripts CGI
(exécutables).
Remarque
Apache s’exécute en local sur le serveur sous le compte spécifié par la directive
User du fichier httpd.conf. Assurez-vous que l’utilisateur a bien les droits
appropriés pour accéder aux ressources nécessaires à l’application.
Consultez le fichier LICENSE d’Apache, fourni avec votre distribution Apache,
pour avoir d’autres informations sur le déploiement. Pour davantage
d’informations sur la configuration d’Apache, voir http://www.apache.org.
Programmation pour des environnements hôtes hétérogènes
En raison des caractéristiques des divers environnements des systèmes
d’exploitation, certains éléments peuvent varier selon les préférences de
l’utilisateur ou la configuration. Les points suivants peuvent affecter le
déploiement d’une application sur un autre ordinateur :
•
•
•
•
•
Résolution d’écran et profondeur de couleurs
Fontes
Versions des systèmes d’exploitation
Applications complémentaires
Emplacement des DLL
Déploiement des applications
17-13
Programmation pour des environnements hôtes hétérogènes
Résolution d’écran et profondeur de couleurs
La taille du bureau et le nombre de couleurs disponibles sur un ordinateur sont
configurable et dépendent du matériel installé. Il est probable que ces
caractéristiques ne sont pas identiques sur les systèmes utilisés pour le
développement et ceux sur lesquels l’application est déployée.
L’aspect d’une application (fenêtres, objets et taille des fontes) sur des
ordinateurs utilisant des résolutions différentes peut être géré de différentes
manières :
• Concevez l’application avec la plus basse résolution employée par les
utilisateurs (généralement, 640x480). Il n’y a rien à faire dans ce cas pour
redimensionner les objets dynamiquement afin de les rendre proportionnels à
la taille d’affichage de l’écran du système hôte. Visuellement, plus la
résolution est importante et plus les objets apparaissent petits.
• Effectuez la conception en utilisant la résolution du système employé pour
effectuer le développement et, à l’exécution, redimensionnez dynamiquement
toutes les fiches et les objets proportionnellement à la différence de résolution
écran entre le système de développement et le système hôte (en utilisant un
coefficient de variation entre les résolutions écran).
• Effectuez la conception en utilisant une résolution du système de
développement et, à l’exécution, redimensionnez dynamiquement les fiches de
l’application. Selon la position des contrôles visuels dans les fiches, cette
option peut nécessiter que les fiches disposent de barres de défilement pour
que l’utilisateur puisse accéder à tous les contrôles des fiches.
Si vous n’utilisez pas de redimensionnement dynamique
Si les fiches et les contrôles visuels constituant une application ne sont pas
redimensionnés dynamiquement à l’exécution, concevez les éléments de
l’application en utilisant la résolution la plus basse. Sinon, les fiches d’une
application exécutée sur un ordinateur utilisant une résolution d’écran plus faible
que celle utilisée pour le système de développement risquent de déborder de
l’écran.
Si par exemple, le système de développement est configuré avec une résolution
écran de 1024x768 et qu’une fiche est conçue avec une largeur de 700 pixels, une
partie de cette fiche ne sera pas visible sur le bureau d’un ordinateur configuré
avec une résolution de 640x480.
Si vous redimensionnez dynamiquement les fiches et les contrôles
Si les fiches et les contrôles visuels d’une application sont dynamiquement
redimensionnés, adaptez tous les aspects du processus de redimensionnement
pour garantir un aspect optimal de l’application pour toutes les résolutions écran
possibles. Voici quelques facteurs à considérer lorsque vous redimensionnez
dynamiquement les éléments visuels d’une application :
• Le redimensionnement des fiches et des contrôles visuels est effectué en
utilisant un ratio calculé en comparant la résolution écran du système de
17-14
Guide du développeur
Programmation pour des environnements hôtes hétérogènes
développement à celle du système sur lequel l’application est installée. Utilisez
une constante pour représenter une dimension de la résolution écran du
système de développement : la hauteur ou la largeur exprimée en pixels.
Récupérez à l’exécution la même dimension pour le système de l’utilisateur en
utilisant la propriété TScreen::Height ou TScreen::Width. Divisez la valeur pour
le système de développement par la valeur pour le système de l’utilisateur
afin d’en dériver le ratio entre les résolutions écran des deux systèmes.
• Redimensionnez les éléments visuels de l’application (fiches et contrôles) en
réduisant ou en augmentant la taille des éléments et leur position dans les
fiches. Ce redimensionnement est proportionnel à la différence entre les
résolutions écran des systèmes du développeur et de l’utilisateur.
Redimensionnez et repositionnez automatiquement les contrôles visuels des
fiches en définissant la propriété TCustomForm::Scaled par true et en appelant
la méthode TWinControl::ScaleBy (TWidgetControl.ScaleBy pour les applications
multiplates-formes). La méthode ScaleBy ne modifie pas la hauteur ou la
largeur de la fiche. Il faut effectuer cette opération manuellement en
multipliant les valeurs en cours des propriétés Height et Width par le ratio de
différence des résolutions écran.
• Les contrôles d’une fiche peuvent être redimensionnés manuellement au lieu
d’utiliser la méthode TWinControl::ScaleBy (TWidgetControl.ScaleBy pour les
applications multiplates-formes), en faisant référence à chaque contrôle dans
une boucle et en affectant ses dimensions et sa position. La valeur des
propriétés Height et Width des contrôles visuels est multipliée par le ratio de
différence des résolutions écran. Repositionnez les contrôles visuels en
fonction de la résolution écran en multipliant la valeur des propriétés Top et
Left par le même ratio.
• Si une application a été conçue sur un ordinateur configuré pour une
résolution écran supérieure à celle de l’utilisateur, les tailles de fontes seront
réduites dans le processus de redimensionnement des contrôles visuels. Si la
taille de la fonte lors de la conception est petite, la fonte redimensionnée à
l’exécution risque d’être trop petite pour être lisible. Par exemple, supposons
que la taille de fonte par défaut d’une fiche est 8. Avec un système de
développement ayant une résolution écran de 1024x768 et celui de l’utilisateur
une résolution 640x480, les contrôles visuels seront réduits d’un facteur 0,625
(640 / 1024 = 0,625). La taille de fonte d’origine de 8 est réduite à 5
(8 * 0,625 = 5). Le texte de l’application apparaît irrégulier et illisible quand il
s’affiche l’affiche dans la fonte réduite.
• Certains contrôles visuels comme TLabel et TEdit se redimensionnent
dynamiquement quand la taille de la fonte du contrôle change. Cela peut
affecter les applications déployées quand les fiches et les contrôles sont
redimensionnés dynamiquement. Le redimensionnement du contrôle provoqué
par la modification de taille de la fonte se cumule à la modification de taille
due au redimensionnement proportionnel aux résolutions écran. Cet effet
indésirable est neutralisé en définissant la propriété AutoSize de ces contrôles
par false.
Déploiement des applications
17-15
Programmation pour des environnements hôtes hétérogènes
• Il faut éviter d’utiliser des coordonnées en pixel explicites, par exemple pour
écrire directement dans un canevas. Il faut à la place modifier les coordonnées
en leur appliquant un ratio proportionnel au ratio de différence des
résolutions écran entre le système de développement et celui d’utilisation. Si,
par exemple, l’application dessine un rectangle dans le canevas de dix pixels
de haut sur vingt pixels de large, multipliez les valeurs dix et vingt par le
ratio de différence de résolution. Ainsi, vous êtes certain que le rectangle
apparaît visuellement de la même taille pour différentes résolutions écran.
Adaptation à des profondeurs de couleurs variables
Pour prendre en compte le fait que tous les ordinateurs sur lesquels l’application
est déployée ne sont pas configurés avec les mêmes possibilités de couleurs, la
solution la plus simple consiste à n’utiliser que des graphiques avec le plus petit
nombre possible de couleurs. Cela s’applique particulièrement aux glyphes des
contrôles qui doivent utiliser des graphiques en 16 couleurs. Pour l’affichage
d’images, vous pouvez soit proposer plusieurs copies de l’image dans différentes
résolutions et niveaux de couleur ou indiquer dans l’application la résolution
minimale et le nombre de couleurs nécessaires à l’application.
Fontes
Windows dispose d’un jeu standard de fontes TrueType et vectorielles. Linux est
founi avec un jeu de fontes dépendant de la distribution. Quand vous concevez
une application devant être déployée sur d’autres ordinateurs, tenez compte du
fait que tous les ordinateurs n’ont pas nécessairement de fontes en-dehors des
jeux standard.
Les composants texte utilisés dans l’application ne doivent utiliser que des fontes
qui sont très probablement disponibles sur les ordinateurs cible.
Quand l’utilisation d’une fonte non standard est absolument nécessaire dans une
application, vous devez distribuer cette fonte avec l’application. Soit le
programme d’installation, soit l’application même doit installer la fonte sur
l’ordinateur cible. La distribution de fontes créées par des tiers peut être sujette à
des restrictions imposées par leurs créateurs.
Windows dispose d’une protection contre l’utilisation d’une fonte inexistante sur
un système. Il lui substitue une fonte existante, la plus proche possible. Bien que
cela empêche les erreurs dues à des fontes manquantes, le résultat final peut
dégrader l’aspect visuel de l’application. Il est préférable de prévoir cette
éventualité à la conception.
Pour mettre à la disposition d’une application Windows une fonte non standard,
utilisez les fonctions AddFontResource et DeleteFontResource de l’API Windows.
Déployez les fichiers .fot des fontes non-standard avec l’application.
17-16
Guide du développeur
Termes du contrat de licence logicielle
Versions des systèmes d’exploitation
Quand vous utilisez des fonctions de l’API du système d’exploitation ou accédez
à des zones du système d’exploitation depuis une application, il y a le risque
que cette fonction, cette opération ou cette zone ne soit pas disponible sur des
ordinateurs utilisant une version du système différente.
Pour prendre cette possibilité en compte, vous avez différentes possibilités :
• Spécifiez dans les spécifications logicielles de l’application les versions du
système sous lesquelles l’application peut s’exécuter. C’est alors à l’utilisateur
de n’installer et de n’utiliser l’application que dans des versions compatibles
du système d’exploitation.
• Testez la version du système d’exploitation lors de l’installation de
l’application. Si une version incompatible du système d’exploitation est
détectée, arrêtez le processus d’installation ou prévenez l’utilisateur du
problème.
• Testez la version du système d’exploitation à l’exécution, juste avant
d’exécuter une opération qui n’est pas applicable à toutes les versions. Si une
version incompatible du système d’exploitation est détectée, abandonnez
l’opération et informez l’utilisateur. Vous pouvez aussi utiliser du code
différent pour les différentes versions du système d’exploitation.
Certaines opérations sont effectuées de façons différentes sous Windows 95/98
et sous Windows NT/2000/XP. Utilisez la fonction GetVersionEx de l’API
Windows pour déterminer la version de Windows.
Remarque
Termes du contrat de licence logicielle
La distribution de certains des fichiers associés aux applications C++Builder est
sujette à des limitations ou est purement et simplement interdite. Les documents
suivants décrivent les stipulations légales concernant la redistribution de ces
fichiers :
DEPLOY
Le document DEPLOY aborde certains aspects légaux de la distribution de divers
composants et utilitaires et autres produits pouvant faire partie ou être associés à
une application C++Builder. DEPLOY est un document installé dans le répertoire
principal de C++Builder. Il aborde les sujets suivants :
•
•
•
•
•
•
Fichiers .exe, .dll et .bpl
Composants et paquets de conception
Moteur de bases de données Borland (BDE)
Contrôles ActiveX
Images exemple
SQL Links
Déploiement des applications
17-17
Termes du contrat de licence logicielle
README
Le document README contient des informations de dernière minute
C++Builder ; il peut donc contenir des informations pouvant affecter les droits de
redistribution des composants, utilitaires ou autres éléments. README est un
document installé dans le répertoire principal de C++Builder.
Contrat de licence
Le contrat de licence C++Builder est un document imprimé qui traite des droits
et obligations légales concernant C++Builder.
Documentation de produits vendus par un tiers
Les droits de redistribution des composants, utilitaires, applications utilitaires,
moteurs de bases de données ou autres logiciels provenant d’un tiers sont régis
par le vendeur fournissant le produit. Consultez la documentation du produit ou
le vendeur pour des informations concernant la redistribution du produit avec
une application C++Builder avant de le distribuer.
17-18
Guide du développeur
Partie
II
Développement d’applications
de bases de données
Partie II
Les chapitres de cette partie présentent les concepts et les connaissances
nécessaires à la création d’applications de bases de données C++Builder.
Remarque
Les niveaux de gestion possibles pour la conception d’applications de bases de
données dépendent de votre édition de C++Builder. En particulier, vous devez
disposer de l’édition Professionnelle pour utiliser les ensembles de données client
et de l’édition Entreprise pour développer des applications de bases de données
multiniveaux.
Développement d’applications de bases de données
Chapitre
18
Conception d’applications
de bases de données
Chapitre 18
Les applications de bases de données permettent aux utilisateurs d’interagir avec
les informations stockées dans les bases de données. Les bases de données
permettent de structurer les informations et de les partager entre plusieurs
applications.
C++Builder permet de gérer les applications de bases de données relationnelles.
Les bases de données relationnelles organisent les informations en tables, qui
contiennent des lignes (enregistrements) et des colonnes (champs). Ces tables
peuvent être manipulées par des opérations simples appelées calculs relationnels.
Lorsque vous concevez une application de bases de données, vous devez
comprendre comment les données sont structurées. A partir de cette structure,
vous pouvez concevoir une interface utilisateur pour afficher les données et
permettre à l’utilisateur d’entrer de nouvelles informations et de modifier les
données existantes.
Ce chapitre présente certains aspects courants de la conception d’une application
de bases de données et les décisions inhérentes à la conception d’une interface
utilisateur.
Utilisation des bases de données
C++Builder comprend de nombreux composants permettant d’accéder aux bases
de données et de représenter les informations qu’elles contiennent. Ils sont
regroupés en fonction du mécanisme d’accès aux données :
• La page BDE de la palette de composants contient les composants qui utilisent
le moteur de bases de données Borland (BDE, Borland Database Engine). Le
BDE définit une API importante pour l’interaction avec les bases de données.
De tous les mécanismes d’accès aux données, le BDE gère le plus large
Conception d’applications de bases de données
18-1
Utilisation des bases de données
éventail de fonctions et offre les meilleurs utilitaires. Il représente le meilleur
support de manipulation des données dans les tables Paradox ou dBASE.
Toutefois, c’est le mécanisme le plus compliqué à déployer. Pour plus
d’informations sur l’utilisation des composants BDE, voir chapitre 24,
“Utilisation du moteur de bases de données Borland”.
• La page ADO de la palette de composants contient les composants qui
utilisent ActiveX Data Objects (ADO) pour accéder aux informations de bases
de données via OLEDB. ADO est un standard Microsoft. De nombreux pilotes
ADO permettent de se connecter à différents serveurs de bases de données.
Grâce aux composants ADO, vous pouvez intégrer votre application dans un
environnement ADO (par exemple, en recourant à des serveurs d’applications
ADO). Pour plus d’informations sur l’utilisation des composants ADO, voir
chapitre 25, “Utilisation des composants ADO”.
• La page dbExpress de la palette de composants contient les composants qui
utilisent dbExpress pour accéder aux informations de bases de données.
dbExpress est un ensemble de pilotes légers qui offrent l’accès le plus rapide
aux informations de bases de données. En outre, étant également disponibles
sous Linux, les composants dbExpress autorisent le développement multiplateforme. Toutefois, les composants base de données dbExpress prennent en
charge l’éventail le plus réduit de fonctions de manipulation de données. Pour
plus d’informations sur l’utilisation des composants dbExpress, voir
chapitre 26, “Utilisation d’ensembles de données unidirectionnels”.
• La page InterBase de la palette de composants contient les composants qui
accèdent directement aux bases de données InterBase, sans passer par une
couche moteur distincte.
• La page AccèsBD de la palette de composants contient les composants
utilisables avec tout mécanisme d’accès aux données. Cette page comprend
TClientDataset, qui peut utiliser les données stockées sur disque ou, par le
biais du composant TDataSetProvider de cette même page, les composants d’un
des autres groupes. Pour plus d’informations sur l’utilisation des ensembles de
données client, voir chapitre 27, “Utilisation d’ensembles de données client”.
Pour plus d’informations sur TDataSetProvider, voir chapitre 28, “Utilisation
des composants fournisseur”.
Remarque
Chaque version de C++Builder comprend ses propres pilotes d’accès aux
serveurs de bases de données par le biais de BDE, ADO ou dbExpress.
Lorsque vous concevez une application de base de données, vous devez choisir
l’ensemble de composants à utiliser. Chaque mécanisme d’accès aux données
diffère par l’éventail des fonctions prises en charge, la facilité de déploiement et
la capacité des pilotes à gérer divers serveurs de bases de données.
Outre un mécanisme d’accès aux données, vous devez choisir un serveur de
bases de données. Il existe différents types de bases de données et vous devez
prendre en considération les avantages et les inconvénients de chaque type avant
de choisir un serveur de bases de données.
18-2
Guide du développeur
Utilisation des bases de données
Bien que tous ces types de bases de données contiennent des tables qui stockent
des informations, certains présentent certaines caractéristiques telles que :
• Sécurité des bases de données
• Transactions
• Intégrité référentielle, procédures stockées et déclencheurs
Types de bases de données
Les serveurs de bases de données relationnelles diffèrent par la façon dont ils
stockent les informations et par celle dont ils permettent à plusieurs utilisateurs
d’y accéder simultanément. C++Builder prend en charge deux types de serveurs
de bases de données relationnelles :
• Les serveurs de bases de données distants résident sur des machines
distinctes. Parfois, les données d’un serveur de bases de données distant ne
résident pas sur une seule machine mais sont réparties entre plusieurs
serveurs. Bien que les serveurs de bases de données distants ne stockent pas
les informations de la même manière, ils fournissent une interface logique
commune aux clients, en l’occurrence SQL (Structured Query Language). Le
fait que vous y accédiez à l’aide de SQL leur vaut parfois d’être appelés
serveurs SQL (ils sont aussi appelés système de gestion de bases de données
distant.) Outre les commandes courantes qui composent SQL, la plupart des
serveurs de bases de données distants gèrent une variante unique du langage
SQL. InterBase, Oracle, Sybase, Informix, Microsoft SQL Server et DB2 sont
des exemples de serveurs SQL.
• Les bases de données locales résident sur votre disque local ou sur un réseau
local. Elles disposent d’interfaces de programmation d’applications
propriétaires pour accéder aux données. Lorsqu’elles sont partagées par
plusieurs utilisateurs, elles utilisent des mécanismes de verrouillage de
fichiers. C’est pourquoi elles sont parfois appelées bases de données à base de
fichiers. Paradox, dBASE, FoxPro et Access sont des exemples de bases de
données locales.
Les applications qui utilisent des bases de données locales sont appelées
applications à niveau unique car l’application et la base de données partagent
un système de fichiers unique. Les applications qui utilisent des serveurs de
bases de données distants sont appelées applications à niveau double ou
applications multiniveaux car l’application et la base de données fonctionnent
sur des systèmes (ou niveaux) indépendants.
Le choix du type de base de données à utiliser dépend de plusieurs facteurs. Par
exemple, il se peut que vos données soient déjà stockées dans une base de
données existante. Si vous créez les tables de bases de données qu’utilise votre
application, les points suivants vous intéressent.
• Combien d’utilisateurs partageront ces tables ? Les serveurs de bases de
données distants sont conçus pour permettre à plusieurs utilisateurs d’accéder
simultanément aux informations. Ils peuvent prendre en charge plusieurs
utilisateurs grâce à un mécanisme appelé transactions. Certaines bases de
Conception d’applications de bases de données
18-3
Utilisation des bases de données
données locales (telles que Local InterBase) offrent également un support de
transaction mais bon nombre ne proposent que des mécanismes de
verrouillage de fichiers tandis que certaines ne présentent aucun support
multi-utilisateur (tels que les fichiers d’ensembles de données client).
• Quelle quantité de données les tables contiendront-elles ? Les serveurs de
bases de données distants peuvent contenir davantage de données que les
bases de données locales. Certains serveurs de bases de données distants sont
conçus pour stocker des quantités volumineuses de données tandis que
d’autres répondent à des impératifs différents (tels que la rapidité des mises à
jour).
• Quel type de performance (vitesse) attendez-vous de la base de données ? Les
bases de données locales sont généralement plus rapides que les serveurs de
bases de données distants car elles résident sur le même système. Chaque
serveur de base de données distant étant conçu pour un type d’opération
particulier, vous pouvez prendre en compte la question de la performance
dans le choix du serveur.
• Quel est le type de support qui sera disponible pour l’administration des
bases de données ? Les bases de données locales ne nécessitent pas autant de
support que les serveurs de bases de données distants. Généralement, leur
coût de fonctionnement est inférieur car elles ne nécessitent pas de serveurs
indépendants ni de licences de site onéreuses.
Sécurité des bases de données
Les bases de données contiennent souvent des informations sensibles. Différentes
bases de données offrent des schémas de sécurité pour protéger ces informations.
Certaines bases de données, comme Paradox et dBASE, n’offrent une protection
qu’au niveau des tables ou des champs. Lorsque les utilisateurs essaient
d’accéder aux tables protégées, ils doivent fournir un mot de passe. Une fois
identifiés, ils ne peuvent visualiser que les champs (colonnes) pour lesquels ils
disposent d’une permission.
La plupart des serveurs SQL requièrent un mot de passe et un nom d’utilisateur
pour être utilisés. Une fois que l’utilisateur est connecté à la base de données, le
nom d’utilisateur et le mot de passe déterminent les tables qu’il peut utiliser.
Pour plus d’informations sur l’attribution de mots de passe pour accéder aux
serveurs SQL, voir “Contrôle de la connexion au serveur” à la page 21-4.
Lorsque vous concevez des applications de bases de données, vous devez
envisager le type d’authentification requis par votre serveur de base de données.
Les applications sont souvent conçues de telle sorte que la connexion de base de
données explicite soit masquée, si bien que les utilisateurs ont uniquement
besoin de se connecter aux applications. Si vous ne souhaitez pas que vos
utilisateurs aient besoin de fournir un mot de passe, vous devez soit utiliser une
base de données qui n’en requiert pas, soit fournir le mot de passe et le nom
d’utilisateur au serveur par programmation. Lorsque vous fournissez le mot de
passe par programmation, vous devez veiller à ce que la sécurité ne soit pas
violée par lecture du mot de passe à partir de l’application.
18-4
Guide du développeur
Utilisation des bases de données
Si vous obligez les utilisateurs à fournir un mot de passe, vous devez déterminer
à quel moment ce dernier est requis. Si vous utilisez une base de données locale
mais envisagez de passer à un serveur SQL plus important, vous pouvez inviter
l’utilisateur à fournir son mot de passe au moment où il se connecte à la base de
données SQL plutôt qu’à l’ouverture des différentes tables.
Si votre application requiert plusieurs mots de passe pour la connexion à
plusieurs bases de données ou systèmes protégés, vous pouvez demander aux
utilisateurs de fournir un mot de passe maître unique qui permet d’accéder à
une table de mots de passe requis par ces systèmes. L’application fournit alors
les mots de passe par programmation, sans que les utilisateurs aient besoin de
fournir plusieurs mots de passe.
Dans les applications multiniveaux, vous pouvez utiliser un modèle de sécurité
différent. Vous pouvez utiliser HTTPs ou COM+ pour contrôler l’accès aux
niveaux intermédiaires et laisser ces derniers gérer tous les détails relatifs à
l’accès aux serveurs de bases de données.
Transactions
Une transaction est un groupe d’actions qui doivent être menées avec succès sur
une ou plusieurs tables dans une base de données avant d’être validées (rendues
définitives). Si l’une des actions du groupe échoue, toutes les actions sont
abandonnées (annulées).
Les transactions garantissent ce qui suit :
• Toutes les mises à jour d’une même transaction sont soit validées, soit
annulées et ramenées à leur état précédent. C’est ce que nous appelons
atomicité.
• Une transaction est une transformation valide de l’état d’un système, en
maintenant les constantes de cet état. C’est ce que nous appelons cohérence.
• Les transactions simultanées ne voient pas les résultats partiels et non validés
les unes des autres afin de ne pas créer d’incohérences dans l’état de
l’application. C’est ce que nous appelons isolation.
• Les mises à jour validées des enregistrements survivent aux pannes, y compris
les pannes de communication, les pannes de processus et les pannes système
des serveurs. C’est ce que nous appelons durabilité.
Les transactions protègent ainsi contre les défaillances matérielles qui se
produisent au milieu d’une commande de base de données ou d’un ensemble de
commandes. L’ouverture d’une transaction vous permet de bénéficier d’un état
durable après les pannes survenues sur les supports disque. Les transactions
constituent aussi la base du contrôle simultané de plusieurs utilisateurs sur les
serveurs SQL. Lorsque tous les utilisateurs interagissent avec la base de données
par le biais de transactions, les commandes d’un utilisateur ne peuvent pas
altérer l’unité d’une transaction d’un autre utilisateur ; le serveur SQL planifie les
transactions entrantes, qui réussissent ou échouent en bloc.
Conception d’applications de bases de données
18-5
Architecture des bases de données
Bien que le support des transactions ne fasse pas partie de la plupart des bases
de données locales, il est fourni par InterBase local. En outre, les pilotes du
moteur de bases de données Borland offrent pour certaines un support des
transactions limité. Le support des transactions de bases de données est fourni
par le composant qui représente la connexion à la base de données. Pour plus de
détails sur la gestion des transactions à l’aide d’un composant connexion de base
de données, voir “Gestion des transactions” à la page 21-6.
Dans les applications multiniveaux, vous pouvez créer des transactions qui
comprennent des actions autres que des opérations de bases de données ou qui
englobent plusieurs bases de données. Pour plus de détails sur l’utilisation des
transactions dans les applications multiniveaux, voir “Gestion des transactions
dans les applications multiniveaux” à la page 29-19.
Intégrité référentielle, procédures stockées et déclencheurs
Toutes les bases de données relationnelles présentent certaines caractéristiques
communes qui permettent aux applications de stocker et de manipuler les
données. En outre, les bases de données offrent souvent des fonctionnalités qui
leur sont propres et qui s’avèrent utiles pour garantir la cohérence des relations
entre les tables d’une base de données. C’est-à-dire :
• Intégrité référentielle. L’intégrité référentielle offre un mécanisme permettant
d’éviter la cassure des relations maître/détail entre les tables. Lorsque
l’utilisateur essaie de supprimer un champ de la table maître, pouvant aboutir
à la création d’enregistrements détail orphelins, les règles de l’intégrité
référentielle évitent la suppression ou suppriment automatiquement les
enregistrements détail orphelins.
• Procédures stockées. Les procédures stockées sont des jeux d’instructions SQL
nommés et enregistrés sur un serveur SQL. Les procédures stockées réalisent
généralement des tâches de bases de données courantes sur le serveur et
renvoient parfois des ensembles d’enregistrements (ensembles de données).
• Déclencheurs. Les déclencheurs sont des ensembles d’instructions SQL
automatiquement créées en réponse à une commande.
Architecture des bases de données
Les applications de bases de données sont construites à partir d’éléments
d’interface utilisateur, de composants qui représentent les informations de bases
de données (ensembles de données) et de composants qui connectent ceux-ci les
uns aux autres et à la source des informations de bases de données.
L’architecture de votre application de base de données représente l’organisation
de tous ces éléments.
18-6
Guide du développeur
Architecture des bases de données
Structure générale
Bien qu’il existe de nombreuses façons d’organiser les composants d’une
application de base de données, la plupart d’entre elles suivent le schéma
général illustré par la figure suivante :
Figure 18.1 Architecture de base de données générique
Fiche interface utilisateur
Il est conseillé d’isoler l’interface utilisateur sur une fiche complètement
indépendante du reste de l’application. Cela présente plusieurs avantages.
L’isolation de l’interface utilisateur des composants qui représentent les
informations de bases de données vous apporte une plus grande flexibilité
conceptuelle : les modifications que vous apportez à la gestion des informations
de bases de données n’imposent pas la réécriture de l’interface utilisateur, tandis
que celles que vous apportez à l’interface utilisateur ne vous obligent pas à
modifier la partie de l’application qui utilise la base de données. En outre, ce
type d’isolation vous permet de développer des fiches communes à diverses
applications, ce qui garantit la cohérence de l’interface utilisateur. De plus, si le
référentiel d’objets contient des liens vers des fiches convenablement conçues,
vous -même et les autres développeurs disposez d’une base de travail et n’êtes
pas obligés de commencer chaque nouveau projet à partir d’aucun élément.
Enfin, le partage des fiches vous permet de développer des standards
d’entreprise pour les interfaces des applications. Pour plus d’informations sur la
création de l’interface utilisateur d’une application de base de données, voir
“Conception de l’interface utilisateur” à la page 18-17.
Module de données
Si vous avez isolé votre interface utilisateur dans sa propre fiche, vous pouvez
utiliser un module de données afin d’y placer les composants qui représentent
les informations de bases de données (ensembles de données), et les composants
qui connectent ces ensembles de données aux autres éléments de votre
application. Comme les fiches de l’interface utilisateur, les modules de données
peuvent figurer dans le référentiel d’objets en vue d’être réutilisés ou partagés
par les applications.
Source de données
Le premier élément du module de données est une source de données. La source
de données relie l’interface utilisateur à un ensemble de données qui représente
les informations d’une base de données. Plusieurs contrôles orientés données
Conception d’applications de bases de données
18-7
Architecture des bases de données
disposés sur une fiche peuvent partager une même source de données. Dans ce
cas, le contenu de chaque contrôle est synchronisé : lorsque l’utilisateur parcourt
les enregistrements, les valeurs figurant dans les différents champs de
l’enregistrement actif sont affichées dans les contrôles correspondants.
Ensemble de données
L’ensemble de données constitue le cœur de votre application de base de
données. Ce composant représente un ensemble d’enregistrements de la base de
données sous-jacente. Ces enregistrements peuvent être les données d’une seule
table de base de données, un sous-ensemble des champs ou des enregistrements
d’une table ou des informations émanant de plusieurs tables jointes en une vue
unique. L’utilisation d’ensembles de données protège la logique de votre
application de la restructuration des tables physiques de la base de données.
Lorsque la base de données sous-jacente change, vous pouvez être amené à
modifier la façon dont le composant ensemble de données spécifie les données
qu’il contient, mais le reste de votre application peut continuer à fonctionner
sans subir de modifications. Pour plus d’informations sur les propriétés et
méthodes courantes des ensembles de données, voir chapitre 22, “Présentation
des ensembles de données”.
Connexion des données
Différents types d’ensembles de données utilisent différents mécanismes de
connexion aux informations de la base de données sous-jacente. Ces différents
mécanismes déterminent les variantes majeures de l’architecture des applications
de bases de données que vous créez. Il existe essentiellement quatre mécanismes
de connexion aux données :
• Connexion directe à un serveur de bases de données. La plupart des
ensembles de données utilisent un descendant de TCustomConnection pour
représenter la connexion à un serveur de bases de données.
• Utilisation d’un fichier dédié sur disque. Les ensembles de données client
permettent d’utiliser un fichier dédié sur disque. Aucun composant connexion
séparé n’est requis lors de l’utilisation d’un fichier dédié car l’ensemble de
données client est en mesure de lire et d’écrire dans le fichier.
• Connexion à un autre ensemble de données. Les ensembles de données client
peuvent utiliser les données fournies par un autre ensemble de données. Un
composant TDataSetProvider fait office d’intermédiaire entre l’ensemble de
données client et son ensemble de données source. Ce fournisseur d’ensemble
de données peut résider dans le même module de données que l’ensemble de
données client ou faire partie d’un serveur d’application exécuté sur une autre
machine. Si le fournisseur fait partie d’un serveur d’application, vous devez
utiliser un descendant spécial de TCustomConnection pour représenter la
connexion au serveur.
• Obtention des données à partir d’un objet DataSpace RDS. Les ensembles de
données ADO peuvent utiliser un composant TRDSConnection pour rassembler
les données des applications de bases de données multiniveaux élaborées à
l’aide de serveurs d’applications ADO.
18-8
Guide du développeur
Architecture des bases de données
Parfois, ces mécanismes peuvent être combinés en une même application.
Connexion directe à un serveur de bases de données
L’architecture de base de données la plus courante est celle dans laquelle
l’ensemble de données utilise un composant connexion pour établir une
connexion à serveur de bases de données. L’ensemble de données peut alors
directement lire les données du serveur et y envoyer les modifications. Cette
architecture est illustrée par la figure suivante :
Figure 18.2 Connexion directe au serveur de bases de données
Chaque type d’ensemble de données utilise son propre type de composant
connexion, qui représente un mécanisme d’accès aux données unique :
• Si l’ensemble de données est un ensemble de données BDE tel que TTable,
TQuery ou TStoredProc, le composant connexion est un objet TDataBase. Vous
connectez l’ensemble de données au composant base de données en
définissant sa propriété Database. Vous n’avez pas besoin d’ajouter
explicitement un composant base de données lorsque vous utilisez un
ensemble de données BDE. Si vous définissez la propriété DatabaseName de
l’ensemble de données, un composant base de données est automatiquement
créé à l’exécution.
• Si l’ensemble de données est un ensemble de données ADO tel que
TADODataSet, TADOTable, TADOQuery ou TADOStoredProc, le composant
connexion est un objet TADOConnection. Vous connectez l’ensemble de
données au composant connexion ADO en définissant sa propriété
ADOConnection. Comme pour les ensembles de données BDE, vous n’avez pas
besoin d’ajouter explicitement le composant connexion : par contre, vous
pouvez définir la propriété ConnectionString de l’ensemble de données.
• Si l’ensemble de données est un ensemble de données dbExpress tel que
TSQLDataSet, TSQLTable, TSQLQuery ou TSQLStoredProc, le composant
Conception d’applications de bases de données
18-9
Architecture des bases de données
connexion est un objet TSQLConnection. Vous connectez l’ensemble de données
au composant connexion SQL en définissant sa propriété SQLConnection.
Lorsque vous utilisez des ensembles de données dbExpress, vous devez
explicitement ajouter le composant connexion. En outre, les ensembles de
données dbExpress présentent la particularité d’être toujours unidirectionnels
et accessibles en lecture seule : cela signifie que vous pouvez uniquement
parcourir les enregistrements dans l’ordre et que vous ne pouvez pas utiliser
les méthodes d’édition des ensembles de données.
• Si l’ensemble de données est un ensemble de données InterBase Express tel
que TIBDataSet, TIBTable, TIBQuery ou TIBStoredProc, le composant connexion
est un objet TIBDatabase. Vous connectez l’ensemble de données au composant
base de données InterBase en définissant sa propriété Database. Comme dans
le cas des ensembles de données dbExpress, vous devez explicitement ajouter
le composant connexion.
Outre les composants précédemment cités, vous pouvez utiliser un ensemble de
données client spécialisé tel que TBDEClientDataSet, TSQLClientDataSet ou
TIBClientDataSet avec un composant connexion de base de données. Lorsque
vous utilisez l’un de ces ensembles de données client, spécifiez le type approprié
de composant connexion comme valeur de la propriété DBConnection.
Bien que chaque type d’ensemble de données utilise un composant connexion
différent, ils effectuent tous la plupart des mêmes tâches et mettent à disposition
la plupart des mêmes propriétés, méthodes et événements. Pour plus
d’informations sur les points communs des différents composants connexion de
base de données, voir chapitre 21, “Connexion aux bases de données”.
Cette architecture représente une application à niveau unique ou une application
à niveau double, selon que le serveur de base de données est une base de
données locale ou un serveur de base de données distant. La logique qui
manipule les informations de bases de données figure dans l’application qui
implémente l’interface utilisateur, tout en étant confinée dans un module de
données.
Remarque
Toutes les versions de C++Builder ne proposent pas les pilotes ou composants
connexion requis pour créer des applications à niveau double.
Utilisation d’un fichier dédié sur disque
Dans sa forme la plus simple, une application de base de données que vous
écrivez ne recourt à aucun serveur de bases de données. Par contre, elle utilise
MyBase, exploite la possibilité qu’ont les ensembles de données client de
s’enregistrer eux-mêmes dans un fichier puis d’en extraire les données.
18-10
Guide du développeur
Architecture des bases de données
Cette architecture est illustrée par la figure suivante :
Figure 18.3 Application de base de données à base de fichiers
Lorsque vous utilisez cette approche à base de fichiers, votre application écrit les
modifications sur disque à l’aide de la méthode SaveToFile de l’ensemble de
données client. SaveToFile accepte un paramètre, le nom du fichier qui est créé
(ou écrasé) et qui contient la table. Lorsque vous souhaitez lire une table
précédemment écrite à l’aide de la méthode SaveToFile, utilisez la méthode
LoadFromFile. LoadFromFile accepte aussi un paramètre, le nom du fichier
contenant la table.
Si vous chargez les données toujours à partir du même fichier et les y
enregistrez toujours, vous pouvez utiliser la propriété FileName au lieu des
méthodes SaveToFile et LoadFromFile. Lorsque FileName a pour valeur un nom de
fichier valide, les données sont automatiquement chargées à partir du fichier à
l’ouverture de l’ensemble de données client et enregistrées dans le fichier à la
fermeture de l’ensemble de données client.
Cette architecture simple de type fichier est une application à niveau unique.
La logique qui manipule les informations de bases de données figure dans
l’application qui implémente l’interface utilisateur, tout en étant confinée dans
un module de données.
L’approche de type fichier présente l’avantage de la simplicité. Aucun serveur de
bases de données ne doit être installé, configuré ou déployé (bien que l’ensemble
de données client requière midas.dll). Aucune licence de site ni administration de
base de données n’est nécessaire.
En outre, certaines versions de C++Builder vous permettent de réaliser des
conversions entre des documents XML arbitraires et les paquets de données
utilisés par un ensemble de données client. Par conséquent, l’approche à base de
fichiers permet d’utiliser des documents XML ainsi que des ensembles de
données dédiés. Pour plus d’informations sur la conversion entre des documents
XML et des paquets de données d’ensemble de données client, voir chapitre 30,
“Utilisation de XML dans les applications de bases de données”.
L’approche à base de fichiers ne gère pas les situations multi-utilisateurs.
L’ensemble de données doit être totalement dédié à l’application. Les données
sont enregistrées dans des fichiers sur disque puis chargées ultérieurement, mais
aucune protection intégrée n’empêche un utilisateur d’écraser les fichiers de
données d’un autre utilisateur.
Conception d’applications de bases de données
18-11
Architecture des bases de données
Pour plus d’informations sur l’utilisation d’un ensemble de données client dont
les données sont stockées sur disque, voir “Utilisation d’un ensemble de données
client avec des données basées sur des fichiers” à la page 27-39.
Connexion à un autre ensemble de données
Certains ensembles de données client spécialisés utilisent le BDE ou dbExpress
pour se connecter à un serveur de bases de données. Ces ensembles de données
client spécialisés sont, de fait, des composants composites qui utilisent de
manière interne un autre ensemble de données pour accéder aux données et un
composant fournisseur pour empaqueter les données de l’ensemble de données
source et appliquer les mises à jour au serveur de bases de données. Ces
composants composites requièrent des ressources système supplémentaires mais
présentent certains avantages :
• Les ensembles de données client offrent le support le plus robuste pour la
manipulation des mises à jour placées en mémoire cache. Par défaut, les
autres types d’ensembles de données envoient directement les modifications
au serveur de bases de données. Vous pouvez réduire le trafic réseau en
utilisant un ensemble de données qui place les mises à jour en mémoire cache
localement puis les applique toutes en une seule transaction. Pour plus
d’informations sur les avantages de l’utilisation d’ensembles de données client
pour placer les mises à jour en mémoire cache, voir “Utilisation d’un
ensemble de données client pour mettre en cache les mises à jour” à la
page 27-18.
• Les ensembles de données client peuvent appliquer les modifications
directement au serveur de bases de données lorsque l’ensemble de données est
accessible en lecture seule. Lorsque vous utilisez dbExpress, outre que c’est le
seul moyen de parcourir librement les données, c’est la seule façon de
modifier les données dans l’ensemble de données. Même lorsque vous
n’utilisez pas dbExpress, le résultat de certaines requêtes et de toutes les
procédures stockées est accessible en lecture seule. L’utilisation d’un ensemble
de données client offre un procédé standard pour rendre ces données
modifiables.
• Un ensemble de données client pouvant directement utiliser des fichiers
dédiés sur disque, son utilisation peut être combinée avec un modèle à base
de fichiers afin d’offrir une application “briefcase” souple. Pour plus
d’informations sur le modèle “briefcase”, voir “Combinaison des approches” à
la page 18-16.
Outre ces ensembles de données client spécialisés, il existe un ensemble de
données client générique (TClientDataSet), qui ne comprend pas de fournisseur
d’ensemble de données ni d’ensemble de données interne. Bien que
TClientDataSet ne possède pas de mécanisme interne d’accès aux bases de
données, vous pouvez le connecter à un autre ensemble de données externe à
partir duquel il peut lire les données et vers lequel il peut envoyer les mises
à jour.
18-12
Guide du développeur
Architecture des bases de données
Malgré son caractère quelque peu complexe, cette approche s’avère parfois
opportune :
• L’ensemble de données source et le fournisseur d’ensemble de données étant
externes, vous pouvez plus facilement contrôler la façon dont ils lisent les
données et appliquent les mises à jour. Par exemple, le composant fournisseur
met à disposition une série d’événements qui ne sont pas disponibles lorsque
vous utilisez un ensemble de données client spécialisé pour accéder aux
données.
• Lorsque l’ensemble de données source est externe, vous pouvez le lier à un
autre ensemble de données dans une relation maître/détail. Un fournisseur
externe convertit automatiquement cette organisation en un seul ensemble de
données comprenant des détails imbriqués. Lorsque l’ensemble de données
source est interne, vous ne pouvez pas créer d’ensembles détail imbriqués de
cette façon.
• La connexion d’un ensemble de données client à un ensemble de données
externe est une architecture qui peut facilement évoluer vers une architecture
multiniveau. Etant donné que le processus de développement est d’autant plus
coûteux et consommateur d’énergie que le nombre de niveaux augmente, vous
pouvez commencer à développer votre application en tant qu’application à
niveau unique ou double. A mesure qu’augmenteront la quantité de données,
le nombre d’utilisateurs et le nombre des différentes applications accédant aux
données, vous serez éventuellement amené à adopter une architecture
multiniveau. Si vous pensez utiliser à terme une architecture multiniveau, il
peut être judicieux de commencer en utilisant un ensemble de données client
avec un ensemble de données source externe. Ainsi, lorsque vous transférez
vers un niveau intermédiaire la logique de l’accès et de la manipulation des
données, le développement effectué est protégé car le code est réutilisable à
mesure que l’application prend de l’ampleur.
• TClientDataSet peut être lié à tout ensemble de données source. Cela signifie
que vous pouvez utiliser des ensembles de données personnalisés (composants
tierces parties) pour lesquels n’existe aucun ensemble de données client
spécialisé correspondant. Certaines versions de C++Builder incluent même des
composants fournisseur spéciaux qui connectent un ensemble de données
client à un document XML plutôt qu’à un autre ensemble de données. (Cela
fonctionne de la même façon que la connexion d’un ensemble de données
client à un autre ensemble de données (source), à la différence que le
fournisseur XML utilise un document XML à la place d’un ensemble de
données. Pour plus d’informations sur ces fournisseurs XML, voir “Utilisation
d’un document XML comme source pour un fournisseur” à la page 30-9.)
L’architecture qui connecte un ensemble de données client à un ensemble de
données externe se présente sous deux versions :
• Connexion d’un ensemble de données client à un autre ensemble de données
dans la même application.
• Utilisation d’une architecture multiniveau.
Conception d’applications de bases de données
18-13
Architecture des bases de données
Connexion d’un ensemble de données client à un autre ensemble
de données dans la même application
Le fait d’utiliser un composant fournisseur vous permet de vous connecter
TClientDataSet à un autre ensemble de données (source). Le fournisseur assemble
les informations de bases de données en paquets de données transportables
(utilisables par les ensembles de données client) et applique à un serveur de base
de données les mises à jour reçues dans les paquets delta (que les ensembles de
données client créent). Cette architecture est illustrée par la figure suivante.
Figure 18.4 Architecture combinant un ensemble de données client et un autre ensemble de données
Cette architecture représente une application à niveau unique ou une application
à niveau double, selon que le serveur de base de données est une base de
données locale ou un serveur de base de données distant. La logique qui
manipule les 

Manuels associés