如何将消息从对象传递到Free Pascal中的另一个对象

时间:2016-09-20 06:14:15

标签: lazarus freepascal destroy

我有一个表单,然后我在设计时放置了一个'TPageControl'对象(名为'MyPages')和一个'TButton'对象(名为'MyButton')。 然后我有了一个名为'TTab'的新类,它扩展了'TTabSheet'。 'TTab'类有一个'TButton'对象作为其成员变量之一,如下所示。

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'的外部调用析构函数。我不知道怎么做。

任何帮助将不胜感激。

感谢。

2 个答案:

答案 0 :(得分:0)

您可以在整个LCL源中找到通知方法(当然也可以在Delphi中找到)。一个简单的例子是TLabeledEdit:这是某种包含TLabel的“TEdit”。如果Label被销毁,LabeledEdit会收到通知,因为它必须将对标签的引用设置为nil。否则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;

请注意,Notification是一个虚方法,必须在组件的受保护部分中使用属性“override”声明。

答案 1 :(得分:0)

我会用一个按钮完成这项任务。

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

然后在您的主要表单上找到FormCreate:

m_btnCloseTab:= TButton.Create(MyPages);

(以上假设MyPages是放在表单上的组件,如果不是,则必须先创建它。)

将按钮设置为顶部和左侧,这对您的TTab来说是有意义的。

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

现在你要做的就是创建你喜欢的新标签,当一个人专注时,只需将该标签作为按钮的父标签即可。例如,您可以在MyPages OnChange方法或其他类似方法中执行此操作。

单击该按钮时,它会执行类似于TTab(父级).Free;

的操作

但是,您可能需要将Parent存储在按钮的OnClick中的本地变量中,例如:

TempTab:TTab

然后只需设置TempTab:= TTab(Parent),将Button的父级设为nil,然后调用TempTab.Free;

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

请声明您的标签:

newTab:= TTab.Create(MyPages);

这应该可以解决你所有的问题,经过一些摆弄后,很容易管理。

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