jihelb
Messages postés49Date d'inscriptionlundi 27 janvier 2003StatutMembreDernière intervention24 mars 2017
-
29 avril 2008 à 19:15
Cirec
Messages postés3833Date d'inscriptionvendredi 23 juillet 2004StatutModérateurDernière intervention18 septembre 2022
-
1 mai 2008 à 21:28
Pour un composant (dérivé de TCustomPanel), j'avais besoin (entre autres) d'une TListBox. (Programme minimal ci-dessous)
MALHEUREUSEMENT, le composant étant déposé sur une forme, dans l'inspecteur d'objet, le clic sur le bouton "Items" rend le Message "Impossible d'affecter TListBoxStrings à TCPListBox" au lieu d'ouvrir la boite de saisie. De plus, impossible de déposer un autre composant sur la forme.
Quelqu'un a-t-il une idée pour me dépanner ? JE DESESPERE ...MERCI.
//====================================
unit U_TCPListBox;
interface//====================================
uses ExtCtrls,StdCtrls,Classes;
type
TCPListBox = class(TCustomPanel)
LB: TListBox;
private
procedure SetLBItems(value: TStrings);
function GetLBItems: TStrings;
protected
public
constructor Create(AOwner: TComponent); override;
published
property LBItems: TStrings read GetLBItems write SetLBItems;
end;
procedure Register;
implementation//==================================
constructor TCPListBox.Create(AOwner: TComponent);
begin
inherited Create(AOwner);
LB:= TListBox.Create(Self);
with LB do begin
Parent:= Self;
end;
end;//-----------------------------------------
procedure TCPListBox.SetLBItems(value: TStrings);
begin
LB.Items.Assign(value);
end;//-----------------------------------------
function TCPListBox.GetLBItems: TStrings;
begin
Result.Assign(LB.Items);
end;//-----------------------------------------
procedure Register;
begin
RegisterComponents('Exemples', [TCPListBox]);
end;//-----------------------------------------
end.
type
TCPListBox = class(TCustomPanel)
private
fListBox: TListBox;
procedure SetListBox(value: TListBox);
public
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
published
property ListBox: TListBox read fListBox write SetListBox;
{ on aura accés a toutes les propriétés et evenements de fListBox
directement dans l'inspecteur d'objet, notement la propriété Items :) }
end;
procedure Register;
implementation
procedure Register;
begin
RegisterComponents('Exemples', [TCPListBox]);
end;
f0xi
Messages postés4205Date d'inscriptionsamedi 16 octobre 2004StatutModérateurDernière intervention12 mars 202235 29 avril 2008 à 21:14
Derniers conseils :
- Une variable ou objet appartenant a un objet maitre doit figurer dans la zone Private. on evite dans la mesure du possible de fournir de tels elements dans d'autres zone (Public/Published/Protected) en dehors d'un accés par le mots reservé "Property".
- Les conventions d'ecriture de code veulent que les identifiants des elements privés, ci-dessus cités, prennent un "f" au debut de leur noms (sauf bien sur les fonction et procedure de definition/recuperation Set/Get, les messages systemes etc)
type
TCPListBox = class(TCustomPanel)
private
procedure SetLBItems(value: TStrings);
function GetLBItems: TStrings;
protected
LB: TListBox; //1
public
constructor Create(AOwner: TComponent); override;
published
property LBItems: TStrings read GetLBItems write SetLBItems;
end;
procedure Register;
implementation
constructor TCPListBox.Create(AOwner: TComponent);
begin
inherited Create(AOwner);
Parent := AOwner as TWinControl; //2
LB := TListBox.Create(AOwner);
LB.Parent := Self; //3
end;
procedure TCPListBox.SetLBItems(value: TStrings);
begin
LB.Items.Assign(value);
end;
function TCPListBox.GetLBItems: TStrings;
begin
//Result.Assign(LB.Items);
Result := lb.Items; //4
end;
procedure Register;
begin
RegisterComponents('Exemples', [TCPListBox]);
end;
end.
Plusieurs erreurs :
0- inclusion de l'unité Controls car nous avons besoin d'affecter la propriété Parent de ton composant (de type TWinControl).
1- Le champ LB ne doit pas être déclaré avec la propriété Public par défaut car il devient alors accessible depuis l'extérieur sans passer par les accesseurs en lecture comme en écriture. Ici, je l'ai mis en portée protected. Au pire, imagine que le code, quelque part ailleurs détruire ta liste. Comment réagira alors le code de ton composant ?
2- Tout composant visuel a besoin d'avoir sa propriété Parent affectée pour que ses coordonnées soient relatives à son parent et non à la fiche.
3- même remarque que ci-dessus.
4- Là, l'erreur était manifeste et le compilateur renvoyait un message d'avertissement assez explicite :[Avertissement] U_TCPListBox.pas(39): La valeur de retour de la fonction 'TCPListBox.GetLBItems' peut être indéfinie
En effet, si Result est du type TStrings, tu ne créais pas d'objet de type TStrings, donc tu appelais la méthode Assign d'un objet égal à NIL. Mais pourquoi créer un objet liste de chaînes puisque tu disposes déjà de cette structure (membre LB).
Pense à activer les contrôles du compilateur (Projet/Options/Messages du compilateur).
Voilà pour les commentaires et remarques. En espérant que cela t'aidera à y voir plus clair à l'avenir.
Bonne prog' et...
May Delphi be with you !
<hr color="#008000" />Pensez à cliquer sur Réponse acceptée lorsque la réponse vous convient.
Vous n’avez pas trouvé la réponse que vous recherchez ?
cs_Delphiprog
Messages postés4297Date d'inscriptionsamedi 19 janvier 2002StatutMembreDernière intervention 9 janvier 201332 30 avril 2008 à 16:56
@cirec : tu as tout à fait raison de faire remarquer cette lacune. Et je ne me suis jamais posé la question, faisant toujours bien attention de transmettre une référence valide en paramètre. J'ai donc remonté la hiérarchie des composants jusqu'à TControl et voici ce que nous dit son constructeur :
constructor TComponent.Create(AOwner: TComponent);
begin
FComponentStyle := [csInheritable];
if AOwner <> nil then AOwner.InsertComponent(Self);
end;
Eh bien, si AOwner vaut nil, il ne se passe rien. Et pourtant, en créant un composant dynamiquement avec nil comme argument, le composant visuel apparait bien sur la fiche.Seul bémol, comme la fiche ne possède pas ce composant créé dynamiquement, il faudra procéder à sa destruction manuellement. Un peu comme on le fait pour les composants non visuels, genre TStringList.
"Le Owner est celui qui se charge de détruire le composant à la fin ce
n'est pas forcément le parent (sauf quand il est déposer depuis l'IDE).
Autrement dit le Owner peut être différent du parent"
Seul le propriétaire est responsable de la destruction des composants qu'il détient. Le parent n'est là que pour permettre un positionnement correct d'un composant visuel (on ne positionne jamais les composants en coordonnées absolues d'écran, heureusement ).
Est-ce mieux ainsi ?
May Delphi be with you !
<hr color="#008000" />Pensez à cliquer sur Réponse acceptée lorsque la réponse vous convient.
type
TCPListBox = class(TCustomPanel)
private
fListBox: TListBox;
fStrings: TStrings;
procedure SetStrings(value: TStrings);
public
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
published
property Items: TStrings read fStrings write SetStrings;
end;
procedure Register;
implementation
procedure Register;
begin
RegisterComponents('Exemples', [TCPListBox]);
end;
constructor TCPListBox.Create(AOwner: TComponent);
begin
inherited Create(AOwner);
fListBox := TListBox.Create(Self);
fListBox.Parent := Self;
fListBox.Align := alClient;
fStrings := fListBox.Items; // on passe la reference de fListBox.Items
// a fStrings
end;
destructor TCPListBox.Destroy;
begin
fListBox.Free;
inherited Destroy;
end;
procedure TCPListBox.SetStrings(value: TStrings);
begin
fStrings.Assign(value); // On travail directement avec fStrings
// et plus fListBox
end;
jihelb
Messages postés49Date d'inscriptionlundi 27 janvier 2003StatutMembreDernière intervention24 mars 2017 30 avril 2008 à 12:42
Bonjour à vous qui m'avez dépanné,
Merci à Foxi pour tes réponses et le temps que tu y a passé.
L'absence du Destructeur était du au fait que je n'avais mis que ce qui riquait d'être en rapport avec mon problème.
Par contre, même avec de l'humour, je trouve ta petite animation pas trés gentille.
Merci à DelphiProg pour ta réponse trés concise et trés explicite.
Pour le point //1 je pensais que c'était correct. Certainement que plusieurs de mes problèmes proviennent de cette erreur.
Pour le point //2, je pensais (bêtement, sans y avoir vraiment réfléchi ni vérifié) que celà était fait par inherited Create()
Et bizarrement j'avais bien mis cette ligne pour le point //3 ? (je suis incohérent avec //2)
Point //4 c'est pourtant simple !!! Merci
Je vais pouvoir finir mon composant et je le publerai sur le site (bien qu'en cherchant à me dépanner, avant de poser ma question, je suis tombé sur des programmes qui y ressemblent un peu ... mais ne sont pas des composants (et c'est là que les difficultés apparaissent)
Cirec
Messages postés3833Date d'inscriptionvendredi 23 juillet 2004StatutModérateurDernière intervention18 septembre 202250 30 avril 2008 à 14:08
@Delphiprog:
pour le point N°2 que se passe t'il si je crée le composant dynamiquement avec Nil comme AOwner ?
Normalement c'est l'IDE qui se charge de lui affecter un parent au moment ou l'on dépose le composant sur la fiche. Si il est créer manuellement il faut lui affecter un parent manuellement aussi.
Le Owner est celui qui se charge de détruire le composant à la fin ce n'est pas forcément le parent (sauf quand il est déposer depuis l'IDE).
Autrement dit le Owner peut être différent du parent
jihelb
Messages postés49Date d'inscriptionlundi 27 janvier 2003StatutMembreDernière intervention24 mars 2017 30 avril 2008 à 17:58
Je constate que ma question a finalement servie à présiser plus clairement dans notre pensée des points qui nous apparaissent aller de soi et qui finalement ont besoin d'être réfléchis "consciemment".
Merci à tous
Cirec
Messages postés3833Date d'inscriptionvendredi 23 juillet 2004StatutModérateurDernière intervention18 septembre 202250 30 avril 2008 à 20:01
Bon ... je m'aperçois que je me suis mal exprimé ^^
tes explications sont juste et heureusement qu'il se comporte comme ça
mais c'est pas là ou je voulais en venir ...
ce que je voulais dire c'est que dans l'implémentation du constructeur la ligne :
Parent := AOwner as TWinControl; //2
est totalement inutile .... que tu passes au constructeur (Nil) ou (Form1), " en dynamique bien sur ", il faudra quand même définir le parent manuellement ... et si le composant est installé, et qu'il est déposé sur la fiche depuis la palette, le parent est affecté automatiquement par Delphi.
"... Et pourtant, en créant un composant dynamiquement avec nil comme argument, le composant visuel apparait bien sur la fiche. ..."
ben chez moi non essaye ce code et tu verras (D7 Perso)
Var CPListBox : TCPListBox;
Begin
CPListBox : = TCPListBox.Create(Form1);
With CPListBox Do
Begin
Parent := Self; // si tu
mets cette ligne en commentaire il ne s'affiche pas ^^ et avec Create(Nil) c'est pareil
Top : = 10;
Left := 10;
End ;
End;
Et pour le troisième point c'est exactement ce que je disais
Owner == Propriétaire
Par contre toi tu essaye d'affecter Owner au parent et c'est là que ça ne me convient pas puisque Owner peut être d'un autre type que TWinControl
Cirec
Messages postés3833Date d'inscriptionvendredi 23 juillet 2004StatutModérateurDernière intervention18 septembre 202250 1 mai 2008 à 21:28
On s'en fout royalement
...
c'était juste pour l'exemple comme le code était sorti du contexte ...
Self ici (dans ce bout de code) ne représente rien et donc pour éviter les confusions j'ai mis Form1 (qui me paraissait plus explicite)
Mais c'est pas ça le plus important ...
étonnant d'ailleurs que tu n'aies pas remarqué ça