TComboBox.Destroy (一定要快)!!!(300分)

  • 主题发起人 主题发起人 autumn
  • 开始时间 开始时间
A

autumn

Unregistered / Unconfirmed
GUEST, unregistred user!
unit ComboBox1;

interface

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

type
TComboBox1 = class(TComboBox)
private
{ Private declarations }
protected
{ Protected declarations }
public
destructor destrory;override;
{ Public declarations }
published
{ Published declarations }
end;

procedure Register;

implementation

procedure Register;
begin
RegisterComponents('Autumn', [TComboBox1]);
end;

{ TComboBox1 }

destructor TComboBox1.destrory;
begin
showmessage(inttostr(items.count));
inherited;

end;

end.

调用:
...
var a:tacombobox;
{$R *.DFM}

procedure TForm1.Button1Click(Sender: TObject);
begin
a:=tacombobox.Create(self);
//a.parent:=self;
a.left:=10;
a.top:=10;
a.DataSet:=adotable1;
a.IdText:='groupid';
a.DisplayText:='submodule';
a.show;
end;

procedure TForm1.Button2Click(Sender: TObject);
begin
a.free;
end;.
...
这样就不会出错。
但是将她编译放到ide中。然后拉下来放到form中加入几项item.然后run 关闭窗体的时候就
出现
Exception EinvalidOperation in module project1.exe at 0002d69c control 'acombobox1' has no parent window
 
//a.parent:=self;
不要注示掉
 
把 showmessage(inttostr(items.count)); 去掉
 
你给出的定义“TComboBox1”和你的调用“tacombobox”根本就是风马牛不相及的嘛,叫人
怎么看得懂问题出在哪里?!
 
更正:a.parent:=self有
showmessage(inttostr(imtes.count)) 我就要在这里free 掉 items里的objects。不然我
那知道有几项items
 
>...
>var a:tacombobox;
把这个放到form的 private里试试

>showmessage(inttostr(imtes.count)) 我就要在这里free 掉 items里的objects。不然我
>那知道有几项items

如果items里有对象的话,是要在destroy之前手工释放的
 
在form.onclose中应
a.destrory:=nil;
 
要指定parent的。
 
我的问题出现在combobox1上
 
是否是这样:
少了constructor create(Aowner:Tcomponent);override;
begin
inherited create(Aowner);
end;
 
同意郭镇松:
把 showmessage(inttostr(items.count)); 去掉
在析构函数中显示一个MESSAGE常会造成问题。具体原因不太清楚。但至少这样做可能造
成该对象所使用的资源不能被按时释放,是线程不安全的。
 
...
procedure TForm1.Button2Click(Sender: TObject);
begin
a.free;
a:=nil;//99%的原因是没有加上这句
//因为a的parent是Form1,而你这里手动将a释放了,但是父窗体Form1不知道
//在你关闭Form1的时候Form1会自动试图释放a从而导致出错,解决方法就是
//加上这句a:=nil;
end;
 
问题可能出在ShowMessage。ShowMessage是需要窗口的,如果destroy的时候已经没有窗
口了就会出错。
 
这是控件的源代码:
unit ComboBox1;

interface

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

type
TComboBox1 = class(TComboBox)
private
{ Private declarations }
protected
{ Protected declarations }
public
destructor destrory;override;
{ Public declarations }
published
{ Published declarations }
end;

procedure Register;

implementation

procedure Register;
begin
RegisterComponents('Autumn', [TComboBox1]);
end;

{ TComboBox1 }

destructor TComboBox1.destrory;
begin
showmessage(inttostr(items.count));
inherited;

end;

end.

这是我调用的源程序(没有任何毛病):
...
var a:tcombobox1;
{$R *.DFM}

procedure TForm1.Button1Click(Sender: TObject);
begin
a:=tcombobox1.Create(self);
//a.parent:=self;
a.left:=10;
a.top:=10;
a.DataSet:=adotable1;
a.IdText:='groupid';
a.DisplayText:='submodule';
a.show;
end;

procedure TForm1.Button2Click(Sender: TObject);
begin
a.free;
end;.
...

问题出现在:将combobox1.pas放到package然后安装.于是在ide的选择板上就有一个tcombobox1
我新建一个项目,放入combobox1然后运行,退出。就出现这个错误:
Exception EinvalidOperation in module project1.exe at 0002d69c control 'acombobox1' has no parent window

