Delphi 为什么复合组件无法创建父控件?

Delphi 为什么复合组件无法创建父控件?,delphi,Delphi,我创建了自己的组件:TPage,它包含子组件TPaper(TPanel)。 问题是,当我将诸如TMemo或TButton之类的控件放在TPaper上(几乎占据了整个区域)时,控件根本不会加载。见下面的例子 TPaper = class(TPanel) protected constructor Create(AOwner: TComponent);override; destructor Destroy;override; public procedu

我创建了自己的组件:TPage,它包含子组件TPaper(TPanel)。 问题是,当我将诸如TMemo或TButton之类的控件放在TPaper上(几乎占据了整个区域)时,控件根本不会加载。见下面的例子

TPaper = class(TPanel)
  protected
      constructor Create(AOwner: TComponent);override;
      destructor Destroy;override;
  public
      procedure Paint; override;
  end;





TPage = class(TCustomControl)
   private
      FPaper:TPaper;
   protected
      procedure CreateParams(var Params:TCreateParams); override;
   public
      constructor Create(AOwner: TComponent);override;
      destructor Destroy;override;

   published
      property Paper: TPaper read FPaper write FPaper;
   end;




constructor TPage.Create(AOwner: TComponent);
  begin
  inherited Create(AOwner);

  PaperOrientation:=poPortrait;
  PaperSize:=psA4;
  PaperBrush:=TBrush.Create;
  PaperBrush.Color:=clWhite;
  PDFDocument:=Nil;
  FPaper:=TPaper.Create(Self);
  FPaper.Parent:=Self;
  FPaper.SetSubComponent(True);
  end;
。。。 Memo1在设计时以TPaper(TPanel)为父项,但在 按“运行”按钮,则不存在

procedure TForm1.btn1Click(Sender: TObject);
begin
if not Assigned(Memo1) then ShowMessage('I do not exist');   //Memo1 is nil
end;
你知道怎么了吗

非常感谢

p.S德尔福7


当我将TMemo放入TPaper并保存单元(Unit1)时,在检查相关dfm文件后,没有TMemo组件的痕迹。(这就是它无法加载到应用程序的原因。)

塞吉是对的。Delphi仅流式处理它们所在的表单所拥有的组件。为了避免读取表单文件时发生EClassNotfound异常(您现在至少应该在dfm文件中看到Tpaper组件),必须使用RegisterClass函数(在单元类中)注册该类。这方面的一个好地方是在你单位的初始化部分

如果将Tpaper的所有者设置为表单不是一个选项,那么您仍然可以通过覆盖Getchildren和GetChildOwner方法并应用TCustomForm使用的逻辑,让Delphi流式处理您的子组件:

TPage = class
 ...
public
  procedure GetChildren(Proc: TGetChildProc; Root: TComponent); override;
  function GetChildOwner:TComponent; override;
end;



procedure TPage.GetChildren(Proc: TGetChildProc; Root: TComponent);  // this is copied
var                                                                  // from 
  I: Integer;                                                        // TCustomForm
  OwnedComponent: TComponent;
begin
  inherited GetChildren(Proc, Root);
  if Root = Self then
    for I := 0 to ComponentCount - 1 do
    begin
      OwnedComponent := Components[I];
      if not OwnedComponent.HasParent then Proc(OwnedComponent);
    end;
end;

function TPage.GetChildOwner: TComponent;
begin
  inherited;
  Result:=Self;
end;

这个问题是5年前的事了,但因为我遇到了同样的问题,在网络中找不到可行的解决方案,所以我决定分享我经过大量测试后发现的解决方案

TClientPanel = class(TCustomControl)
private
  procedure WMNCHitTest(var Message: TWMNCHitTest); message WM_NCHITTEST;
public
  constructor Create(AOwner: TComponent); override;
end;

TMainPanel = class(TCustomControl)
private
  FClient: TClientPanel;
protected
  function GetChildOwner: TComponent; override;
  procedure GetChildren(Proc: TGetChildProc; Root: TComponent); override;
  procedure ReadState(Reader: TReader); override;
  procedure CreateComponentEvent(Reader: TReader; ComponentClass: TComponentClass; var Component: TComponent);
public
  constructor Create(AOwner: TComponent); override;
  destructor Destroy; override;
...
end;

constructor TClientPanel.Create(AOwner: TComponent);
begin
  inherited Create(AOwner);
  ControlStyle := ControlStyle + [csAcceptsControls, csNoDesignVisible];
end;

procedure TClientPanel.WMNCHitTest(var Message: TWMNCHitTest);
begin
  if not (csDesigning in ComponentState) then
    Message.Result := HTTRANSPARENT
  else
    inherited;
end;

var
  TClientPanel_Registered: Boolean = False;

constructor TMainPanel.Create(AOwner: TComponent);
begin
  inherited Create(AOwner);
  FClient := TClientPanel.Create(Self);
  FClient.Parent := Self;
  FClient.Align := alClient;
  Exclude(FComponentStyle, csInheritable);
  if not TClientPanel_Registered then
  begin
    RegisterClasses([TClientPanel]);
    TClientPanel_Registered := True;
  end;
end;

destructor TMainPanel.Destroy;
begin
  FClient.Free;
  inherited Destroy;
end;

function TMainPanel.GetChildOwner: TComponent;
begin
  Result := Self;
end;

procedure TMainPanel.GetChildren(Proc: TGetChildProc; Root: TComponent);
begin
  Proc(TControl(FClient));
end;

procedure TMainPanel.CreateComponentEvent(Reader: TReader; ComponentClass: TComponentClass; var Component: TComponent);
begin
  if ComponentClass.ClassName = 'TClientPanel' then Component := FClient;
end;

procedure TMainPanel.ReadState(Reader: TReader);
begin
  Reader.OnCreateComponent := CreateComponentEvent;
  inherited ReadState(Reader);
  Reader.OnCreateComponent := nil;
end;
不是很专业,但我希望这会有所帮助:^)


p.S.刚刚做了一个快速测试(XE5),但基本上可以正常工作。

谢谢您的回答。我试图在我的代码中实现这个代码段,但它并没有像我预期的那样工作。TPaper中插入的组件根本不出现在dfm文件中。我在帮助中找到了TComponentStyle:csSubComponent该组件是组件的子组件,它是其所有者属性的值。与顶级组件不同,子组件不会与它们所在的表单或数据模块一起保存。相反,子组件显示为其所有者的已发布属性的值,其已发布属性和事件与所属组件一起保存在表单文件中。我想,我不能期望插入TPaper的任何TWinCOntrol都会保存在dfm文件中。如何将备忘录插入TPaper?如果您像这样做:AMemo:=Tmemo.create(FPaper),那么很明显,它不会流到dfm中。请尝试AMemo:=TMemo.create(MyPage),并在设计时查看这是否有效。我把TPAge放在表格上。它里面也有TPaper。很好。然后,我将任何VCL控件(TMemo等)放入次同调TPaper中。看起来也不错。但当我按下Run按钮时,应用程序运行,但TMemo不显示。所以我关闭应用程序,我看到我珍贵的TMemo驻留在TPaper中。所以我做了这个把戏:我把Unit1保存在某个地方,然后打开Unit1.dfm,我没有看到任何TMemo对象“description”。难怪我没有加载。这很可能是因为我之前写的论点。我不想在programmaticaly中创建TMemo。我敢打赌,它会那样工作的。