Delphi 10.1 Firemonkey - 组件构造期间的属性值

时间:2016-11-24 18:58:42

标签: delphi components firemonkey delphi-10.1-berlin

我在使用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;

没有任何结果,代码中的分隔线不起作用(但在构造函数部分工作......)

1 个答案:

答案 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.