Why the composite component fails to parent controls?
I created my own Component : TPage , which Contains Subcomponent TPaper (TPanel). The problem is, that when I put controls such as TMemo or TButton on the TPaper (which fills up nearly whole area), the controls do not load at all. see example below
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 is parented in TPaper (TPanel) at design-time, but after pressing "Run" it does not exist.
procedure TForm1.btn1Click(Sender: TObject);
begin
if not Assigned(Memo1) then Sho开发者_如何学PythonwMessage('I do not exist'); //Memo1 is nil
end;
Have you any idea what's wrong?
Thanks a lot
P.S Delphi 7
When I put TMemo inside TPaper and save the unit (Unit1), after inspection of associated dfm file, there is no trace of TMemo component. (Thats why it can not load to app.)
Serge is right. Delphi only streams components that are owned by the Form they reside in. In order to avoid the EClassNotfound Exception, which occurs during reading of the form file (You should now at least see a Tpaper component in your dfm file) you must register the class by using the RegisterClass function (in the unit Classes). A good place for this would be in the initialisation section of your unit.
If setting the owner of Tpaper to a Form is not an option, then you can still get Delphi to stream your subcomponents by overriding the Getchildren and GetChildOwner methods and applying the logic TCustomForm uses:
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;
The question is 5 years ago, but because I came across the same problem and could not find a workable solution in the network decided to share what I found as a solution after much testing.
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;
Not very professional, but I hope it will help :^)
P.S. just did a quick test (XE5), but basically works.
精彩评论