提个问题,调用Form的Free方法去卸载一个窗体,窗体的 OnDestroy 事件是否是最后一个发生的事件?如果窗体中其他控件上有事件处理呢?TListVie

  • 主题发起人 主题发起人 dirk
  • 开始时间 开始时间
D

dirk

Unregistered / Unconfirmed
GUEST, unregistred user!
提个问题,调用Form的Free方法去卸载一个窗体,窗体的 OnDestroy 事件是否是最后一个发生的事件?如果窗体中其他控件上有事件处理呢?TListView控件是否比较特别?(100分)<br />以前我一直以为Form的 OnDestroy 事件是在卸载了窗体上的所有控件后,才触发的(不知
对否?),所以,书上都说,要在 OnDestroy 事件中释放一些资源,我也正是这么做的,
但现在我的程序却莫名其妙的出错,查了半天,发现,居然有些控件的事件会在Form的
OnDestroy事件之后触发,我有点糊涂了,请哪位高手帮我解释一下,代码请参考:

项目中放置Form1和Form2,其中Form1中放置一个按钮,在OnClick事件中写:

procedure TForm1.Button1Click(Sender: TObject);
begin
Form2:=TForm2.Create(self);
Form2.ShowModal ;
ShowMessage('aaa');
Form2.Free ;
end;

Form2中放置一个ListView1,上面随便加两个ListItem,并在ListView1的OnChange事件中写下

procedure TForm2.ListView1Change(Sender: TObject; Item: TListItem;
Change: TItemChange);
begin
ShowMessage('LV Change');
end;

在Form2的OnDestroy事件中写:
procedure TForm2.FormDestroy(Sender: TObject);
begin
ShowMessage('Form Destroy');
end;

运行,单击Form1上的按钮,显示Form2,选中ListView1上的一个ListItem,再关闭Form2,
好了,你会发现MessageBox的出现顺序是:
'aaa'
'Form Destroy'
'LV Change'
TListView的OnChange事件在Form的OnDestory事件之后发生,所以,如果你在FormDestory
事件中释放了TListView的OnChange事件中要使用的资源,就等着程序出错吧!

好像就TListView是这样,我用TTreeview试验,没有此现象。
 
应该是FormDestroy后释放Form作为Owner的控件,而在释放TListView的时候会触发'LV Change'吧。
 
不是吧,我觉得FormDestroy应该是最后一个触发的事件,在触发这个事件之前就应该将
Form作为Owner的控件释放,因为你想,Form上的控件有可能要引用一些动态创建的对象,
这些对象只应该在FormDestroy事件中释放才安全,所以FormDestroy应该是最后一个触发
的事件,而如果按你所说,我们应该在什么地方安全地释放这些对象呢(也就是所有以
Form为Owner的控件都释放后)?
 
在ListView1释放时调用的ListView1Change事件,在Form2上加一个Button可以证明
procedure TForm2.Button1Click(Sender: TObject);
begin
ListView1.Free;
end;
 
是的,这点我知道,我的问题是FormDestroy事件是否是释放一个窗体时触发的最后一个事件?
Form是否应该在释放了其上所有以Form为Owner的控件后再触发FormDestroy事件?
我认为是,但实际上却好像不是?
 
刚才写乱了,修改了一下。
 
//没想明白各事件之间的执行顺序,也没时间想,不过解决办法倒是有一个
procedure TForm1.FormDestroy(Sender: TObject);
begin
ListView1.OnChange := nil;
end;
 
我现在是这样做:
procedure TForm1.FormDestroy(Sender: TObject);
begin
ListView1.Selected := nil;
//……销毁其他对象
end;
强制触发OnChange过程,无奈的举动。

我觉得我的理解是对的,那么,是delphi的失误吗?敬请高手解释一下。
 
destructor TComponent.Destroy;
begin
Destroying;
if FFreeNotifies <> nil then
begin
while Assigned(FFreeNotifies) and (FFreeNotifies.Count > 0) do
TComponent(FFreeNotifies[FFreeNotifies.Count - 1]).Notification(Self, opRemove);
FreeAndNil(FFreeNotifies);
end;
DestroyComponents;
if FOwner <> nil then FOwner.RemoveComponent(Self);
inherited Destroy;
end;
 
