FAQ DelphiConsultez toutes les FAQ
Nombre d'auteurs : 124, nombre de questions : 934, dernière mise à jour : 23 octobre 2024 Ajouter une question
Cette FAQ a été réalisée à partir des questions fréquemment posées sur les forums Delphi et Delphi et bases de données de www.developpez.com et de l'expérience personnelle des auteurs.
Nous tenons à souligner que cette FAQ ne garantit en aucun cas que les informations qu'elle propose soient correctes. Les auteurs font le maximum, mais l'erreur est humaine. Cette FAQ ne prétend pas non plus être complète. Si vous souhaitez y apporter des corrections ou la compléter, contactez un responsable (lien au bas de cette page).
Nous espérons que cette FAQ saura répondre à vos attentes. Nous vous en souhaitons une bonne lecture.
L'équipe Delphi de Developpez.com.
- 4.1. Types de données (35)
- 4.2. Compilation (12)
- 4.3. Langage - Divers (10)
- Qu'est-ce que le mot clé Self et comment l'utiliser ?
- Comment accéder aux méthodes "Protected" d'une classe ?
- Comment accéder aux variables privées d'une classe ?
- Comment affecter Nil lors d'une exception dans un constructeur ?
- Un tableau de procédures : comment et pourquoi ?
- Comment passer un nombre variable de paramètres à une procédure externe ?
- Comment retrouver le GUID d'une interface ?
- Comment vider le buffer du clavier ?
- Comment calculer la cardinalité d'un ensemble ou trouver le nombre de bits "allumés" dans une variable ?
- Comment créer une fonction pouvant renvoyer un résultat de type quelconque ?
- Comment passer un nombre variable de paramètres à une procédure ?
- Comment créer un GUID dynamiquement ?
- Comment réaliser un IF immédiat ?
- Comment calculer la distance entre deux points GPS
- Comment modifier facilement plusieurs composants visuels avec RTTI
Schématiquement à la question qui suis-je ?, le mot clé Self répond moi-même en renvoyant la référence de l'instance en cours d'utilisation au sein d'une méthode de sa classe. Self permet donc de connaître l'instance en cours et dans certains cas de lever toute ambiguité.
Par exemple ici :
Code delphi : | Sélectionner tout |
1 2 3 4 5 | procedure TObject.Free; begin if Self <> nil then Destroy; end; |
Self est aussi une référence sur une classe et peut donc ne pas être affectée (MonObjet:=Nil). Cette méthode étant statique son appel ne pose pas de problème dans ce cas.
Et là :
Code delphi : | Sélectionner tout |
1 2 3 | with TPaintBox(Sender).Canvas.Brush do begin Color := Self.Color; |
Self permet ici de savoir exactement de quelle propriété on parle.
Dans le cas d'une méthode de classe l'utilisation de Self à une signification différente, il s'agit d'une variable de type référence de classe contenant la classe effective de l'objet. Schématiquement dans ce cas le mot clé Self répond à la question De quelle classe suis-je ? en répondant De la classe TMaclasse.
Supposons que vous souhaitez, pour telle ou telle raison utiliser une Méthode protégée d'un Objet particulier que l'on appellera MaSomme (cela peut être un composant visuel ou non par exemple).
Supposons que notre objet ai été défini comme suit, dans une unité à part :
Code delphi : | Sélectionner tout |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 | unit UnitSomme; interface type TSomme = class private FA,FB: Integer; FResultat: Integer; procedure SetA(const Value: Integer); procedure SetB(const Value: Integer); protected // La fonction que l'on souhaiterais utiliser... function Somme(A,B:Integer):Integer; public constructor Create; destructor Destroy; property A:Integer read FA write SetA; property B:Integer read FB write SetB; property Resultat:Integer read FResultat; end; ... implementation ... function TSomme.Somme(A,B:Integer):Integer; begin result:=A+B; end; ... end. |
Code delphi : | Sélectionner tout |
1 2 3 4 5 6 | var MaSomme:TSomme; ... MaSomme.A:=5; MaSomme.B:=9; Result:=MaSomme.Resultat; // ... |
La solution :
L'idée est la suivante :
- Créer une classe dérivée de la classe TFactorielle, que l'on appellera par exemple TPublicFactorielle , ceci dans l'unité même ou l'on souhaite utiliser la méthode.
- de Transtyper notre objet MaFactorielle comme suit :
Code delphi : | Sélectionner tout |
TPublicFactorielle(MaFactorielle)
De cette façon, nous pourrons contourner la protection objet sans pour autant enfreindre les lois de la POO.
Voici l'unité d'une fiche fraîchement créée dans laquelle nous avons ajouté le code ad hoc :
Code delphi : | Sélectionner tout |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 | unit Unit1; interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, unitFactorielle; type //Notre classe dérivée... //A travers sa définitions, toutes les méthodes protégées deviennent //visibles pour tout le code écrit dans notre unité unit1 TPublicSomme=class(TSomme); TForm1 = class(TForm) private { Déclarations privées } public { Déclarations publiques } procedure Calcule; end; ... implementation ... procedure TForm1.Calcule; var MaSOmme:TSomme; begin ... //On transtype MaSomme afin d'utiliser la méthode rendu visible Result:=TPublicSomme(MaSomme).Somme(A,B); .... end; |
Dans la FAQ/Sources, nous retrouverons les astuces suivantes qui exploitent ce procédé, ou en dérivent :
Un exemple concret :
On souhaite, par exemple, accéder depuis le code d'une fiche à la variable privée FImageIndex d'un TMenuItem , (n'importe quelle variable privée de n'importe quel objet de n'importe quelle classe aurait pu faire l'affaire).
Solution :
Tout réside dans les possibilités du transtypage, et du fait que les types classes ne sont rien d'autre que des types de pointeurs particuliers.
Tout d'abord, Il faut déclarer la classe TMenuItemPublic de telle façon quelle dérive exactement du même objet que TMenuItem, en l'occurence de TComponent.
Vous remarquerez, que l'on y retrouve exactement les mêmes variables et exactement dans le même ordre que dans la classe TMenuItem, à la différence que ces variables sont toutes déclarées publiques.
Ainsi, on obtient une classe ayant la même structure en mémoire que la classe TMenuItem, ce qui nous autorise alors à transtyper un TMenuItem en TMenuItemPublic et nous permet alors d'accéder à toutes les variables privées de TMenuItem comme si elles avaient été déclarées Public !
Code delphi : | Sélectionner tout |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 | type TMenuItemPublic=class(TComponent) public FCaption: string; FHandle: HMENU; FChecked: Boolean; FEnabled: Boolean; FDefault: Boolean; FAutoHotkeys: TMenuItemAutoFlag; FAutoLineReduction: TMenuItemAutoFlag; FRadioItem: Boolean; FVisible: Boolean; FGroupIndex: Byte; FImageIndex: TImageIndex; FActionLink: TMenuActionLink; FBreak: TMenuBreak; FBitmap: TBitmap; FCommand: Word; FHelpContext: THelpContext; FHint: string; FItems: TList; FShortCut: TShortCut; FParent: TMenuItem; FMerged: TMenuItem; FMergedWith: TMenuItem; FMenu: TMenu; FStreamedRebuild: Boolean; FImageChangeLink: TChangeLink; FSubMenuImages: TCustomImageList; FOnChange: TMenuChangeEvent; FOnClick: TNotifyEvent; FOnDrawItem: TMenuDrawItemEvent; FOnAdvancedDrawItem: TAdvancedMenuDrawItemEvent; FOnMeasureItem: TMenuMeasureItemEvent; FAutoCheck: Boolean; end; |
Code delphi : | Sélectionner tout |
1 2 3 4 5 6 | var UnMenuItem:TMenuItem; begin .... TMenuItemPublic(UnMenuItem).FImageIndex:=NewIndex; .... end; |
Certains constructeurs de classe peuvent lors de leur exécution générer une exception. Dans ce cas son destructeur est appelé automatiquement.
Cette situation étant gérée le plus souvent par le bloc suivant :
Code delphi : | Sélectionner tout |
1 2 3 4 5 6 7 8 9 10 11 | Var MonObjet : TUneClasse; ParametreUn : String; begin Try MonObjet:=TUneClasse.Create(ParametreUn); ...Traitement... Finally FreeAndNil(MonObjet); end; end; |
La modification de l'instance concernée dans le destructeur de sa classe n'est pas possible.
Pour éviter cette possible violation d'accés il faut tout simplement initialiser la variable à NIL AVANT l'appel du
constructeur :
Code delphi : | Sélectionner tout |
1 2 3 4 5 6 7 8 9 10 11 12 | Var MonObjet : TUneClasse; ParametreUn : String; begin Try MonObjet:=Nil; MonObjet:=TUneClasse.Create(ParametreUn); ...Traitement... finally FreeAndNil(MonObjet); end; end; |
Code delphi : | Sélectionner tout |
1 2 3 4 5 6 7 8 9 10 11 12 | Class procedure TUneClasse.CreateAndNilIfExcept(Var AObjet:TUneClasse; ParametreUn:String); begin try AObjet:=TUneClasse.Create(ParametreUn); except On Exception do begin AObjet:=Nil; //Evite la violation d'accés Raise; // On redéclenche l'exception end; end; end; |
Code delphi : | Sélectionner tout |
1 2 3 4 5 6 7 8 9 10 11 | Var MonObjet : TUneClasse; ParametreUn : String; begin Try TUneClasse.CreateAndNilIfExcept(MonObjet,ParametreUn); ...Traitement... finally FreeAndNil(MonObjet); end; end; |
L'utilisation de l'instruction case ... of en Delphi, quoique pratique dans la plupart des cas de programmation peut parfois se révéler inappropriée lorsque le nombre de branchements devient trop grand :
- Lourdeur du code
- Vitesse d'exécution désastreuse.
Ce cas peut parfois être rencontré lorsque l'on écrit par exemple :
- un émulateur : on effectue une action particulière différente pour chaque instruction du microprocesseur.
Dans ce cas, la vitesse d'exécution est primordiale. - un jeu d'aventure :
- Chaque case d'une carte peut être un type de rencontre différente.
- Chaque objet possédé par le héros possède un effet différent lorsqu'il est utilisé, chaque arme une attaque particulière, chaque sortilège déclenche un effet particulier.
Dans ce cas c'est la clarté du code qui permettra le design d'un univers riche en contenu et rebondissements.
Par exemple, ce type de code…
Code delphi : | Sélectionner tout |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | procedure TForm1.UneActionADeterminer(Parametre:integer); begin ... case Parametre of 0:MaProcedure0; 1:MaProcedure1; 2:MaProcedure2; 3:MaProcedure3; 4:MaProcedure4; 5:MaProcedure5; ..... 203:MaProcedure203; //etc... end; ... end; |
Code delphi : | Sélectionner tout |
1 2 3 4 5 6 | procedure TForm1.UneActionADeterminer(Parametre:integer); begin ... MesProcedures[Parametre]; ... end; |
Pour aboutir à ce résultat, nous devons :
1) Définir un type de variable pouvant contenir un type générique de procédure ou de fonction avec ou sans paramètre (Variables procédurales) :
Il s'agit de définir ici un type de variable un peu particulière destiné à recevoir une procédure ou une fonction. Ce genre de variable s'appelle une variable procédurale. La syntaxe pour déclarer ce type de variable est la suivante :
Code delphi : | Sélectionner tout |
<Nom du Type> = <procedure ou function>[(<paramètres>)[:<type de resultat>]] [of object;]
Ainsi aurons nous ce genre de déclarations pour des :
- Variables de type procedure : avec paramètres (c'est ce genre de déclaration qui est utilisé pour définir des variables de type évènement comme TNotifyEvent, TDrawItemEvent, etc..) :
Code delphi : | Sélectionner tout |
1 2 | type TProcedureAvecParametres=procedure(a,b:integer;s:string) of object; |
- sans paramètre :
Code delphi : | Sélectionner tout |
1 2 | type TProcedureSimple=procedure of object; |
Code delphi : | Sélectionner tout |
1 2 | type TOperationArithmetique=function(a,b:real):real of object; |
- sans paramètre :
Code delphi : | Sélectionner tout |
1 2 | type TTraiterUnCodeErreur=function:boolean of object; |
L'exemple suivant concerne des fonctions avec paramètres, définies dans une classe (notez le " of object")
Code delphi : | Sélectionner tout |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | type TOperationArithmetique=function(a,b:real):real of object; TForm1 = class(TForm) ... private { Déclarations privées } public { Déclarations publiques } ... TableauDeFonctions:array[0..3] of TOperationArithmetique; function Addition(a,b:real):real; function Multiplication(a,b:real):real; function Division(a,b:real):real; function Soustraction(a,b:real):real; ... end; ... implementation ... function TForm1.Addition(a,b:real):real; begin result:=a+b; end; function TForm1.Multiplication(a,b:real):real; begin result:=a*b; end; |
Code delphi : | Sélectionner tout |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 | type TOperationArithmetique=function(a,b:real):real; TForm1 = class(TForm) ... private { Déclarations privées } public { Déclarations publiques } ... TableauDeFonctions:array[0..3] of TOperationArithmetique; ... end; function Addition(a,b:real):real; function Multiplication(a,b:real):real; function Division(a,b:real):real; function Soustraction(a,b:real):real; ... implementation ... function Addition(a,b:real):real; begin result:=a+b; end; function Multiplication(a,b:real):real; begin result:=a*b; end; |
Dans cette partie de code, nous allons simplement remplir notre tableau aussi simplement que nous le ferions avec des variables simples comme des entiers ou des chaînes de caractères :
Code delphi : | Sélectionner tout |
1 2 3 4 5 6 7 | procedure TForm1.InitialisationDuTableau begin TableauDeFonctions[0]:=Addition; TableauDeFonctions[1]:=Multiplication; ... //etc. end; |
- Avec des procédures sans paramètre :
Code delphi : | Sélectionner tout |
1 2 3 4 5 6 7 8 9 | procedure TForm1.UneActionADeterminer(Parametre:integer); begin //si Parametre est compris entre la borne inférieure et la borne supérieure du tableau de procédures if ((Parametre>=low(TableauDeProcedures)) and (Parametre<=high(TableauDeProcedures)) then //si l'entrée du tableau contient effectivement quelque chose (une procédure) if Assigned(TableauDeProcedures[Parametre]) then //exécution de la procédure ad hoc TableauDeProcedures[Parametre]; end; |
- Avec des fonctions acceptant des paramètres :
Code delphi : | Sélectionner tout |
1 2 3 4 5 6 | procedure TForm1.Calcule(OperationChoisie:integer;NombreA,NombreB:real;var resultat:real); begin if ((OperationChoisie>=0) and (OperationChoisie<=3)) then if Assigned(TableauDeFonctions[OperationChoisie]) then resultat:=TableauDeFonctions[OperationChoisie](NombreA,NombreB); end; |
Dans un nouveau projet, nous avons placé un TButton sur notre fiche.
Code delphi : | Sélectionner tout |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 | unit Unit1; interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls; type //Définition d'un type de procedure simple TProcedureAffichage=procedure of object; TForm1 = class(TForm) Button1: TButton; procedure Button1Click(Sender: TObject); procedure FormCreate(Sender: TObject); private { Déclarations privées } public { Déclarations publiques } TableauDeProcedures:array[0..2] of TProcedureAffichage; procedure AffichageShowMessage; procedure AffichageDansLaFiche; procedure AffichageBarreDeTitre; end; var Form1: TForm1; implementation {$R *.dfm} { TForm1 } procedure TForm1.AffichageShowMessage; begin showmessage('Procedure n°1'); end; procedure TForm1.AffichageDansLaFiche; begin canvas.font.Color:=random($ffffff); canvas.TextOut(10,10,'Procédure n°2'); end; procedure TForm1.AffichageBarreDeTitre; var i:integer; oldcaption:string; begin oldcaption:=Caption; for i:=0 to 9 do begin caption:='Procedure n°3'; sleep(200); caption:=''; sleep(100); end; caption:=oldcaption; end; //Événement OnClick du Bouton procedure TForm1.Button1Click(Sender: TObject); var i:integer; begin //choix aléatoire d'une procedure d'affichage i:=Random(3); if assigned(TableauDeProcedures<i>) then TableauDeProcedures<i>; end; //Événement OnCreate de la fiche procedure TForm1.FormCreate(Sender: TObject); begin Randomize; TableauDeProcedures[0]:=AffichageShowMessage; TableauDeProcedures[1]:=AffichageDansLaFiche; TableauDeProcedures[2]:=AffichageBarreDeTitre; end; end. |
C'est un grand débat qui dépasse le cadre et l'objectif de cette simple QR. En effet, il est tout de même étonnant de constater que bien que ce type se gère comme une variable classique, c'est un type de variable à laquelle s'applique le @, le nil et le Assigned() des pointeurs. Si vous souhaitez approfondir le sujet, nous vous suggérons la lecture de la documentation de Delphi aux rubriques suivantes :
- Assigned,fonction
- Types procédure
- Types procédure dans les instructions et les expressions
Quand vous importez une fonction en langage C qui prend un nombre variable de paramètres, utilisez la directive varargs.
Par exemple :
Code delphi : | Sélectionner tout |
function printf(Format: PChar): Integer; cdecl; varargs;
Vous trouverez des informations complémentaires dans l'aide de Delphi à la rubrique nommée : Déclarations externes
On souhaite parfois connaître le GUID d'une interface Delphi malheureusement il n'existe pas de méthode dans la VCL pour ce faire. Heureusement les RTTI nous offrent une solution au travers de l'appel à la fonction Typeinfo.
Code delphi : | Sélectionner tout |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | //Programme de type Console uses Typinfo; var DonneeDeType : PTypeData; begin //Récupére les information de RTTI d'une interface DonneeDeType := GetTypeData(typeinfo(ISWbemObject)); //Vérifie si l'interface posséde bien un GUID if ifHasGuid in DonneeDeType^.IntfFlags then Writeln(GUIDToString(DonneeDeType^.Guid)); Writeln(DonneeDeType^.IntfUnit); // Nom de l'unité où est déclarée l'interface readln; end. |
La seule différence réside dans le fait que l'appel est effectué à la compilation et que l'on doit lui passer un nom de type et non pas un nom d'instance.
Pour une classe l'appel de GetTypeData(TMonObjet.ClassInfo) est identique à GetTypeData(TypeInfo(TMonObjet)).
Pour vider le buffer du clavier on utilise l'API PeekMessage qui opère sur la file d'attente des messages du thread courant.
Code delphi : | Sélectionner tout |
1 2 3 4 5 6 7 | procedure VideBufferClavier; var Msg: TMsg; begin while PeekMessage(Msg, 0, WM_KEYFIRST, WM_KEYLAST, PM_REMOVE or PM_NOYIELD) do; end; |
Voici une fonction permettant d'obtenir le nombre de bits "allumés" dans le contenu d'une variable de n'importe quel type.
Code delphi : | Sélectionner tout |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | function card(var UneVariable; size: integer): integer; const // table des bits actifs dans un groupe de 4 bits (exemple : 9=8+1-->2 bits) NBits: array[0..15] of integer=( 0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4); var t: TbyteArray absolute UneVariable; //table pointant sur le premier octet de la variable //dont on souhaite connaître le nombre de //bits allumés j: integer; PoidsFort:byte; PoidsFaible:byte; begin result:=0; //Pour chaque octet du tableau for j := 0 to size - 1 do begin //chaque octet est coupé en deux groupe de 4 bits PoidsFort:=t[j] shr 4 PoidsFaible:=t[j] and $F; //On dénombre pour chaque groupe de 4 bits le nombre de bits allumés inc(result, NBits[PoidsFort] and $F] + NBits[PoidsFaible]); end; end; |
Code delphi : | Sélectionner tout |
1 2 3 4 5 6 7 8 9 10 11 12 13 | var MonEnsemble:Set of Byte; NomBreDElements:integer; begin ... MonEnsemble:=[128,25,96,36,200]; //on passse à notre fonction notre variable //et aussi sa taille en octets, obtenue grace à la fonction SizeOf() ) NomBreDElements:=Card(MonEnsemble,SizeOf(MonEnsemble)); ShowMessage(IntToStr(NomBreDElements)); //affiche "5" ... end; |
Code delphi : | Sélectionner tout |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | var UnEntier:integer; UnBoolean:Boolean; UnReel:double; UneClasse:TForm; UneChaine:string; UnTableau:array[] of ... //QuelqueChose begin ... NomBreDElements:=Card(UnEntier,SizeOf(UnEntier)); NomBreDElements:=Card(UnBoolean,SizeOf(UnBoolean)); NomBreDElements:=Card(UnReel,SizeOf(UnReel)); NomBreDElements:=Card(UneClasse,SizeOf(UneClasse)); NomBreDElements:=Card(UneChaine,SizeOf(UneChaine)); NomBreDElements:=Card(UnTableau:,SizeOf(UnTableau)); ... end; |
Cet outil mathématique peut être intéressant pour l'élaboration d'un jeu de cartes (par exemple), qui permet de déterminer facilement le nombre de cartes restantes dans la main d'un joueur, la pioche, etc.
À propos de la directive absolute :
cette directive est un relicat du Turbo Pascal, elle permet de créer une nouvelle variable placée à la même adresse qu'une variable existante. Pour cela, il s'agit de placer le mot absolute après le nom de type, dans la déclaration de la nouvelle variable, en le faisant suivre du nom d'une variable existante (déclarée précédemment).
On peut voir cette directive comme un transtypage de variable.
On peut souhaiter quelquefois qu'un traitement au sein d'une même fonction puissent renvoyer un type de donnée différents selon les cas.
Vous trouverez une solution dans le tutoriel cité.
Quelques fois, on peut être amené à manipuler des DLL écrites en C ou C++ utilisant un nombre variable de paramètres. Dans ce cas, la solution se trouve ici.
En revanche, réaliser en natif une méthode utilisant un nombre variable de paramètres nécessite une autre approche. Dans un premier temps, on pourrait penser qu'un tableau de pointeurs suffirait mais au moins 2 questions se posent, à savoir comment :
- connaître le nombre de paramètres dans un tableau de pointeurs ?
- retrouver le type de chaque paramètre ?
Les traitements à mettre en place seraient assez délicats. Le langage Delphi nous proposant le type Variant, pouvant encapsuler n'importe quel type Delphi, voyons comment procéder sans trop d'efforts dans le tutoriel cité ci-dessous.
Il existe dans Delphi un raccourci pour créer un GUID lors de l'écriture du code : Ctrl+Shift+G…
Mais comment faire lorsque l'on veut créer ce GUID dynamiquement pendant l'exécution d'un programme ?
Il suffit d'utiliser la fonction CoCreateGuid. On peut donc créer une fonction qui permet de retourner un GUID facilement:
Code delphi : | Sélectionner tout |
1 2 3 4 5 6 7 8 | function CreateGuid: string; var GUID: TGUID; begin Result := ''; if CoCreateGuid(GUID) = S_OK then Result := GUIDToString(GUID); end; |
Note :
À partir de BDS 2006 l'unité comObj propose une fonction identique :
Code delphi : | Sélectionner tout |
function CreateClassID: string;
Code delphi : | Sélectionner tout |
function CreateGUID(out Guid: TGUID): HResult;
Un If immédiat permet de réaliser une affectation conditionnelle. Il s'agit suivant la valeur de vérité d'une condition (vraie ou fausse) d'affecter une valeur à une variable donnée. Ceci peut se faire soit à travers une construction if...then...else (trois instructions) ou avec l'opérateur ?..: pour ceux qui connaissent le langage C.
A partir de Delphi 6, nous pouvons utiliser l'instruction IfThen dont une signature est :
Code delphi : | Sélectionner tout |
function IfThen (AValue : Boolean; const ATrue : Integer; const AFalse : Integer = 0) : Integer; overload;
Pour les versions antérieures à la version 6, le code suivant permet d'implémenter un équivalent de IfThen :
Code delphi : | Sélectionner tout |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 | Interface { Immediate If } { IIF renvoyant un entier } function IIF(ACondition: boolean; ATruePart, AFalsePart: integer): integer; overload; { IIF renvoyant un extended } function IIF(ACondition: boolean; ATruePart, AFalsePart: Extended): Extended; overload; { IIF renvoyant une string } function IIF(ACondition: boolean; ATruePart, AFalsePart: string): string; overload; { IIF renvoyant un objet } function IIF(ACondition: boolean; ATruePart, AFalsePart: TObject): TObject; overload; Implementation function IIF(ACondition: boolean; ATruePart, AFalsePart: integer): integer; { Immediate IF pour les entiers } begin if ACondition then Result := ATruePart else Result := AFalsePart; end; function IIF(ACondition: boolean; ATruePart, AFalsePart: Extended): Extended; { Immediate IF pour les flottant } begin if ACondition then Result := ATruePart else Result := AFalsePart; end; function IIF(ACondition: boolean; ATruePart, AFalsePart: string): string; { Immediate IF pour les chaînes } begin if ACondition then Result := ATruePart else Result := AFalsePart; end; |
Code delphi : | Sélectionner tout |
1 2 3 4 5 6 7 | function IIF(ACondition: boolean; ATruePart, AFalsePart: TObject): TObject; { Immediate IF pour les objets } begin if ACondition then Result := ATruePart else Result := AFalsePart; end; |
Utilisation des fonctions :
Code delphi : | Sélectionner tout |
1 2 3 4 5 6 7 8 | procedure TMyObject.TestIIF; var idx: integer; MyString: String; begin idx := 10; { Par exemple } MyString := IIF(idx=0,'Zero','Différent de Zero'); end; |
Code delphi : | Sélectionner tout |
1 2 3 4 5 6 7 8 9 10 | procedure TMyObject.TestIIF; var idx: integer; MyString: String; begin idx := 10; { Par exemple } if idx=0 then MyString = 'Zero' else MyString = 'Différent de Zéro'; end; |
Il est possible avec Delphi de calculer la distance entre deux points GPS, c'est-à-dire la distance à vol d'oiseau. Pour cela, nous allons travailler avec des degrés décimaux (DD). Si vous travaillez en DMS (degrés, minutes, secondes), il faudra convertir au préalable les données.
Il s'agit d'un calcul complexe, car il faut tenir compte de la rotondité de la terre. Pour cela, il faut utiliser une formule de trigonométrie sphérique. Les degrés passent en radians avec DegToRad de l'unité Math. On applique la formule et on passe la valeur qui est en miles puis en kilomètres (1 mile = 1.609344 km).
Code delphi : | Sélectionner tout |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 | procedure TForm2.Button1Click(Sender: TObject); var eLatitude1, eLatitude2 : Extended; eLongitude1, eLongitude2 : Extended; rLatitude1, rLatitude2 : Extended; rTheta : Extended; eDistance : Extended; begin // coordonnées GPS de la ville de Dijon (21000) eLatitude1 := 47.366667; eLongitude1 := 5.033333; // coordonnées GPS de la ville d'Aubagne (13400) eLatitude2 := 43.283333; eLongitude2 := 5.566667; // conversion des degrés en radians rLatitude1 := DegToRad(eLatitude1); rLatitude2 := DegToRad(eLatitude2); // différence entre les deux longitudes rTheta := DegToRad(eLongitude1 - eLongitude2); // trigonométrie circulaire eDistance := Sin(rLatitude1) * Sin(rLatitude2) + Cos(rLatitude1) * Cos(rLatitude2) * Cos(rTheta); eDistance := ArcCos(eDistance); eDistance := eDistance * 180 / Pi; // 60 minutes par degré, et passage de mile en mile nautique eDistance := eDistance * 60 * 1.1515; // passage des miles en kilomètres eDistance := eDistance * 1.609344; // affichage de la valeur ShowMessage(eDistance.ToString); end; |
Dans certaines situations, on peut vouloir adapter certains composants visuels pour refléter leur état.
Par exemple, on peut vouloir désactiver ou activer des composants, changer leur couleur de fond, vider leur contenu, etc.
Pour modifier une ou plusieurs propriétés d'un ensemble de composants visuels, on peut le faire individuellement pour chacun.
Mais on peut utiliser aussi le RTTI.
Voilà une petite classe sans prétention qui permet ce genre de chose :
Code Delphi : | Sélectionner tout |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 | unit UStateManager; interface uses System.SysUtils, System.Classes, System.StrUtils, System.Rtti, Winapi.Windows, Vcl.Controls; type TStateManager = class(TObject) public /// <summary> /// Permet de définir à l'avance le Owner sur lequel sera applliqué la traitement /// </summary> class var TheOwner: TWinControl; /// <summary> /// Permet de modifier plusieurs TControls sur un objet visuel qui en contient plusieurs /// </summary> /// <param name="OwnerCtrls"> /// Composant qui contient les éléments à traiter /// </param> /// <param name="PropNames"> /// Les noms des propriétés à traiter /// </param> /// <param name="Values"> /// Les valeurs pour chaque propriété de PropNames /// </param> class procedure ChangeState(OwnerCtrls: TWinControl; PropNames: array of string; Values: array of const); class procedure EnableCtrls(OwnerCtrls: TWinControl; State: Boolean; EnabledOrReaddOnly: Boolean = False); end; implementation { TStateManager } procedure AssignValue(Control: TControl; Prop: TRttiProperty; Valeur: TVarRec); begin case Valeur.VType of vtInteger: Prop.SetValue(Control, Valeur.VInteger); vtBoolean: Prop.SetValue(Control, Valeur.VBoolean); vtChar: Prop.SetValue(Control, Valeur.VChar); vtExtended: Prop.SetValue(Control, Valeur.VExtended^); vtString: Prop.SetValue(Control, Valeur.VString^); vtWideChar: Prop.SetValue(Control, Valeur.VWideChar); vtAnsiString: Prop.SetValue(Control, AnsiString(Valeur.VAnsiString^)); vtWideString: Prop.SetValue(Control, WideString(Valeur.VWideString^)); vtInt64: Prop.SetValue(Control, Int64(Valeur.VInt64)); vtUnicodeString: Prop.SetValue(Control, UnicodeString(Valeur.VUnicodeString)); end; end; class procedure TStateManager.ChangeState(OwnerCtrls: TWinControl; PropNames: array of string; Values: array of const); var I, J: Integer; Ctx : TRttiContext; Typ : TRttiType; Prop: TRttiProperty; P : Pointer; begin if not Assigned(OwnerCtrls) then if Assigned(TStateManager.TheOwner) then OwnerCtrls := TStateManager.TheOwner else raise Exception.Create('No defined Owner'); for I := 0 to Pred(OwnerCtrls.ControlCount) do begin P := OwnerCtrls.Controls[I].ClassInfo; if Assigned(P) then begin Typ := Ctx.GetType(P); for J := Low(PropNames) to High(PropNames) do begin Prop := Typ.GetProperty(PropNames[J]); if Assigned(Prop) then AssignValue(OwnerCtrls.Controls[I], Prop, Values[J]); end; end; end; end; class procedure TStateManager.EnableCtrls(OwnerCtrls: TWinControl; State: Boolean; EnabledOrReaddOnly: Boolean); var I : Integer; Ctx : TRttiContext; Typ : TRttiType; Prop: TRttiProperty; P : Pointer; Vl : TVarRec; begin if not Assigned(OwnerCtrls) then if Assigned(TStateManager.TheOwner) then OwnerCtrls := TStateManager.TheOwner else raise Exception.Create('No defined Owner'); Vl.VType := vtBoolean; Vl.VBoolean := State; for I := 0 to Pred(OwnerCtrls.ControlCount) do begin P := OwnerCtrls.Controls[I].ClassInfo; if Assigned(P) then begin Typ := Ctx.GetType(P); Prop := Typ.GetProperty('Enabled'); if Assigned(Prop) then AssignValue(OwnerCtrls.Controls[I], Prop, Vl) else begin if EnabledOrReaddOnly then begin Prop := Typ.GetProperty('ReadOnly'); if Assigned(Prop) then AssignValue(OwnerCtrls.Controls[I], Prop, Vl) end; end; end; Prop := nil; end; end; end. |
Et un exemple d'utilisation :
Code Delphi : | Sélectionner tout |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 | unit Unit5; interface uses Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, System.Rtti, System.TypInfo, Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, Vcl.Mask, Vcl.Wwdbedit, Vcl.ExtCtrls, Vcl.Wwdotdot, Vcl.Wwdbcomb; type TForm5 = class(TForm) Edit1: TEdit; Label1: TLabel; Button1: TButton; RadioButton1: TRadioButton; Panel1: TPanel; wwDBEdit1: TwwDBEdit; Memo1: TMemo; wwDBComboBox1: TwwDBComboBox; Panel2: TPanel; Memo2: TMemo; procedure FormDblClick(Sender: TObject); end; var Form5: TForm5; implementation {$R *.dfm} uses UStateManager; procedure TForm5.FormDblClick(Sender: TObject); begin TStateManager.TheOwner := Panel2; TStateManager.ChangeState(nil, ['color', 'readonly', 'Text'], [clYellow, False, 'Hello']); // or ... TStateManager.ChangeState(Panel2, ['color', 'readonly', 'Text'], [clYellow, False, 'Hello']); TStateManager.EnableCtrls(Panel2, False, True); end; end. |
Proposer une nouvelle réponse sur la FAQ
Ce n'est pas l'endroit pour poser des questions, allez plutôt sur le forum de la rubrique pour çaLes sources présentées sur cette page sont libres de droits et vous pouvez les utiliser à votre convenance. Par contre, la page de présentation constitue une œuvre intellectuelle protégée par les droits d'auteur. Copyright © 2024 Developpez Developpez LLC. Tous droits réservés Developpez LLC. Aucune reproduction, même partielle, ne peut être faite de ce site et de l'ensemble de son contenu : textes, documents et images sans l'autorisation expresse de Developpez LLC. Sinon vous encourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts.