Reputation: 85
I've got a problem with simply thing in Delphi 10.1 with Firemonkey. When creating a new component (based on TLayout, where are some another components like TDateEdits) I wanted to create a property
property EditDate_Position:TPosition read FDateEdits_Position write FDateEdits_PositionSet stored True;
where I declared a FDateEdits_Position as TPosition, and FDateEdits_PositionSet is a function FDateEdits_PositionSet(Value:TPosition).
The main constructor of component consist a code:
PointF.X:=10;
PointF.Y:=30;
FDateEdits_Position:=TPosition.Create(PointF);
So I have this property EditDate_Position in Object Inspector, and I can modify this value. But why - after compiling and running, this value is reseted to values as in Constructor? I tried to use a
If (csDesigning in ComponentState) then
begin
PointF.X:=10;
PointF.Y:=30;
FDateEdits_Position:=TPosition.Create(PointF);
end;
to exclude those lines when running, but the program crashes (FDateEdits not created). I looked to Object Inspector - values are properly, and more - in .fmx file I see proper values.
So what I should do? I've noticed that this value is in begin point when constructor is executing, but a bit moment after it (checked with a TTimer with an Interval=1) - it takes proper values.
Overriding the AfterConstruction procedure doesn't fix this problem, and I need a something with a startup (creation moment) with proper values. And more: not everything have this behavior - I see that properties type of Boolean are similar like TPosition, but a TBitmap property is working properly...
I think that it is an result of the TPosition.Create(PointF), but how to create this without setting those default values during runtime?
procedure TTest.FDateEdits_PositionSet(Value:TPosition);
begin
FDateEdits_Position:=Value;
FDateEdits_Resize;
end;
FDateEdits_Resize moves some components in (Self).
There is an sample code (but not the same, it's simplified):
unit Layout1;
interface
uses
System.SysUtils, System.Classes, FMX.Types, FMX.Controls, FMX.Layouts,
FMX.StdCtrls, System.Types;
type
TLayout1 = class(TLayout)
private
{ Private declarations }
FBtn:TButton;
FPosition:TPosition;
procedure FPositionSet(Value:TPosition);
protected
{ Protected declarations }
public
{ Public declarations }
constructor Create(AOwner:TComponent); override;
destructor Destroy; override;
published
{ Published declarations }
property BtnPosition:TPosition read FPosition write FPositionSet;
end;
procedure Register;
implementation
constructor TLayout1.Create(AOwner:TComponent);
var
PointF:TPointF;
begin
inherited Create(AOwner);
FBtn:=TButton.Create(Self);
FBtn.Parent:=Self;
FBtn.Stored:=False;
FBtn.Text:='Text';
PointF.X:=10;
PointF.Y:=10;
FPosition:=TPosition.Create(PointF);
FBtn.Position.Assign(FPosition);
end;
destructor TLayout1.Destroy;
begin
If FPosition<>nil then FPosition.Free;
If FBtn<>nil then FBtn.Free;
inherited;
end;
procedure TLayout1.FPositionSet(Value:TPosition);
begin
FPosition.Assign(Value);
FBtn.Position.Assign(Value);
end;
procedure Register;
begin
RegisterComponents('Samples', [TLayout1]);
end;
end.
But I've noticed that simply calling
Layout11.BtnPosition.X:=50;
doesn't have any result, the breakline in code doesn't work (but in constructor section works...)
Upvotes: 2
Views: 822
Reputation: 598089
What you describe is normal behavior. TPosition
's subproperties are defined as nodefault
, so they are always stored in the FMX file regardless of value. Your constructor is called at design-time and run-time, so you have to set default values first. When opening an existing Form/Frame at design-time, or running the project at run-time, the FMX is loaded to overwrite the defaults. Perfectly normal behavior. If you don't want your component to act on the default values when loading the FMX file, you need to check the ComponentState
property and override the virtual Loaded()
method.
In order for assignments to BtnPosition.X
(or Y
) to have an effect, you need to assign an event handler to the TPosition.OnChange
event.
Try this:
unit Layout1;
interface
uses
System.SysUtils, System.Classes, FMX.Types, FMX.Controls, FMX.Layouts,
FMX.StdCtrls, System.Types;
type
TLayout1 = class(TLayout)
private
{ Private declarations }
FBtn: TButton;
FPosition: TPosition;
procedure FPositionChanged(Sender: TObject);
procedure FPositionSet(Value: TPosition);
protected
{ Protected declarations }
procedure Loaded; override;
procedure Notification(AComponent: TComponent; Operation: TOperation); override;
public
{ Public declarations }
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
published
{ Published declarations }
property BtnPosition: TPosition read FPosition write FPositionSet;
end;
procedure Register;
implementation
constructor TLayout1.Create(AOwner: TComponent);
var
PointF: TPointF;
begin
inherited Create(AOwner);
FBtn := TButton.Create(Self);
FBtn.Parent := Self;
FBtn.Stored := False;
FBtn.Text := 'Text';
PointF.X := 10;
PointF.Y := 10;
FPosition := TPosition.Create(PointF);
FPosition.OnChange := FPositionChanged;
If not (csLoading in ComponentState) then
FBtn.Position.Assign(FPosition);
end;
destructor TLayout1.Destroy;
begin
FPosition.Free;
FBtn.Free;
inherited;
end;
procedure TLayout1.FPositionChanged(Sender: TObject);
begin
if (FBtn <> nil) and not (csLoading in ComponentState) then
FBtn.Position.Assign(FPosition);
end;
procedure TLayout1.FPositionSet(Value: TPosition);
begin
if Value <> FPosition then
FPosition.Assign(Value);
end;
procedure TLayout1.Loaded;
begin
inherited;
FBtn.Position.Assign(FPosition);
end;
procedure TLayout1.Notification(AComponent: TComponent; Operation: TOperation);
begin
inherited;
if (Operation = opRemove) and (AComponent = FBtn) then
FBtn := nil;
end;
procedure Register;
begin
RegisterComponents('Samples', [TLayout1]);
end;
end.
Upvotes: 2