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