问题的本质:在tcombobox.items.addobject 在程序的后期需要free掉各自的object吗?如果要,那写在什么地方,是
destroy吗.
 
最终的解决方案:参照ahm的方法新建一个tstringlist的属性纪录建立的object,在destroy
中释放.不知是否有更好的方法
unit aComboBox;

interface

uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
StdCtrls,DB;

type
TaComboBox = class(TComboBox)
private
FDataSet: TDataSet;
FDisplayText: string;
FIdText: string;
function GetIdValue: string;
{ Private declarations }
protected
FNeedFree:boolean;//ÐèÒªÔÚDestroryÖÐFree aObject
FObjectList:TStringList;//test now...
FBookmark:TBookmark;//¼Í¼DataSetµÄÖ¸Õë
FActiveState:boolean;//¼Í¼DataSetµÄ״̬
FDropDownEvent:TNotifyEvent;//±£´æDropDownEventµÄʼþ
procedure aDropDown(Sender:TObject);//×Ô¶¨ÒåDropDownEvent
procedure GetList;//»ñÈ¡Áбí
procedure FreeAObject;//ÊÍ·ÅAObject;
{ Protected declarations }
public
procedure aRefresh;
property IdValue:string read GetIdValue;//»ñÈ¡IdµÄÖµ

constructor Create(AOwner:TComponent);override;
destructor Destroy; override;

{ Public declarations }
published
property DataSet:TDataSet read FDataSet write FDataSet;//±í
property DisplayText:string read FDisplayText write FDisplayText ;//ÏÔʾµÄ×Ö¶Î
property IdText:string read FIdText write FIdText;//¹Ø¼ü×Ö
{ Published declarations }
end;
TaComboBoxIdObject=class
aGuid:string;
// destructor destroy;override;
end;


procedure Register;

implementation


procedure Register;
begin
RegisterComponents('Autumn', [TaComboBox]);
end;

{ TaComboBox }

procedure TaComboBox.aDropDown(Sender: TObject);
begin
try
{ inherited DropDown }
if Assigned(FDropDownEvent) then FDropDownEvent(Sender);
finally

if Items.Count=0 then GetList;
end;
end;

procedure TaComboBox.aRefresh;
begin
FreeAObject;
FObjectList.Clear;
FNeedFree:=false;
Items.Clear;


end;


constructor TaComboBox.Create(AOwner: TComponent);
begin
inherited Create(AOwner);
FObjectList:=TStringList.Create;
FDropDownEvent:=OnDropDown;
OnDropDown:=aDropDown;
end;

destructor TaComboBox.Destroy;
begin
if FNeedFree then FreeAObject;
FObjectList.Free;
inherited;

end;

procedure TaComboBox.FreeAObject;
var aObject:TaComboBoxIdObject;
i:integer;

begin
for i:=0 to FObjectList.Count-1 do begin
if Assigned(FObjectList.Objects) then begin
aObject:=TaComboBoxIdObject(FObjectList.Objects);
aObject.Free;
end;
end;
end;

function TaComboBox.GetIdValue: string;
begin
result:='';
if Items.Count=0 then Exit;
if Items.Objects[ItemIndex] is TaComboBoxIdObject then begin
result:=TaComboBoxIdObject(Items.Objects[ItemIndex]).aGuid;
end
else
result:=IntToStr(Integer(Items.Objects[ItemIndex]));
end;

procedure TaComboBox.GetList;
var aObject:TaComboBoxIdObject;
begin
// Items.Clear;
with FDataSet do
try
FActiveState:=Active;
if Active then begin
FBookmark:=GetBookmark;
DisableControls;
end
else
Open;
First;
FNeedFree:=FieldByName(FIdText).DataType=ftGuid;
while Not FDataSet.Eof do begin
if FNeedFree then begin
FNeedFree:=true;
aObject:=TaComboBoxIdObject.Create;
aObject.aGuid:=FieldbyName(FIdText).AsString;
FObjectList.AddObject(FieldByName(FDisplayText).AsString,aObject);
// Items.AddObject(FieldByName(FDisplayText).AsString,aObject);
end
else begin
Items.AddObject(FieldByName(FDisplayText).AsString,
TObject(FieldByName(FIdText).AsInteger) );
end;