TComponent.Owner的帮助:
……
This means that when a form is destroyed, all the components on the form are also destroyed.
DeStory事件到底是不是一个组件的最后的事件?
 
http://www.delphibbs.com/delphibbs/DispQ.asp?LID=1354567
关注一下吧没有人提起来呀!用MODEM打电话利用PC机的耳机和麦克风,自己编程如何实现?
(我知道有现成的软件可以用,但是我想自己实现这个上东西)
QQ:65466700
MAIL: along@b2sun.com
TEL :13802785865
http://b2sun.com
请各位大侠多多指教!

 
没人能回答我吗?
 
只有看delphi的源码了
 
就是hryyx贴出来的,看过了,不太懂,好像没有看到Owner释放下属组件的代码。

高手来看看吧!
 
destructor TCustomForm.Destroy;
begin
if not (csDestroying in ComponentState) then GlobalNameSpace.BeginWrite;
try
if OldCreateOrder then DoDestroy;//首先到用窗体中定义的事件。
MergeMenu(False);
if HandleAllocated then DestroyWindowHandle;
Screen.RemoveForm(Self);
FCanvas.Free;
FIcon.Free;
FreeAndNil(FActionLists);
inherited Destroy;//然后调用继承的Destroy.
//调用了TComponent.Destroy来释放
//该窗体上所有的控件。
finally
GlobalNameSpace.EndWrite;
end;
end;

destructor TComponent.Destroy;
var
I: Integer;
begin
Destroying;
if FFreeNotifies <> nil then
begin
for I := FFreeNotifies.Count - 1 downto 0 do
begin
TComponent(FFreeNotifies).Notification(Self, opRemove);
if FFreeNotifies = nil then Break;
end;
FFreeNotifies.Free;
FFreeNotifies := nil;
end;
DestroyComponents;
if FOwner <> nil then FOwner.RemoveComponent(Self);
inherited Destroy;
end;
所有出现上面这样的结果是正确的。borland之所以这样设计我想是因为:
在触发onDestroy之前释放了所有的控件后,如果要在OnDestroy使用
窗体中的某一个控件时,会出现指针访问错误。
 
补充一下:
to dirk:
FormDestroy触发
自己定义子程序A
执行Destory过程。//系统隐藏。
FormDestroy结束
Destory中过程中在调用窗体上某一个组件的Destroy方法时,可能会触发
该组件的相关事件例如在调用ListView.Destroy时,在一定情况下会触发
ListViewChange事件。
 
看了lodgue的解答,疑惑顿消,但我觉的还是TListView有点问题,像TTreeView就不会这
样,看来以后编程要小心点,在FormDestroy中释放对象时还要顾及到像TListView这样的
控件的感受才行,不然都不知道是怎么死的 :)

这个问题再挂两天就发分!
 
Form的OnDestroy事件并不是最后一个被触发的事件,而是第一个事件.
释放不释放以Form为Owner的控件与OnDestroy并没有多大的关系.如果
在运行期动态添加了控件,应该主动释放的话,应该在该事件句柄中释放.

ListView的OnChange事件是在释放其中的ListItem或ListColumn时产
生的.一个没有Column和没有ListItem的ListView的释放的时候只可能
触发OnDestroy事件.如果为了防止ListView的OnChange使用已经被释
放的资源,在Form的OnDestroy事件中设置某个全局变量或对象变量为
True,这样如果这个变量为True时,ListView的OnCange不做任何处理.

如何捕捉Form释放时的最后一个事件?眼下没有很有效的方法.你只能
自己定义一个事件,然后重载Form的FreeInstance方法,在该方法中调
用自己定义的事件句柄.
 
同意 svw0506 的方法,其实我通常就这样用的
procedure TForm1.FormDestroy(Sender: TObject);
begin
xxx.OnChange := nil;
xxx.OnClick:= nil;
end;
这也许是最简单也最安全的方法
 

Similar threads

S
回复
0
查看
3K
SUNSTONE的Delphi笔记
S
S
回复
0
查看
2K
SUNSTONE的Delphi笔记
S
D
回复
0
查看
1K
DelphiTeacher的专栏
D
后退
顶部