2016-09-20 2 views
0

J'ai un formulaire et puis j'ai un objet 'TPageControl' (nommé 'MyPages') et un objet 'TButton' (nommé 'MyButton') placé dessus à temps de conception. Ensuite, j'ai une nouvelle classe appelée 'TTab' qui étend 'TTabSheet'. La classe 'TTab' a un objet 'TButton' comme l'une de ses variables membres comme ci-dessous.Comment passer un message d'un objet à un autre objet dans Free Pascal

class TTab = class(TTabSheet) 
private 
    m_btnCloseTab: TButton; 
end; 

Lorsque je clique sur le « MyButton », il créerait un nouvel objet « TTAB », init l'onglet (comme instancier le « m_btnCloseTab ») et l'ajouter à « MyPages » à Durée.

Procedure TForm1.MyButtonClick(Sender:TObject); 
var 
    newTab: TTab; 
    newCaption: AnsiString; 
begin 
    newCaption:= 'Tab' + IntToStr(count); //count is a global var 
    inc(count); 

    newTab:= TTab.Create(nil); 
    newTab.Init(newCaption); 
    newTab.Parent(MyPages); 
end; 

Voici à quoi ressemble la procédure TTab.Init (newCaption: AnsiString).

Procedure TTab.Init(newCaption: AnsiString); 
begin 
    Self.Caption:= newCaption; 
    m_btnCloseTab:= TButton.Create(nil); 
    with m_btnCloseTab do begin 
    Parent:= Self; 
    Left:= 10; 
    Top:= 10; 
    Caption:= 'Close Tab'; 
    Visible:= True; 
    OnClick:= @closeTab; 
    end; 
end; 

Cela ajoute un nouvel onglet. Le bouton de fermeture est également affiché sur chaque onglet. Comment puis-je cliquer sur le «m_btnCloseTab» sur chaque onglet pour fermer cet onglet particulier?

Si je définis un destructeur (en remplaçant le destructeur de la TTabSheet) pour TTab comme ci-dessous, je peux l'appeler de l'extérieur.

Destructor TTab.Destroy; 
begin 
    if m_btnCloseTab <> nil then begin 
    m_btnCloseTab.Destroy; 
    m_btnCloseTab:= nil; 
    end; 
    inherited; 
end; 

Mais je ne peux pas appeler le Destructeur depuis l'intérieur de l'onglet (enfin, vous pouvez). Si je le fais, je ne peux pas libérer l'objet m_btnCloseTab car cela donnerait une exception, car nous sommes toujours son gestionnaire d'événements. Si je ne le libère pas, l'onglet se ferme bien, mais la mémoire est divulguée (parce que nous n'avons pas libéré m_btnCloseTab). Je crois que je dois déclencher un événement pour que le destructeur puisse être appelé depuis l'extérieur de 'TTab'. Je ne sais pas comment le faire.

Toute aide serait appréciée.

Merci.

+0

Je ne suis pas sûr si je comprends tout correctement. Mais si vous créez m_btnCloseTab avec l'onglet comme propriétaire (mbtn_CloseTab: = TButton.Create (self), au lieu de ... Create (nil)), le bouton est automatiquement détruit avec l'onglet. En outre, TTab devrait avoir une méthode de notification qui est appelée chaque fois qu'un de ses enfants est détruit et que vous pouvez utiliser pour mettre mbtn_CloseTab à nil: Cela éviterait que Tab appelle le destructeur des boutons dans le cas où les boutons ont été détruits auparavant quelque raison. –

+0