Next;
end;
if FNeedFree then
Items.Assign(FObjectList);
finally
FDataSet.Active:=FActiveState;
if FDataSet.Active then begin
FDataSet.GotoBookmark(FBookmark);
FDataSet.EnableControls;
FDataSet.FreeBookmark(FBookmark);
end;
end;
end;


{ TaComboBoxIdObject }


{ TaComboBoxIdObject }

{destructor TaComboBoxIdObject.destroy;
var a:tstringlist;
begin
a:=tstringlist.create;
if fileexists('d:/a.txt') then
a.LoadFromFile('d:/a.txt');
a.add(' application.MessageBox(a,b,0);'+timetostr(now));
a.savetofile('d:/a.txt');
a.free;
inherited;

end;}
end.
 
你的问题到底是什么呀?
如果是一开始提的问题,只要在form的ondestroy事件中加上acombobox1.free即可解决,
不过由于你的控件代码有问题,给别人用的时候不能保证别人会加free,还是会出现毛病的。
 
一刀:
控件那里有问题?请指教。form的那段代码是测试那个控件用的。本身没有问题
 
你在控件的ondestroy里面使用它的属性(items.count),这是危险的。
因为如果你不是显式释放这个combobox,那么就由它的owner的destroy来释放。
在你的程序里也就是form,但是由于这时候form已经调用了destroy过程,
本身已经不存在了,因此运行到combobox的destroy时,就会发生“no parent windows”的错误。

所以如上所说,必须要显式调用combobox.free才可以。但是你不能保证
控件的使用者都这样去做,因此安全的办法就是不要在destroy里再引用属性和方法。
 
一刀:
>你在控件的ondestroy里面使用它的属性(items.count),这是危险的。
>因为如果你不是显式释放这个combobox,那么就由它的owner的destroy来释放。
>在你的程序里也就是form,但是由于这时候form已经调用了destroy过程,
>本身已经不存在了,因此运行到combobox的destroy时,就会发生“no parent windows”的错误。
谢谢。你说到我的心坎上了.那你是如何释放你在Items.AddObject中创建的对象的?
我的做法是用一个TStringList纪录这些Objects的位置。在Destroy中再释放这个TStringList
的对象,再释放TStringList(aComboBox.pas)
 
最好的办法应该从 TComboBox.Items 的继承着手:
TComboBox.Items 其实是一个 TComboBoxStrings 类对象,虽然它被声明成 TStrings,如下:
TCustomComboBox = class(TWinControl)
private
FItems: TStrings;
...
end;
constructor TCustomComboBox.Create(AOwner: TComponent);
begin
...
<font color = #ff0000><strong>FItems := TComboBoxStrings.Create;</font></strong>
TComboBoxStrings(FItems).ComboBox := Self;
...
end;

所以,你只需要从 TComboBoxStrings 继承一个 TMyComboBoxStrings,把 Clear 方法 Override 就可以了:
TMyComboBoxStrings = class(TComboBoxStrings)
...
public
procedure Clear; override;
end;

procedure TMyComboBoxStrings.Clear;
begin
for ... // 清除你的对象
Inherited;
end;

到了这一步,如果能够 Override TComboBox 创建 FItems 的代码,你的要求就可以实现了,
可惜的是,我发现这段代码是嵌在 Create 中间的,没有单独列出来,所以此路不通。
如果 VCL 能够写成下面的形式,就可以用这个方法达到你的要求了:
TCustomComboBox = class(TWinControl)
private
FItems: TStrings;
...
protected
procedure CreateItems; virtual;
end;

procedure TCustomComboBox.CreateItems;
begin
FItems := TComboBoxStrings.Create;
TComboBoxStrings(FItems).ComboBox := Self;
end;

constructor TCustomComboBox.Create(AOwner: TComponent);
begin
...
CreateItems;
...
end;

有可能的话,你试一试 Override DestroyWnd 方法吧,这样也许不会出问题。
 

Similar threads

I
回复
0
查看
728
import
I
I
回复
0
查看
633
import
I
I
回复
0
查看
527
import
I
I
回复
0
查看
515
import
I
后退
顶部