2016-09-20 46 views
0

我有一个表单,然后我有一个'TPageControl'对象(名为'MyPages')和一个'TButton'对象(名为'MyButton')在它上面设计时间。 然后我有一个名为'TTab'的新类,它扩展了'TTabSheet'。 'TTab'类有一个'TButton'对象作为其成员变量之一,如下所示。如何将消息从对象传递到Free Pascal中的另一个对象

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

当我点击“为myButton”,它会创建一个新的“TTAB”对象,初始化标签(如实例化“m_btnCloseTab”),并在运行时添加到“MyPages”。

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; 

这就是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; 

这增加了一个新的标签好吧。关闭按钮也显示在每个选项卡上。

如何在每个选项卡上单击'm_btnCloseTab'以关闭该特定选项卡?

如果我为TTab定义一个析构函数(通过重写TTabSheet的析构函数),我可以从外部调用它。

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

但我不能从标签内部调用析构函数(可以)。如果我这样做了,我不能释放m_btnCloseTab对象,因为它会发出异常,因为我们仍然是它的事件处理程序。如果我没有释放它,那么标签会很好地关闭,但内存会泄漏(因为我们没有释放m_btnCloseTab)。

我相信我必须触发一个事件,才能从'TTab'的外部调用析构函数。我不知道该怎么做。

任何帮助,将不胜感激。

谢谢。

+0

我不确定是否正确理解了一切。但是,如果您创建mbtnCloseTab并将Tab作为其所有者(mbtn_CloseTab:= TButton.Create(self),而不是... Create(nil)),那么该按钮会随选项卡一起自动销毁。另外,TTab应该有一个Notification方法,当它的一个子节点被销毁并且你可以使用它来设置mbtn_CloseTab为零时,它会被调用:这样可以避免Tab在按钮被销毁之前调用按钮的析构函数一些原因。 –

+0

谢谢您的评论(我认为没有人再使用Free Pascal),但它对实际结果没有影响。 通过使用create(nil),我必须在析构函数中释放m_btnCloseTab。通过使用Create(Self),TTab类的析构函数会自动执行它。 什么是通知方法?我如何实现一个?我认为这是我需要做的事情,有点像C#中的Delegate函数。欣赏它,如果你能指向我的教程或文档如何做到这一点。 –

+0

你错了。访问Lazarus论坛,查看活跃的Pascal社区。 –

回答

0

您可以在整个LCL源代码(当然也可以在Delphi中)找到通知方法。一个简单的例子是TLabeledEdit:这是一种包含TLabel的“TEdit”。如果标签被销毁,LabeledEdit会被通知,因为它必须将对标签的引用设置为零。否则,TLabeledEdit的析构函数会试图再次销毁标签 - BOOM。这里的方法是这样的(从ExtCtrls粘贴):

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

在这里,你可以看到你在你的情况下该怎么做:

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

请注意,通知是一个虚拟的方法和必须在组件的受保护部分中使用属性“覆盖”进行声明。

+0

从m_BtnCloseTab的onClick事件处理程序调用Destroy时,它仍然崩溃。 我想了解什么通知不完全。 什么事件触发对通知的调用?通知程序多次被调用,从我开始创建新标签到实际查杀的时候。 m_BtnCloseTab:= nil; 只在碰撞前被调用。崩溃意味着,我得到以下错误: 项目project1引发异常类'外部:SIGSEGV'。 在地址40CDA8 –

+0

按钮的所有者仍然是零?我可以想象一个被销毁的对象必须通知其拥有者,因为这是最终会毁掉它的对象。但是,如果业主没有,就没有人被通知...... - 通知深入到LCL(VCL)的核心,由TComponent引入。对于opRemove操作,只要TComponent被销毁,就会调用它。 –

0

我会为此任务使用一个按钮。

从TTab中取出m_btnCloseTab声明并将其放在私有主窗体中。

然后在你的主窗体的FORMCREATE:

m_btnCloseTab:= TButton的。创建(MyPages);

(以上假设MyPages是在窗体上放置一个组成部分,如果没有则必须先创建。)

给按钮顶部和左侧,使您TTAB的感觉。

现在,当表单关闭时释放MyPages时释放m_btnCloseTab。

现在,您只需创建新标签,然后只需将该标签作为按钮的父级即可。例如,您可以在MyPages OnChange方法或任何类似方法中执行此操作。

当按钮被点击时,它执行类似TTab(Parent).Free;

但是,您可能需要存储父在按钮的OnClick一个局部变量,说:

TempTab:TTAB

然后,只需设置TempTab:= TTAB(家长),设置按钮的母公司为nil ,然后调用TempTab.Free;

我也会给你的标签的所有者。这样,如果用户在标签仍然打开的情况下关闭表单(也就是说,您的按钮没有被点击),则所有者将释放它们。

所以,声明你的标签,如:

NEWTAB:= TTab.Create(MyPages);

这应该可以解决您的所有问题,并且在稍微摆弄之后,很容易管理。

最后一个建议我会使用.Free和/或FreeAndNil()方法,而不是直接调用.destroy。

+0

这是一个好主意。当我有空闲时,我会尝试一下。 目前,在我的实际项目中,我找到了解决方法。而不是释放选项卡,我只是通过设置TTab.PageControl:= nil在Tabage关闭按钮被点击时从TPageControl中移除它们。 在我的应用程序中这不是一个大问题,因为选项卡的数量是有限的,而且这些选项卡无论如何保存在列表中,当主窗体关闭时将被释放。我试图在“隐藏”标签之前释放尽可能多的内存,以使应用程序不会感到沉重。 –

相关问题