質問
たとえば、実行時に (条件付きで) インスタンス化されたサブクラス化されたコンポーネントで TEdit を置き換えて解放することは可能ですか?もしそうなら、いつ、どのように行うべきですか?親を nil に設定し、フォーム コンストラクターと AfterConstruction メソッドで free() を呼び出そうとしましたが、どちらの場合も実行時エラーが発生しました。
具体的に言うと、アクセス違反エラー(EAccessViolation)が発生しました。フレームの構築時にコンポーネントを解放すると、フォーム制御のハウスキーピングが台無しになるという François の指摘は正しいようです。
解決
このより一般的なルーチンは、フォームまたはフレームのいずれかで動作します (新しいコントロールのサブクラスを使用するように更新されました)。
function ReplaceControlEx(AControl: TControl; const AControlClass: TControlClass; const ANewName: string; const IsFreed : Boolean = True): TControl;
begin
if AControl = nil then
begin
Result := nil;
Exit;
end;
Result := AControlClass.Create(AControl.Owner);
CloneProperties(AControl, Result);// copy all properties to new control
// Result.Left := AControl.Left; // or copy some properties manually...
// Result.Top := AControl.Top;
Result.Name := ANewName;
Result.Parent := AControl.Parent; // needed for the InsertControl & RemoveControl magic
if IsFreed then
FreeAndNil(AControl);
end;
function ReplaceControl(AControl: TControl; const ANewName: string; const IsFreed : Boolean = True): TControl;
begin
if AControl = nil then
Result := nil
else
Result := ReplaceControlEx(AControl, TControlClass(AControl.ClassType), ANewName, IsFreed);
end;
このルーチンを使用してプロパティを新しいコントロールに渡します
procedure CloneProperties(const Source: TControl; const Dest: TControl);
var
ms: TMemoryStream;
OldName: string;
begin
OldName := Source.Name;
Source.Name := ''; // needed to avoid Name collision
try
ms := TMemoryStream.Create;
try
ms.WriteComponent(Source);
ms.Position := 0;
ms.ReadComponent(Dest);
finally
ms.Free;
end;
finally
Source.Name := OldName;
end;
end;
次のように使用します:
procedure TFrame1.AfterConstruction;
var
I: Integer;
NewEdit: TMyEdit;
begin
inherited;
NewEdit := ReplaceControlEx(Edit1, TMyEdit, 'Edit2') as TMyEdit;
if Assigned(NewEdit) then
begin
NewEdit.Text := 'My Brand New Edit';
NewEdit.Author := 'Myself';
end;
for I:=0 to ControlCount-1 do
begin
ShowMessage(Controls[I].Name);
end;
end;
注意:これをフレームの AfterConstruction 内で行う場合は、ホストしているフォームの構築がまだ完了していないことに注意してください。
そこでコントロールを解放すると、フォーム コントロールのハウスキーピングが台無しになるため、多くの問題が発生する可能性があります。
ShowMessage に表示する新しい Edit Caption を読み取ってみるとどうなるかを確認してください...
その場合に使用したいのは、
...ReplaceControl(Edit1, 'Edit2', 間違い)
そして、
...FreeAndNil(編集1)
後で。
他のヒント
コントロールを削除するには、TEdit の親の RemoveControl を呼び出す必要があります。新しいコントロールを追加するには、InsertControl を使用します。
var Edit2: TEdit;
begin
Edit2 := TEdit.Create(self);
Edit2.Left := Edit1.Left;
Edit2.Top := Edit2.Top;
Edit1.Parent.Insertcontrol(Edit2);
TWinControl(Edit1.parent).RemoveControl(Edit1);
Edit1.Free;
end;
TEdit.Create を使用するクラスに置き換え、Left と Top で行ったように必要なプロパティをすべてコピーします。
実際に RTTI (TypInfo ユニットを参照) を使用して、一致するすべてのプロパティのクローンを作成できます。しばらく前にこのコードを書きましたが、今は見つかりません。探し続けます。