析构函数中的TComboBox'Control没有父窗口'

时间:2013-08-23 23:22:33

标签: delphi delphi-xe2 destructor

我正在使用Delphi XE2。我构建了一个自定义TComboBox,以便我可以轻松添加键/字符串对并处理组件析构函数中的清理。

为简洁起见,省略了所有 if not (csDesigning in ComponentState) 代码。

interface

type
  TKeyRec = class(TObject)
    Key: string;
    Value: string;
  end;

  TMyComboBox = class(TComboBox)
  public
    destructor Destroy; override;
    procedure AddItemPair(const Key, Value: string);
  end;

implementation

destructor TMyComboBox.Destroy;
var i: Integer;
begin
  for i := 0 to Self.Items.Count - 1 do
    Self.Items.Objects[i].Free;
  Self.Clear;
  inherited;
end;

procedure TMyComboBox.AddItemPair(const Key, Value: string);
var rec: TKeyRec;
begin
  rec := TKeyRec.Create;
  rec.Key := Key;
  rec.Value := Value;
  Self.Items.AddObject(Value, rec);
end;

当应用程序关闭时,将调用析构函数,但Items.Count属性不可访问,因为TComboBox必须具有父控件才能访问此属性。在调用析构函数时,它不再具有父控件。

之前我曾见过这个问题,并且必须将对象存储在单独的TList中并单独释放它们。但这只有效,因为我将它们添加到TList的顺序始终与添加到组合框中的字符串相同。当用户选择一个字符串时,我可以使用组合框索引来查找TList中的相关对象。如果对组合框进行排序,那么索引将不匹配,因此我不能总是使用该解决方案。

有没有人见过这个?你是如何解决这个问题的?能够释放组件析构函数中的对象真是太好了!

3 个答案:

答案 0 :(得分:2)

您可以覆盖功能GetItemsClass

function GetItemsClass: TCustomComboBoxStringsClass; override;

Combo调用它来创建Items(默认情况下它可能是TComboBoxStrings)。 然后,您可以创建自己的TComboBoxStrings后代,例如TComboBoxStringObjects,其中 你可以释放与项目链接的对象(当项目被删除时)。

答案 1 :(得分:1)

在阅读the link from SertacDavid Heffernan's comment and NGLN's answer)之后,我认为将对象存储在托管列表而不是GUI控件中的解决方案是最好的。为此,我创建了一个从TCustomComboBox下降的组合框。这样,我就可以宣传除Sortedpublished之外的所有属性。这使内部FList与组合框Items属性中的字符串保持同步。我只是确保在添加它们之前按照我想要的方式对它们进行排序......

以下显示了我的所作所为。为简洁起见,我只包括基本代码(较少的范围检查),但包括一些条件逻辑,允许组合框在没有对象的情况下使用。

FList中正确销毁

destructor,释放所有对象而没有任何运行时异常,对象列表在组件本身内进行管理,而不必在其他地方进行管理 - 使其非常便携。当控件在设计时添加到表单或在运行时创建时,它可以工作。我希望这对其他人有用!

interface

type
  TMyComboBox = class(TCustomComboBox)
  private
    FList: TList;
    FUsesObjects: Boolean;
    function GetKey: string;
  public
    constructor Create(AOwner: TComponent); override;
    destructor Destroy; override;
    procedure AddItemPair(const Key, Value: string);
    procedure ClearAllItems;
    procedure DeleteItem(const Index: Integer);
    property Key: string read GetKey;
  published
    // all published properties (except Sorted) from TComboBox
  end;

implementation

type
  TKeyRec = class(TObject)
    Key: string;
    Value: string;
  end;

function TMyComboBox.GetKey: string;
begin
  if not FUsesObjects then
    raise Exception.Create('Objects are not used.');

  Result := TKeyRec(FList.Items[ItemIndex]).Key;
end;

constructor TMyComboBox.Create(AOwner: TComponent);
begin
  inherited;

  if not (csDesigning in ComponentState) then
  begin
    FUsesObjects := False;
    FList := TList.Create;
  end;
end;

destructor TMyComboBox.Destroy;
begin
  if not (csDesigning in ComponentState) then
  begin
    ClearAllItems;
    FreeAndNil(FList);
  end;

  inherited;
end;

procedure TMyComboBox.AddItemPair(const Key, Value: string);
var rec: TKeyRec;
begin
  FUsesObjects := True;
  rec := TKeyRec.Create;
  rec.Key := Key;
  rec.Value := Value;
  FList.Add(rec);
  Items.Add(Value);
end;

procedure TMyComboBox.ClearAllItems;
var i: Integer;
begin
  if not (csDesigning in ComponentState) then
  begin
    if FUsesObjects then
    begin
      for i := 0 to FList.Count - 1 do
        TKeyRec(FList.Items[i]).Free;
      FList.Clear;
    end;
    if not (csDestroying in ComponentState) then
      Clear; // can't clear if the component is being destroyed or there is an exception, 'no parent window'
  end;
end;

procedure TMyComboBox.DeleteItem(const Index: Integer);
begin
  if FUsesObjects then
  begin
    TKeyRec(FList.Items[Index]).Free;
    FList.Delete(Index);
  end;
  Items.Delete(Index);
end;

end.

答案 2 :(得分:0)

有一种方法可以避免重写组件以使用另一个列表来保存对象。解决方案是使用WM_DESTROY消息以及ComponentState属性。 当组件即将被销毁时,其状态将更改为csDestroying,因此下次收到WM_DESTROY消息时,它将不再是窗口重新创建过程的一部分。 我们在组件库中成功使用此方法。

TMyCombo = class(TCombobox)
...
  procedure   WMDestroy(var message: TMessage); message WM_DESTROY;
...

procedure TMyCombo.WMDestroy(var message: TMessage);
var
  i: integer;
begin
  if (csDestroying in ComponentState) then
    for i:=0 to Items.Count - 1 do
      Items.Objects[i].Free;
  inherited;
end;