TStringlistのObjectsに関連付けられたコンポーネントのSave、Loadをしたいのですが、どのような方法が最適でしょうか?
ObjectsにはTComponentの継承クラスが必ず存在し、空になることはないためSaveToStreamをoverrideしてinherited SaveToStreamしたあと、TWriterを使い、WriteListBegin、WriteListEndでWriteComponentをはさみ、ひたすらコンポーネントをリストしようかと考えました。が、TWriterを扱ったことがないためTWriter.Create(Stream: TStream; BufSize: Integer);のBufSizeをいくつにしたらよいかがわからず、つまずきました。ヘルプを見てもこの値がいまいちわかりません。
また、他にうまくObjectsをSave、Loadする方法があれば教えていただきたいです。よろしくお願いします。[D6Per]
Objects[] に記憶されているのはインスタンスのポインタに相当するものです。
これをセーブしても、次にロードしたとき、同じインスタンスが存在することは
一般にはないと思いますけど。
BufSize については過去に議論がありました。
https://www.petitmonte.com/bbs/answers?question_id=2997
> これをセーブしても、
あっ、勘違いでした。すみません。WriteComponent() ですね。
レスありがとうございます。BufSizeについては検索で簡単にヒットしますね。お手数をおかけしましてすみませんでした。
TStringListを継承して次のようなクラスを作りました。
type
TStrCompList = class(TStringList)
private
function GetComp(Index: Integer): TComponent;
procedure PutComp(Index: Integer; const Value: TComponent);
public
procedure Clear; override;
procedure SaveToStream(Stream: TStream); override;
procedure LoadFromStream(Stream: TStream); override;
property Objects[Index: Integer]: TComponent read GetComp write PutComp;
end;
---
function TStrCompList.GetComp(Index: Integer): TComponent;
begin
Result:= TComponent(inherited GetObject(Index));
end;
procedure TStrCompList.PutComp(Index: Integer; const Value: TComponent);
begin
inherited PutObject(Index, Value);
end;
procedure TStrCompList.Clear;
var
i: Integer;
begin
for i:= 0 to Count-1 do Objects[i].Free;
inherited;
end;
procedure TStrCompList.SaveToStream(Stream: TStream);
var
i: Integer;
w: TWriter;
begin
w:= TWriter.Create(Stream, $fff);
try
w.WriteListBegin;
for i:= 0 to Count-1 do begin
w.WriteString(Strings[i]);
w.WriteComponent(Objects[i]);
end;
w.WriteListEnd;
w.FlushBuffer;
finally
w.Free;
end;
end;
procedure TStrCompList.LoadFromStream(Stream: TStream);
var
r: TReader;
begin
Clear;
r:= TReader.Create(Stream, $fff);
try
r.ReadListBegin;
while not r.EndOfList do
AddObject(r.ReadString, r.ReadComponent(nil));
r.ReadListEnd;
r.FlushBuffer;
finally
r.Free;
end;
end;
ですが、AddObject('qwe',Button1)をしてSaveToStreamをするとw.WriteComponent(Objects[i]);でAccessViolationエラーが出てしまいます。
どこを直せばよいのでしょうか?よろしくお願いします。
Helpによると
WriteComponent メソッドを直接呼び出さないでください。
コンポーネントのプロパティが存在するかのテストが必要?
プロパティ関連のプログラムが不完全では。
コンポーネントをストリーミングして何をなさりたいのか分かりませんが、
> w.WriteComponent(Objects[i]);
これだと Objects[] は TObject 型なので AV が起こるのでは?
明示的に w.WriteComponent(TButton(Objects[i])); とかでキャストするのかなぁ?
ヘルプによると
WriteComponent メソッドを直接呼び出さないでください。WriteComponent
メソッドは Root プロパティ内の各所有コンポーネントで再帰的に呼び出されます。
となっていますので、IDE が使っているのでためしたことは無いです。
@123さま、えーとさまレスありがとうございます。
>これだと Objects[] は TObject 型なので AV が起こるのでは?
>明示的に w.WriteComponent(TButton(Objects[i])); とかでキャストするのかなぁ?
Objects[]がTComponentを返すクラスにしてあるので。。。結局以下のようにしてうまくいったようです。何度もレスをしていただきありがとうございました。
procedure TStrCompList.SaveToStream(Stream: TStream);
var
i: Integer;
w: TWriter;
begin
w:= TWriter.Create(Stream, $fff);
try
w.WriteListBegin;
for i:= 0 to Count-1 do begin
w.WriteString(Strings[i]);
w.WriteRootComponent(Objects[i]);
end;
w.WriteListEnd;
finally
w.Free;
end;
end;
procedure TStrCompList.LoadFromStream(Stream: TStream; AOwner: TComponent);
var
i: Integer;
r: TReader;
begin
Clear;
r:= TReader.Create(Stream, $fff);
try
r.ReadListBegin;
while not r.EndOfList do
begin
i:= Add(r.ReadString);
Objects[i]:= r.ReadRootComponent(nil);
AOwner.InsertComponent(Objects[i]);
end;
r.ReadListEnd;
finally
r.Free;
end;
end;
ツイート | ![]() |