Merci pour le commentaire (je pensais que plus personne n'utilisait Free Pascal) mais cela ne change rien au résultat. En utilisant create (nil), je dois libérer le m_btnCloseTab par moi-même dans le destructeur. En utilisant Create (Self), le destructeur de la classe TTab le fait automatiquement. Qu'est-ce qu'une méthode de notification? Comment puis-je en implémenter un? Ce que je pense est ce que j'ai besoin de faire, un peu comme une fonction de délégué en C#. Appréciez-le si vous pouvez me diriger vers un tutoriel ou une documentation sur la façon de le faire. –

+0

Vous avez tort. Visitez le forum Lazarus pour voir une communauté Pascal vivante. –

Répondre

0

Vous pouvez trouver des méthodes de notification partout dans les sources LCL (et bien sûr dans Delphi). Un exemple simple est un TLabeledEdit: c'est une sorte de "TEdit" qui contient un TLabel. Si Label est détruit, LabeledEdit en est averti car il doit mettre la référence à l'étiquette à zéro. Sinon, le destructeur de TLabeledEdit tentera de détruire à nouveau l'étiquette - BOOM. Ici, la méthode est comme celui-ci (collé à partir ExtCtrls):

procedure TCustomLabeledEdit.Notification(AComponent: TComponent; 
    Operation: TOperation); 
begin 
    inherited Notification(AComponent, Operation); 
    if (AComponent = FEditLabel) and (Operation = opRemove) then 
    FEditLabel := nil; 
end; 

Et vous pouvez voir ce que vous avez à faire dans votre cas:

procedure TTab.Notification(AComponent: TComponent; 
    Operation: TOperation); 
begin 
    inherited Notification(AComponent, Operation); 
    if (AComponent = m_BtnCloseTab) and (Operation = opRemove) then 
    m_BtnCloseTab := nil; 
end; 

S'il vous plaît noter que la notification est une méthode virtuelle et doit être déclaré avec l'attribut "override" dans la section protégée du composant.

+0

Il se bloque toujours lorsque Destroy est appelé à partir du gestionnaire d'événements onClick de m_BtnCloseTab. J'essaie de comprendre ce que fait exactement la Notification. Quel événement déclenche un appel à Notification? La procédure de notification a été appelée plusieurs fois, à partir du moment où j'ai commencé à créer un nouvel onglet pour la tuerie réelle. m_BtnCloseTab: = nil; a été appelée juste avant de se bloquer.Crashing signifie que j'obtiens l'erreur suivante: Project project1 a levé la classe d'exception 'External: SIGSEGV'. A l'adresse 40CDA8 –

+0

Le propriétaire du bouton est-il toujours vide? Je pourrais imaginer qu'un objet détruit doit notifier son propriétaire car c'est l'objet qui va le détruire à la fin. Mais si le propriétaire est nul il n'y a personne à notifier ... - La notification s'étend profondément au cœur de la LCL (VCL) et est introduite par TComponent. Pour l'opération opRemove, elle est appelée à chaque fois qu'un composant TComponent est détruit. –

0

Je voudrais utiliser un seul bouton pour cette tâche.

Extrait la déclaration m_btnCloseTab de TTab et la place dans la forme principale privée.

ensuite sur votre principale de FormCreate de forme:

m_btnCloseTab: = TButton.Créer (MyPages);

(ci-dessus suppose l'MyPages est un composant placé sur le formulaire, sinon il doit d'abord être créé.)

Donnez le bouton un haut et à gauche qui fait sens pour votre TTAB de.

Maintenant, m_btnCloseTab sera libéré lorsque MyPages sera libéré lorsque le formulaire sera fermé.

Maintenant tout ce que vous avez à faire est de créer vos nouveaux onglets comme vous le souhaitez et quand on est concentré, il suffit de faire de cet onglet le parent de votre bouton. Vous pouvez le faire, par exemple, dans la méthode MyPages OnChange ou tout ce qu'elle a comme ça.

Lorsque le bouton est cliqué, il fait quelque chose comme TTab (Parent) .Free;

Cependant, vous devrez peut-être stocker Parent dans une variable locale OnClick du bouton, dire:

TempTab: TTAB

Ensuite, définissez simplement TempTab: = TTAB (Parent), réglez Parent Bouton à zéro , puis appelez TempTab.Free;

Je voudrais aussi donner à vos onglets un propriétaire. De cette façon, si l'utilisateur ferme le formulaire avec les onglets toujours ouverts (c'est-à-dire que votre bouton n'a pas été cliqué), le propriétaire les libérera.

donc déclarer vos onglets comme:

newtab: = TTab.Create (MyPages);

Cela devrait résoudre tous vos problèmes et, après un peu de fiddling, est assez facile à gérer.

Une recommandation finale J'utiliserais la méthode .Free et/ou FreeAndNil() plutôt que d'appeler directement .destroy.

+0

C'est une bonne idée. Je vais essayer quand j'ai du temps libre. Pour le moment, dans mon projet actuel, j'ai trouvé une solution de contournement. Au lieu de libérer les onglets, je les supprime du TPageControl en réglant le paramètre TTab.PageControl: = nil lorsque l'on clique sur le bouton de fermeture de l'onglet. Ce n'est pas un gros problème dans mon application car le nombre d'onglets est fini, et ces onglets sont quand même conservés dans une liste qui sera libérée lorsque le formulaire principal sera fermé. J'essaie de libérer autant de mémoire que possible avant de «cacher» les onglets pour que l'application ne soit pas trop lourde. –