我正在使用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
中的相关对象。如果对组合框进行排序,那么索引将不匹配,因此我不能总是使用该解决方案。
有没有人见过这个?你是如何解决这个问题的?能够释放组件析构函数中的对象真是太好了!
答案 0 :(得分:2)
您可以覆盖功能GetItemsClass
:
function GetItemsClass: TCustomComboBoxStringsClass; override;
Combo调用它来创建Items(默认情况下它可能是TComboBoxStrings
)。
然后,您可以创建自己的TComboBoxStrings
后代,例如TComboBoxStringObjects
,其中
你可以释放与项目链接的对象(当项目被删除时)。
答案 1 :(得分:1)
在阅读the link from Sertac(David Heffernan's comment and NGLN's answer)之后,我认为将对象存储在托管列表而不是GUI控件中的解决方案是最好的。为此,我创建了一个从TCustomComboBox
下降的组合框。这样,我就可以宣传除Sorted
到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.
答案 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;