7

たとえば、実行時に (条件付きで) インスタンス化されたサブクラス化されたコンポーネントで TEdit を置き換えて解放することは可能ですか? もしそうなら、それはいつ、どのように行われるべきですか?親を nil に設定し、フォーム コンストラクターと AfterConstruction メソッドで free() を呼び出そうとしましたが、どちらの場合も実行時エラーが発生しました。


具体的に言うと、アクセス違反エラー (EAccessViolation) が発生しました。フレーム構築時にコンポーネントを解放すると、フォーム コントロールのハウスキーピングが台無しになる、というフランソワの言葉は正しいようです。

4

3 に答える 3

8

このより一般的なルーチンは、フォームまたはフレームのいずれかで動作します (新しいコントロールのサブクラスを使用するように更新されています)。

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;

CAUTION : フレームの AfterConstruction 内でこれを行う場合は、ホスティング フォームの構築がまだ完了していないことに注意してください。
そこでコントロールを解放すると、フォーム コントロールのハウスキーピングが台無しになるため、多くの問題が発生する可能性があります。
新しい Edit Caption を読み取って ShowMessage に表示しようとすると、何が得られるかを確認してください...
その場合、
...ReplaceControl(Edit1, 'Edit2', False )を使用してから ...FreeAndNil
を実行します
(Edit1)
後で。

于 2008-09-23T18:40:32.957 に答える
8

コントロールを削除するには、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 で行ったように、必要なすべてのプロパティをコピーします。

于 2008-09-23T13:44:11.500 に答える
1

実際にRTTI(TypInfoユニットを参照)を使用して、一致するすべてのプロパティのクローンを作成できます。しばらく前にこのコードを書いたのですが、今は見つかりません。探し続けます。

于 2008-09-23T18:17:24.543 に答える