为什么复合组件无法进行父控件?

时间:2010-04-19 18:49:15

标签: delphi

我创建了自己的组件: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 Delphi 7

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

2 个答案:

答案 0 :(得分:2)

塞尔是对的。 Delphi只传输它们所在的Form所拥有的组件。为了避免在读取表单文件期间发生的EClassNotfound异常(您现在至少应该在dfm文件中看到Tpaper组件),您必须注册该类通过使用RegisterClass函数(在单元类中)。一个好的地方就是你单位的初始化部分。

如果将Tpaper的所有者设置为Form不是一个选项,那么您仍然可以通过重写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;

答案 1 :(得分:0)

问题是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),但基本上都有效。