2

TPersistentフィールドを持つTComponentからプロパティを書き出す方法について非常に混乱しています。たとえば、私は持っています:

  TChildObj = class( TPersistent )
  PRIVATE
    FVisible: boolean;
    FColor: TColor;
  PUBLIC
  PUBLISHED
    property Visible : boolean
               read FVisible
               write FVisible;
    property Color : TColor
               read FColor
               write FColor;
  end;


  TTest = class( TComponent )
    constructor Create( AOwner : TComponent ); override;
    destructor Destroy; override;
  private
    FChildObj : TChildObj;
    FOne: integer;
  published
    property One : integer
               read FOne
               write FOne;
    property ChildObj : TChildObj
               read FChildObj;
  end;

次のライターコードを使用する場合:

procedure TForm1.Button5Click(Sender: TObject);
var
  MS : TMemoryStream;
  SS : TStringStream;
  Test : TTest;
begin
  Test := TTest.Create( Self );
  MS := TMemoryStream.Create;
  SS := TStringStream.Create;
  try
    MS.WriteComponent( Test );
    MS.Position := 0;
    ObjectBinaryToText( MS, SS );
    SS.SaveToFile( 'c:\scratch\test.txt' );
  finally
    MS.Free;
    SS.Free;
  end;

end;

私は次のものだけを取得します:

object TTest
  One = 0
end

つまり、TPersistentTChildObjがありません。

コンポーネントのシリアル化に関するこの記事では、「コンポーネントはデフォルトで、TComponentではないタイプTPersistentのプロパティをストリーミングします。TPersistentプロパティはコンポーネントと同じようにストリーミングされ、ストリーミングされる他のTPersistentプロパティがある場合があります。」ただし、System.Classesにステップインすると、12950行目(XE3)付近にテストがあります。

  if (PropInfo^.GetProc <> nil) and
     ((PropInfo^.SetProc <> nil) or
     ((PropInfo^.PropType^.Kind = tkClass) and
      (TObject(GetOrdProp(Instance, PropInfo)) is TComponent) and
      (csSubComponent in TComponent(GetOrdProp(Instance, PropInfo)).ComponentStyle))) then

これは、コンポーネントとサブコンポーネントのみがシリアル化されていることを示しているようです。TChildObjをTComponentから派生させる(そしてそれに名前を付ける)と、書き込まれたファイルにその名前が表示されます(ただし、プロパティは表示されません)。

私が本当に理解していないのは、TControl(コンポーネント)にはFontプロパティ(TPersistent)があり、たとえばTLabelを作成すると、これが正常にストリーミングされることです。

それとも、これはデフォルトのプロパティと関係がありますか?

助けていただければ幸いです。

4

1 に答える 1

12

TPersistentRTLがプロパティをストリーミングする必要があるかどうかを決定するときは、要件のリストを詳しく見てください。

if (PropInfo^.GetProc <> nil) and
 ((PropInfo^.SetProc <> nil) or
 ((PropInfo^.PropType^.Kind = tkClass) and
  (TObject(GetOrdProp(Instance, PropInfo)) is TComponent) and
  (csSubComponent in TComponent(GetOrdProp(Instance, PropInfo)).ComponentStyle))) then

ChildObjプロパティは読み取り専用プロパティであるため、PropInfo^.SetProc <> nil要件を満たしていません。また、派生TComponentサブコンポーネントではないため、isis TComponentおよびcsSubComponent要件を満たしていません。そのため、プロパティがDFMから欠落しています。

最も簡単な解決策は、ChildObjプロパティを読み取り専用ではなく読み取り/書き込みにすることです(必要な場合をTComponent除いて、この状況では使用しないでください)。

また、オブジェクトTTestを解放するためのデストラクタがありません。TChildObjまた、適切な方法として、ハンドラーを割り当てることがTChildObjできるOnChangeイベントを指定して、サブプロパティTTestへの変更に対応できるようにする必要があります。TChildObj

これを試して:

type
  TChildObj = class(TPersistent)
  private
    FVisible : Boolean;
    FColor : TColor;
    FOnChange : TNotifyEvent;
    procedure Changed;
    procedure SetVisible(Value : Boolean);
    procedure SetColor(Value : TColor);
  public
    procedure Assign(Source : TPersistent); override;
    property OnChange : TNotifyEvent read FOnChange write FOnChange;
  published
    property Visible : Boolean read FVisible write SetVisible;
    property Color : TColor read FColor write SetColor;
  end;

  TTest = class(TComponent)
  private
    FChildObj : TChildObj;
    FOne : integer;
    procedure ChildObjChanged(Sender : TObject);
    procedure SetChildObj(Value : TChildObj);
  protected
    procedure Loaded; override;
  public
    constructor Create(AOwner : TComponent); override;
    destructor Destroy; override;
  published
    property One : integer read FOne write FOne;
    property ChildObj : TChildObj read FChildObj write SetChildObj;
  end;

procedure TChildObj.Assign(Source: TPersistent);
begin
  if Source is TChildObj then
  begin
    FVisible := TChildObj(Source).Visible;
    FColor := TChildObj(Source).Color;
    Changed;
  end else
    inherited;
end;

procedure TChildObj.Changed;
begin
  if Assigned(FOnChange) then
    FOnChange(Self);
end;

procedure TChildObj.SetVisible(Value : Boolean);
begin
  if FVisible <> Value then
  begin
    FVisible := Value;
    Changed;
  end;
end;

procedure TChildObj.SetColor(Value : TColor);
begin
  if FColor <> Value then
  begin
    FColor := Value;
    Changed;
  end;
end;

constructor TTest.Create(AOwner : TComponent);
begin
  inherited;
  FChildObj := TChildObj.Create;
  FChildObj.OnChange := ChildObjChanged;
end;

destructor TTest.Destroy;
begin
  FChildObj.Free;
  inherited;
end;

procedure TTest.ChildObjChanged(Sender : TObject);
begin
  if csLoading in ComponentState then Exit;
  // use ChildObj values as needed...
end;

procedure TTest.Loaded;
begin
  inherited;
  ChildObjChanged(nil);
end;

procedure TTest.SetChildObj(Value : TChildObj);
begin
  if FChildObj <> Value then
    FChildObj.Assign(Value);
end;

アプローチする場合は、TComponent代わりにこれを試してください。

type
  TChildObj = class(TComponent)
  private
    FVisible : Boolean;
    FColor : TColor;
    FOnChange : TNotifyEvent;
    procedure Changed;
    procedure SetVisible(Value : Boolean);
    procedure SetColor(Value : TColor);
  public
    procedure Assign(Source : TPersistent); override;
    property OnChange : TNotifyEvent read FOnChange write FOnChange;
  published
    property Visible : Boolean read FVisible write SetVisible;
    property Color : TColor read FColor write SetColor;
  end;

  TTest = class(TComponent)
  private
    FChildObj : TChildObj;
    FOne : integer;
    procedure ChildObjChanged(Sender : TObject);
    procedure SetChildObj(Value : TChildObj);
  protected
    procedure Loaded; override;
  public
    constructor Create(AOwner : TComponent); override;
  published
    property One : integer read FOne write FOne;
    property ChildObj : TChildObj read FChildObj write SetChildObj;
  end;

procedure TChildObj.Assign(Source: TPersistent);
begin
  if Source is TChildObj then
  begin
    FVisible := TChildObj(Source).Visible;
    FColor := TChildObj(Source).Color;
    Changed;
  end else
    inherited;
end;

procedure TChildObj.Changed;
begin
  if Assigned(FOnChange) then
    FOnChange(Self);
end;

procedure TChildObj.SetVisible(Value : Boolean);
begin
  if FVisible <> Value then
  begin
    FVisible := Value;
    Changed;
  end;
end;

procedure TChildObj.SetColor(Value : TColor);
begin
  if FColor <> Value then
  begin
    FColor := Value;
    Changed;
  end;
end;

constructor TTest.Create(AOwner : TComponent);
begin
  inherited;
  FChildObj := TChildObj.Create(Self);
  FChildObj.SetSubComponent(True);
  FChildObj.OnChange := ChildObjChanged;
end;

procedure TTest.ChildObjChanged(Sender : TObject);
begin
  if csLoading in ComponentState then Exit;
  // use ChildObj values as needed...
end;

procedure TTest.Loaded;
begin
  inherited;
  ChildObjChanged(nil);
end;

procedure TTest.SetChildObj(Value : TChildObj);
begin
  if FChildObj <> Value then
    FChildObj.Assign(Value);
end;
于 2013-02-08T01:03:31.653 に答える