7

を含むフォームがありTFrameます。には、動的に入力される がTFrame含まれています。ComboBoxComboBoxエントリには、関連付けられたオブジェクトがあります。のオーバーライドされたデストラクタTFrameが呼び出されるまでに、関連付けられたオブジェクトを解放することなく、 内の ItemComboBoxがすでにクリアされています。これは、デザイナー ビューでフォームに をドロップするか、所有者としてComboBoxnil または を使用してコードで動的に作成した場合に発生します。TFrame現在、包含のOnDestroyイベントを使用して、包含TFormのクリーンアップ手順を呼び出していTFrameます。

TFrameのコンテナによる明示的なプロシージャ呼び出しを必要としないより良い方法はありますか? ComboBox動的に追加されたオブジェクトをどこで解放するのが理想的ですか?

4

4 に答える 4

8

TFrameのデストラクタが呼び出されると、ComboBox の項目は既にクリアされているとのことです。そうではありません。ComboBox アイテムは決してクリアされません。ComboBox によってアイテムが破棄されると、カウントは 0 になります。

アプリケーションを終了し、VCL がフレームと ComboBox を含むフォームを破棄すると、ネイティブの ComboBox コントロールも OS によって破棄されます。これは、破棄されるウィンドウに配置されているためです。後で項目にアクセスしてフレーム デストラクタでオブジェクトを解放できるようにする場合、VCL は項目数が 0 のネイティブ ComboBox コントロールを再作成する必要があります。

私が提案する解決策は簡単です。OnDestroyフレームをフレームワークに解放したままにしないでください。代わりに、フォームのイベントでフレームを破棄してください。これは、フォームの基になるウィンドウが破棄される前であるため、オブジェクトにアクセスできます。

フォームユニット

procedure TMyForm.FormDestroy(Sender: TObject);
begin
  MyFrame.Free;
end;

フレームユニット

destructor TMyFrame.Destroy;
var
  i: Integer;
begin
  for i := 0 to ComboBox1.Items.Count - 1 do
    ComboBox1.Items.Objects[i].Free;
  inherited;
end;
于 2013-02-24T15:36:55.367 に答える
8

TFrameWM_DESTROYハンドラーを次のように利用できます。

unit Unit2;

interface

uses
  Windows, Messages, SysUtils, Classes, Controls, Forms, StdCtrls;

type
  TFrame1 = class(TFrame)
    ComboBox1: TComboBox;
  private
    procedure WMDestroy(var Msg: TWMDestroy); message WM_DESTROY;
    procedure FreeComboBoxItems;
  public
    constructor Create(AOwner: TComponent); override;
  end;

implementation

{$R *.dfm}

constructor TFrame1.Create(AOwner: TComponent);
begin
  inherited;
  // Add some object items to the ComboBox
  ComboBox1.AddItem('a', TButton.Create(nil));
  ComboBox1.AddItem('b', TMemoryStream.Create);
  ComboBox1.AddItem('c', TList.Create);
end;

procedure TFrame1.WMDestroy(var Msg: TWMDestroy);
begin
  // Make sure the TFrame is actually destroying - not recreated
  if (csDestroying in ComponentState) then
    FreeComboBoxItems;
  inherited;
end;

procedure TFrame1.FreeComboBoxItems;
var
  I: Integer;
begin
  OutputDebugString('TFrame1.FreeComboBoxItems');
  with Self.ComboBox1 do
    for I := 0 to Items.Count - 1 do
    begin
      OutputDebugString(PChar(Items.Objects[I].ClassName + '.Free'));
      Items.Objects[I].Free;
    end;
end;

end.

もう 1 つのオプションは、ベースの祖先TAppBaseFormクラスとTAppBaseFrameアプリケーション全体の を作成し、すべてのフォームを として派生させTAppBaseForm、すべてのフレームをとして派生させることTAppBaseFrameです。このようにして、所有者のフォームがイベントハンドラーで破棄されたことをTAppBaseFormすべての子に通知できます。その時点で、ComboBox アイテムはまだ有効です (Sertac Akyuz の回答で説明されているように)。TAppBaseFrameTAppBaseForm.FormDestroy

于 2013-02-24T15:50:26.923 に答える
4

一般的に言えば、GUIコントロールにデータ(またはあなたの場合はオブジェクト)を保存することはお勧めできないため、あなたの質問はあまり役に立ちません。デザインの変更方法に関する David のコメントも参照してください。

ただし、コンボボックスがフォームの直接の子であることと、フォームの別の子(この場合はフレーム)の子であることの違いは、質問に答えるのが興味深いものです。どうやら、そのフレームのデストラクタが呼び出される前に、コンボ ボックスの項目が破棄されます。探索する明白な代替手段は、overriding Frame.BeforeDestruction、overriding Frame.DestroyWindowHandle、overriding Frame.DestroyWnd、または overriddenWM_DESTROYでの catchFrame.WndProcですが、アイテムが既になくなる前にそれらのいずれも呼び出されません。

次に試みることは、コンボ ボックスに対してこれを繰り返すことです。がコンボ ボックスにWM_DESTROY到達すると、アイテムがまだそこにあることがわかります。ただし、VCL がコンボ ボックスを頻繁に再作成する可能性があるため、コントロールが実際に破棄されている場合にのみ、そのメッセージをキャッチしないように注意してください。次のように、の介在クラスを使用して実装しTComboBoxます。

unit Unit2;

interface

uses
  Windows, Messages, Classes, Controls, Forms, StdCtrls;

type
  TComboBox = class(StdCtrls.TComboBox)
  protected
    procedure WndProc(var Message: TMessage); override;
  end;

  TFrame1 = class(TFrame)
    ComboBox1: TComboBox;
  end;

implementation

{$R *.dfm}

{ TComboBox }

procedure TComboBox.WndProc(var Message: TMessage);
var
  I: Integer;
begin
  if (Message.Msg = WM_DESTROY) and (csDestroying in ComponentState) then
    for I := 0 to Items.Count - 1 do
      Items.Objects[I].Free;
  inherited WndProc(Message);
end;

end.

さて、あなたの質問に答えるために:「これはより良い方法ですか?」

はい、フレームのレベルでオブジェクトの破壊を保証するためです。つまり、インスタンスごとに個別にこれを処理することを覚えておく必要はありません。

いいえ、そうではありません。このソリューションでは、使用を不要な余分な境界に制限する状況で、コンボ ボックス内のオブジェクトを解放できるようにする必要があるためです。

それで、この答えは役に立ちますか?それが現在のアプローチを使用できない場合は、そうです。


さらに、含まれているフォームハンドラーでフレームのParentプロパティを nil に設定する別の方法も見つけました。OnDestroy

procedure TForm2.FormDestroy(Sender: TObject);
begin
  Frame1.Parent := nil;
end;

この場合、フレームのデストラクタ内のコンボ ボックスに格納されているオブジェクトを安全に破棄できます。しかし、この解決策は説明的ではないため、現在の解決策よりもさらに悪いです。その後Frame1.FreeComboObjects、はるかに優れています。

于 2013-02-24T13:29:40.960 に答える