使用tpagecontrol时的所有权原则

时间:2017-05-06 12:24:13

标签: delphi generics

两天前,我给出了question的接受答案(最让我困扰的是):

  newtabsheet:=ttabsheet.Create(PageControl1);
  NewTabSheet.PageControl := PageControl1;
  newtabsheet.Caption:='tab1';
  i1:=tabs.Count;
  tabs.Add(newtabsheet);

我已尽力分析此代码,这些是我的结果。

第1行:我创建了一个ttabsheetpagecontrol1作为父/所有者(基于下面的构造函数)。

constructor TTabSheet.Create(AOwner: TComponent);
begin
  inherited Create(AOwner);
  Align := alClient;
  ControlStyle := ControlStyle + [csAcceptsControls, csNoDesignVisible,
    csParentBackground, csPannable];
  Visible := False;
  FTabVisible := True;
  FHighlighted := False;
end;

然后我在变量Newtabsheet中存储了对它的引用(这个转换基于@David Heffernan给出one我的问题的答案。)

第2行:我使用该引用将pagecontrol1分配给了属性.pagecontrol

这是设置方法的代码:

procedure TTabSheet.SetPageControl(APageControl: TPageControl);
begin
  if FPageControl <> APageControl then
  begin
    if FPageControl <> nil then
      FPageControl.RemovePage(Self);
    Parent := APageControl;
    if APageControl <> nil then
      APageControl.InsertPage(Self);
  end;
end;

基于此我认为(可能是错误的)它是将现有父项与新传递的参数进行比较,如果存在不同的话,那么parent<>nil将从前一个{{1}中删除此newtabsheet (视觉效果)并将其分配给新的父/所有者,然后如果新的pagecontrol将其插入新的parent<>nil(视觉效果)。

所以这条线是不必要的(也许是错误的),因为我已经在第一行中做到了。

第3行:我使用引用将pagecontrol的标题指定为&#39; tab1&#39;。

第5行:我使用add方法存储对标签列表tabsheettabsheet的引用。

这是事情开始在我脑海中浮现的地方。

我认为父母/所有者是tabs:TList<ttabsheet>;,但tabs仍然显示标签(根据此question的已接受答案,更改父级会直观地移除pagecontrol1 1}}来自tabsheet),但它没有。

现在这可能是错误的,但如果它只是一个参考,那么为什么当我通过pagecontrol1tabsheet删除pagecontrol1 PageControl1.ActivePage.freetabs.count保持不变。

如果我从标签中删除tabsheet,则tabsheet上的pagecontrol1不会被删除(直观删除)。

从此question我了解到仿制品成为了父母/所有者,您无需担心从tabsheet释放pagecontrol1,因为制表符是父级,您只需要将其从tabs中解放出来。

我的问题:此代码中发生了什么以及我为什么会遇到这种行为?

澄清

当我在页面控件中删除ttabsheet时,为什么当我尝试在列表中使用对它的引用时,为什么它没有引发错误,它是两个不同的对象。

1 个答案:

答案 0 :(得分:3)

Owner负责内存管理。将TPageControl指定为Owner的{​​{1}}表示TTabSheet会在TPageControl被销毁时销毁TTabSheetTPageControl和ownee包含彼此的指针,因此他们可以相互通知与内存管理相关的重要事件。

Owner负责窗口管理和视觉演示。将Parent指定为TPageControl的{​​{1}}表示Parent窗口是TTabSheet窗口的子窗口,并将显示在TTabSheet窗口的客户区域内。 TPageControl和子项包含彼此的指针,以便他们可以相互通知与窗口管理相关的重要事件。

TPageControlParent 两个不同的东西。他们可以是同一个对象,但他们不必(例如 - 在设计时创建的组件始终归Owner所有,{{1} },或正在设计的Parent,但可以是容器组件的子代,如TForm等。

TFrame负责没有。它只是一个任意值的动态数组,在这种情况下恰好是TDataModule个指针。而已。 TPanelTList之间没有所有者/家长关系。

TTabSheet被销毁时,它本身与其TList之间存在关系链接。 TTabSheetTTabSheet的管理层中删除了自己。它已不再归TPageControl所有,并且不再是TTabSheet的子级。这两个对象清除了彼此的指针。

TPageControlTPageControl之间没有关系链接。 TPageControl没有TTabSheet甚至存在的概念。当TList添加到TTabSheet并从TList中删除时,TTabSheet只是添加/删除指向 TList对象的指针。 TList未通知TTabSheet有关插入/移除的信息。并且没有从TListTTabSheet的指针,因此当TTabSheet被销毁时,没有适当的机制允许指向TList的指针已从TTabSheet中移除。指针保留在列表中,您可以继续使用指针(用于比较等),只要您不取消引用它以访问被破坏的TTabSheet

如果您想要在TList被销毁时自动从TTabSheet删除TTabSheet指针,则需要拨打TList&#39; s {{ 1}}方法。然后,您的TTabSheet回调可以从TTabSheet移除FreeNotification()指针以响应Notification()通知,例如:

TTabSheet

如果您希望TListopRemove移除TMyForm = class(TForm) ... private tabs: TList<TTabSheet>; ... protected procedure Notification(AComponent: TComponent; Operation: TOperation); override; // <-- ADD THIS ... end; ... procedure TMyForm.DoSomething; var NewTabSheet: TTabSheet; ... begin ... NewTabSheet := TTabSheet.Create(PageControl1); NewTabSheet.PageControl := PageControl1; NewTabSheet.Caption := 'tab1'; tabs.Add(NewTabSheet); NewTabSheet.FreeNotification(Self); // <-- ADD THIS ... end; procedure TMyForm.Notification(AComponent: TComponent; Operation: TOperation); begin inherited; if (Operation = opRemove) and (AComponent is TTabSheet) then tabs.Remove(TTabSheet(AComponent)); // <-- HERE end; 时,会自动从TTabSheet移除指针,请切换为TPageControl。默认情况下,其TList属性为true,因此当使用TObjectListOwnsObjects方法从列表中删除指针时,它将销毁TTabSheetRemove()方法不会销毁Delete(),因此请在Extract()回调中使用TTabSheet代替Remove(),例如:

Notification()