4

我正在使用德尔福 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. 如果组合框已排序,则索引将不匹配,因此我不能总是使用该解决方案。

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

4

3 回答 3

2

您可以覆盖功能GetItemsClass

function GetItemsClass: TCustomComboBoxStringsClass; override;

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

于 2013-08-24T07:39:11.913 回答
1

在阅读了 Sertac 的链接David Heffernan 的评论和 NGLN 的回答)之后,我相信将对象存储在托管列表而不是 GUI 控件中的解决方案是最好的。为此,我创建了一个从TCustomComboBox. 这让我可以提升除Sortedto之外的所有属性published。这使内部与组合框属性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.
于 2013-08-24T08:30:15.820 回答
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;
于 2014-09-12T17:26:50.993 回答