我在使用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 ,我可以修改这个值。但是为什么 - 在编译和运行之后,这个值会像在Constructor中一样重置为值? 我试过用
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;
没有任何结果,代码中的分隔线不起作用(但在构造函数部分工作......)
答案 0 :(得分:2)
您所描述的是正常行为。 TPosition
的子属性定义为nodefault
,因此它们始终存储在FMX文件中,而不管其值如何。您的构造函数在设计时和运行时调用,因此您必须先设置默认值。在设计时打开现有表单/框架或在运行时运行项目时,将加载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.