Delphi 10.1 Firemonkey - 属性 组件构造期间的值
Delphi 10.1 Firemonkey - Property values during component construction
我在使用 Firemonkey 的 Delphi 10.1 中遇到了简单的问题。
创建新组件时(基于 TLayout,其中有一些其他组件,如 TDateEdits)我想创建一个 属性
property EditDate_Position:TPosition read FDateEdits_Position write FDateEdits_PositionSet stored True;
其中我将 FDateEdits_Position 声明为 TPosition,并且 FDateEdits_PositionSet 是一个 函数 FDateEdits_PositionSet(Value:TPosition).
组件的主要构造函数有一段代码:
PointF.X:=10;
PointF.Y:=30;
FDateEdits_Position:=TPosition.Create(PointF);
所以我在Object Inspector中有这个属性 EditDate_Position,我可以修改这个值。但是为什么 - 在编译和 运行 之后,这个值被重置为构造函数中的值?
我尝试使用
If (csDesigning in ComponentState) then
begin
PointF.X:=10;
PointF.Y:=30;
FDateEdits_Position:=TPosition.Create(PointF);
end;
在 运行 时排除那些行,但程序崩溃(FDateEdits 未创建)。我查看了 Object Inspector - 值是正确的,而且更多 - 在 .fmx 文件中我看到了正确的值。
那我该怎么办?我注意到这个值在构造函数执行时处于开始点,但在它之后的片刻(使用 Interval=1 的 TTimer 检查)- 它采用正确的值。
覆盖 AfterConstruction 过程并不能解决这个问题,我需要一个具有适当值的启动(创建时刻)的东西。还有更多:不是所有的东西都有这种行为——我看到布尔值的属性类型与 TPosition 相似,但是 TBitmap 属性 工作正常...
我认为这是 TPosition.Create(PointF) 的结果,但是如何在运行时不设置这些默认值的情况下创建它?
procedure TTest.FDateEdits_PositionSet(Value:TPosition);
begin
FDateEdits_Position:=Value;
FDateEdits_Resize;
end;
FDateEdits_Resize 移动一些组件在 (Self).
有示例代码(但不一样,是简化版):
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.
但我注意到只要调用
Layout11.BtnPosition.X:=50;
没有任何结果,代码中的断行不起作用(但在构造函数部分起作用...)
您描述的是正常行为。 TPosition
的子属性定义为 nodefault
,因此无论值如何,它们始终存储在 FMX 文件中。您的构造函数在设计时和 运行 时被调用,因此您必须首先设置默认值。在设计时打开现有的 Form/Frame,或在 运行 时 运行 打开项目时,加载 FMX 以覆盖默认值。完全正常的行为。如果您不希望您的组件在加载 FMX 文件时根据默认值进行操作,则需要检查 ComponentState
属性 并覆盖虚拟 Loaded()
方法。
为了使 BtnPosition.X
(或 Y
)的赋值生效,您需要为 TPosition.OnChange
事件分配一个事件处理程序。
试试这个:
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.
我在使用 Firemonkey 的 Delphi 10.1 中遇到了简单的问题。 创建新组件时(基于 TLayout,其中有一些其他组件,如 TDateEdits)我想创建一个 属性
property EditDate_Position:TPosition read FDateEdits_Position write FDateEdits_PositionSet stored True;
其中我将 FDateEdits_Position 声明为 TPosition,并且 FDateEdits_PositionSet 是一个 函数 FDateEdits_PositionSet(Value:TPosition).
组件的主要构造函数有一段代码:
PointF.X:=10;
PointF.Y:=30;
FDateEdits_Position:=TPosition.Create(PointF);
所以我在Object Inspector中有这个属性 EditDate_Position,我可以修改这个值。但是为什么 - 在编译和 运行 之后,这个值被重置为构造函数中的值? 我尝试使用
If (csDesigning in ComponentState) then
begin
PointF.X:=10;
PointF.Y:=30;
FDateEdits_Position:=TPosition.Create(PointF);
end;
在 运行 时排除那些行,但程序崩溃(FDateEdits 未创建)。我查看了 Object Inspector - 值是正确的,而且更多 - 在 .fmx 文件中我看到了正确的值。
那我该怎么办?我注意到这个值在构造函数执行时处于开始点,但在它之后的片刻(使用 Interval=1 的 TTimer 检查)- 它采用正确的值。
覆盖 AfterConstruction 过程并不能解决这个问题,我需要一个具有适当值的启动(创建时刻)的东西。还有更多:不是所有的东西都有这种行为——我看到布尔值的属性类型与 TPosition 相似,但是 TBitmap 属性 工作正常...
我认为这是 TPosition.Create(PointF) 的结果,但是如何在运行时不设置这些默认值的情况下创建它?
procedure TTest.FDateEdits_PositionSet(Value:TPosition);
begin
FDateEdits_Position:=Value;
FDateEdits_Resize;
end;
FDateEdits_Resize 移动一些组件在 (Self).
有示例代码(但不一样,是简化版):
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.
但我注意到只要调用
Layout11.BtnPosition.X:=50;
没有任何结果,代码中的断行不起作用(但在构造函数部分起作用...)
您描述的是正常行为。 TPosition
的子属性定义为 nodefault
,因此无论值如何,它们始终存储在 FMX 文件中。您的构造函数在设计时和 运行 时被调用,因此您必须首先设置默认值。在设计时打开现有的 Form/Frame,或在 运行 时 运行 打开项目时,加载 FMX 以覆盖默认值。完全正常的行为。如果您不希望您的组件在加载 FMX 文件时根据默认值进行操作,则需要检查 ComponentState
属性 并覆盖虚拟 Loaded()
方法。
为了使 BtnPosition.X
(或 Y
)的赋值生效,您需要为 TPosition.OnChange
事件分配一个事件处理程序。
试试这个:
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.