在Delphi 中用程序实现自定义窗体的创建和显示顺序(2)
以上方法虽然可以实现动态的有选择地创建和显示窗体,但是有一个致命的弱点,就是程序关闭权限的转移,不能保留Form1的程序关闭权限。怎样真正实现有选择的创建和显示窗体,并且能保持Form1为主窗体呢?方法如下:
把3个窗体都放置在Auto-create forms里面,即3个窗体都自动创建。
在Form1中设置一个全局变量
var
flag:Integer;
然后把上面Form1那段代码改成如下所示:
procedure TForm1.FormCreate(Sender: TObject);
var
randomnum:Integer;
begin
flag:=0;
Label1.Caption:='Form1 Create Complete!';
Randomize;
randomnum:=Random(10);
if (randomnum>0)and (randomnum<=4) then
flag:=2
else if (randomnum>4)and (randomnum<=8) then
flag:=3
else
flag:=1;
end;
(代码6)
然后,在Form2的OnCreate() 事件中加入以下代码:
procedure TForm2.FormCreate(Sender: TObject);
begin
Label1.Caption:='Form2 Create Completed!';
end;
在Form3的OnCreate() 事件中加入以下代码:
procedure TForm3.FormCreate(Sender: TObject);
begin
Label1.Caption:='Form3 Create Complete! (' +IntToStr(flag)+') ';
Form3.Hide;
if flag=3 then
Begin
Form3.Visible:=True;
End
else if flag=2 then
Begin
Form2.Visible:=true;
End
else if flag=1 then
Begin
Form1.Show;
end;
(代码7)
还要记得把Form2和Form3的FormStyle属性改成fsStayOnTop,把Form1的FormStyle属性设成fsNormal,否则无法在屏幕的最前端显示所需要的窗体。把Form2和 Form3的Visible属性设成False,否则程序将无法正确地Focus(聚焦)于所需窗体。经过这样的修改之后就能根据随机结果选择最终要显示的窗体。
分析一下上面的过程可知道,窗体1,2,3是按顺序创建的,但是在Form1的创建过程中设置了一个全局变量作为记录的标记,记录应该显示的是哪个窗体,Form2的创建过程没有做任何动作,只是简单的一个显示信息,而在Form3的创建过程中则判断标志flag的值,由此决定究竟应该显示哪个窗体。
注意到主窗体是一定会显示出来的,这也是Form1的Visible没有设置成 False的原因,因为即使设置成False,也是会显示出来的。
以上所说的方法,其中的3个窗体都是已经被完全创建的,只是其中一个或两个没显示出来而已(设置成Visible为False),如果现在打开Spy++,可以清楚地看到3个窗口都在。但是我们的目的不仅仅如此,我们还要实现窗体创建的动态性和选择性。
前面说过使用Appliation.CreateForm()的方法来动态创建窗体有它的局限性,就是所谓的权限的转移。实际上我们可以改变这种情形,方法如下:
在Project1.dpr中只创建Form1
begin
Application.Initialize;
Application.CreateForm(TForm1, Form1);
Application.Run;
end.
在Form1的OnCreate()中的代码和代码5一样。Form2,Form3的OnCreate()不需要任何代码, 这时程序的关闭权限还是没有回到Form1的手里,其实我们只要在Form1的OnClose()中加入一句就行了:
Application.Terminate;
如果不加入这一句,则关闭Form1是不会关闭整个应用程序的,必须关闭Form2或Form3才行。 有些人可能会问,为什么不是Free而是Terminate呢?查一下Delphi的帮助文件关于Free的描述,其中有一个注意事项如下所述:
Warning: Never explicitly free a component within one of its own event handlers or the event handler of a component it owns or contains. For example, don’t free a button, or the form that owns the button, in its OnClick event handler.
可以看到,在Delphi中不能在一个组件它自己的事件处理中明确地释放这个组件。这就是为什么不能用Application.Free的原因。必须用Application.Terminate这一句来释放窗体创建的资源,否则将出现资源没有释放的错误。如何知道资源没有释放呢?在Delphi编译器则体现为Project在Running状态,并且需要通过Run->Program Reset来结束程序;另外,如果把程序Build了之后,脱离Delphi编译器直接运行程序,然后关闭Form1,这时候打开Windows的任务管理器,在进程一页将显示Project仍然在,占用着内存和CPU,需要通过结束进程来去掉它。
关于Application.Terminate,Delphi的帮助文件有这样的描述:
Call Terminate to end the application programmatically. By calling Terminate rather than freeing the application object, you allow the application to shut down in an orderly fashion.
这里,Delphi很明确的告诉我们,调用Terminate而不是Free,可以让应用程序以整齐的方式关闭。
到这里我们的任务还没结束,我们只是让Form1 和Form2或者是Form3有同等的关闭程序的权限,我们还要剥夺Form2或是Form3的程序关闭权,方法是在Form2和Form3的OnClose()事件函数中加入一句Application.Run,这样按Form2和Fomr3的关闭按钮都不会有什么反应了,当然也就不会关闭程序了。我们再添加一个按钮用来“关闭”窗体,在其Onclik事件中加入一句
Form2.Visible:=False;
所谓的关闭其实是把显示关闭,而不是把窗体关闭,窗体的资源是没有释放的。Delphi的帮助文件中关于Application.Run的描述不鼓励我们使用Run,但是从实际使用情况来看,并没有什么坏处,只是窗体的关闭按钮的功能被屏蔽掉了,但是我们有替代的办法,把窗体的BorderStyle属性设置成bsNone就可以把窗体的Title标题栏取掉,用其他按钮或者其他控件,代替标题栏上的最小化,最大化,关闭按钮。
到此为止,我们可以说是基本解决了本文开始提出的问题,但是还有一个重要问题我们忽略了,Fom1总是会出现,聪明的你可能想到了在Project.dpr中编写一些代码来处理,对极了,请看下面代码:
program Project1;
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls,
Unit1 in 'Unit1.pas' {Form1},
Unit2 in 'Unit2.pas' {Form2},
Unit3 in 'Unit3.pas' {Form3};
{$R *.res}
var
randomnum:Integer;
begin
Application.Initialize;
Randomize;//设置随机器
randomnum:=Random(10);//在0到10之间取随机值
if (randomnum>0)and (randomnum<=4)then//如果随机值在0到4之间
Begin
Application.CreateForm(TForm2, Form2);
End
else if (randomnum>4)and (randomnum<=8)then//如果随机数在4到8之间
Begin
Application.CreateForm(TForm3, Form3);
End
Else //如果随机数在8到10之间
Begin
Application.CreateForm(TForm1, Form1);
End;
Application.Run;
end.
实现以上代码后,Form1,Form2,Form3不用写任何处理代码,不用修改任何属性,就能解决我们的问题,我们可能要慨叹了,为什么一开始没想到这样做。
三. 总结
使用Appliation.CreateForm()的方法来动态创建窗体有它的局限性,本文介绍了一个简单而实用的方法用来动态显示窗体,这种方法的关键是设置一个标志,根据实际情况给标志赋值,等到全部窗体创建完毕后,根据所设标志决定需要显示的窗体。但是这种方法的缺点是所有的窗体都将被创建。本文介绍了另外一种方法,这种方法能真正实现动态选择创建窗体,但是同样存在缺点,就是程序关闭权限被平均化了,但是我们有相应的解决办法。这两种方法各有其优缺点,为此我们用到第三种方法,在Project.dpr中编写代码实现窗体的动态选择创建,到此则完满地解决了本文开始提出的问题。
如果读者有更好的建议或解决办法,欢迎指教,我的Email是allencnj@163.com.