如何知道已经form已经建立了(50分)

  • 主题发起人 主题发起人 waterfish
  • 开始时间 开始时间
W

waterfish

Unregistered / Unconfirmed
GUEST, unregistred user!
如何知道已经form已经建立了
我在程序里写了段代码,是建立一个form然后show它,当这个form退出时
destroy。本来,这样做没有什么问题的,但是,我发现,因为使用了show
而不是showmodal所以用户可以进入主菜单里再执行该段程序,这样就又多了
一个同样的form出来。怎么能防止这种情况呢?
 
在你的form的OnClose事件中加上一句即可
procedure TForm1.FormClose(Sender: TObject;
var Action: TCloseAction);
begin
Action := caFree;
end;
 
When the application start,
you can add this code:
YourForm := Nil;
When you want to show it, you can use such code:
if (YourForm=Nil) then
YourForm := YourFormType.Create;
if (YourForm<>Nil) then
begin
YourForm.Show;
{ and let yourForm showForegroundWindow;}
end
else
begin
{ You should compain why it is so cold to you, :) }
end;
 
可以使用FindWindow 函数
具体用法:
If FindWindow('Form标题',nil) =0 then
//根据标题寻找窗口, 找不到时返会0
begin
//执行建立Form的过程
....
end;
 
SeaSky兄的有一点问题:有两个标题相同的创体?
FindWindow的第一个参数是窗口标题,第二个参数是窗口的类名,如果类名
保持一致就不会有问题了,所以应改成:
procedure N1Click(Sender:TObject);
begin

if FindWindow('Form1','TForm1') <> 0 then

begin

Form1:=TForm1.Create(Self);
end;
end;
 
SeaSky兄的有一点问题:有两个标题相同的创体?
FindWindow的第一个参数是窗口标题,第二个参数
是窗口的类名,找到后返回的Handle不为零.
如果类名保持一致就不会有问题了,
所以应改成:
procedure N1Click(Sender:TObject);
begin

if FindWindow('Form1','TForm1') <> 0 then

begin

Form1:=TForm1.Create(Self);

Form1.Show;.....
end;
end;
 
我明明记得FindWindow的第一个参数是ClassName,第二个参数是窗口标题.
而且找不到时返回值为0,所以应该是:
if FindWindow('TForm1','Form1')=0 then
begin
Form1:=TForm1.Create(Self);
Form1.Show;
end;

另外:如果没有什么特殊要求的话,你可以把子窗口FormStyle设为fsStayOnTop
这样,只有关闭子窗口才能回到父窗口,也就不会出现这样的问题了.
 
这个问题看起来很简单, 但是做起来并不简单;
按我前面所说,在TMyForm.OnClose
中令其Close Action为caFree, 然后用以下方法测试;
1.assigned(MyForm), form没显示时=true,显示时=true,free后=true
2.MyForm<>nil, true, true, true
3.FindWindow('FormName',nil), false, false, false
4.FindWindow(nil,'FormName'), true, true, true
5.FindWindow('FormName','FormName'), false, false, false
按理说其中的1,2,4应该得出正确的结果, true, true, false;
不知道是否Delphi
的bug? 我用的是D4,经过UpdatePack#1,UpdatePack#2升级
最后用FindComponent函数测试成功, 得到true,true,false:
if Application.FindComponent('FormName') then
...
else
...;
 
各位大侠的解决方法并不十分完善,小弟在http://www.netease.com/~ganglion/处
发现一个更好的解决办法.对防止主窗口的二次运行十分有用,且能将最小化的窗口
自动激活,估计稍做修改就可适合任意 Form ,为避免各位的麻烦现摘抄如下,waterfish 老兄也可自己去ganglion去看
1、对主窗口程序的改动:
在主窗口(即程序创建的第一个窗口)中interface节加入
const
CM_RESTORE = WM_USER + $1000;
{自定义的“恢复”消息}
MYAPPNAME = "My Delphi Program";
并在Form的定义的public节中加入
procedure CreateParams(var Params: TCreateParams);
override;
Procedure RestoreRequest(var message: TMessage);
message CM_RESTORE;
在implementation节中加入
{指定窗口名称}
procedure TForm1.CreateParams(var Params: TCreateParams);
begin
inherited CreateParams(Params);
Params.WinClassName := MYAPPNAME;
end;

{处理“恢复”消息}
procedure TForm1.RestoreRequest(var message: TMessage);
begin
if IsIconic(Application.Handle) = TRUE then
Application.Restore
else
Application.BringToFront;
end;

经过以上修改,程序的主窗口的类名已经被指定了,
这是进行判断的基础。一般在程序刚开始运行的时候进行判断,所以还要对DPR文件进行修改。
2、对DPR文件的改动
在 uses 节中添加 windows、messages这两个单元加入下列语句,
注意两个文件中常量CM_RESTORE和MYAPPNAME的定义必须一致
const
CM_RESTORE = WM_USER + $1000;
{自定义的“恢复”消息}
MYAPPNAME = "My Delphi Program";
var
RvHandle : hWnd;
将下列语句插到程序最前部(在Application.Initialize之前)
RvHandle := FindWindow(MYAPPNAME, NIL);
if RvHandle > 0 then
begin
PostMessage(RvHandle, CM_RESTORE, 0, 0);
Exit;
end;
这段程序的意思是如果找到一个类名相同的窗口,则向该窗口发送一个消息,并退出,
而本例中原窗口收到该消息后会自动激活或从图标还原,
从而达到了避免二次运行且能自动调出前一例程的目的。
 
我觉得大家对这个问题讨论的很深入,
但似乎有点跑题了.看看问题的最后两句话
就知道,pega的方法才是waterfish老兄所需要的.
 
我觉得最简单的方法是这样的:
当选择菜单打开Form以后,立即把这个菜单变灰。
不信,请重看waterfish的原题要求的是什么。
 
非常感谢大家给我这么多意见,令我一时不知所措了。本来我想这事情在delphi里
应该是再简单不过的事情了,谁知道会有这么多的...
在编写中我碰到了问题:当我定义了form后,例如abc : Tfrom
第一次用if abc=nil then
...判断的时候是没有错的,之后当然是建立这个form了
但这之后的的判断就再也不能得到正确的结果了,不知道为何
 
The reason may be:
After you free your Form, you did not assign a Nil to your variable
(like abc, you might add abc := Nil;
after abc.Destroy;
)
Or:
self.Destroy;
Self := Nil;
in your Form's cleanup procedure, :)
 
I must add a note:
That the method of disable the menu after use it is good. But I
did encounter a problem once that this method is no use. I remembered
that I disabled a button after user selected it. However, one best user
of my program click the mouse too quickly, then
windows prepared
two mouse click msg before the button really realized it was clicked,:(.
And that code is not "Reenterable", so my program crashed badly, :(~~~
 
add these lines before your other tasks. I can't believe there's a
person can as so quick to get 2 mouse click messages.
Button1.visible:=false;
Application.Processmessages;
 
子窗口的OnClose事件中定义一个Message. 比如:
const
MY_CLOSEMESSAGE = WM_USER+1;
procedure TMyForm.FormOnClose(Sender: TObject;
var Action: TCloseAction);
begin
if 可以关闭 then
PostMessage(MainForm.Handle, MY_CLOSEMESSAGE);
end;

主窗口中:
Type
TForm1= class(TForm)
....
procedure MyCloseMessage(var Msg: TMessage);message MY_CLOSEMESSAGE;
end;

implementation
procedure TForm1.MyCloseMessage(var Msg: TMessage);
begin
MyForm:=nil;
end;

这样你就可以用Assigned(MyForm)来判断是否需要重建form并show
不过最好还是由主窗来关闭子窗.
procedure TMyForm.FormOnClose(Sender: TObject;
var Action: TCloseAction);
begin
if 可以关闭 then
PostMessage(MainForm.Handle, MY_CLOSEMESSAGE);
Action:=acNone;
end;

procedure TForm1.MyCloseMessage(var Msg: TMessage);
begin
MyForm.Destroy;
MyForm:=nil;
end;
 
开始我还以为判断Form是否为nil有什么特殊意义呢,
原来在Close时还得再赋一个nil给它,那又和普通的Flag有什么分别?
用FindComponent也是可行的
我试了一下:
Form2:=TForm2.Create(Self);
ShowMessage(Form2.Name);
结果发现Form2.Name是'Form2_1',再执行是'Form2_2'...
所以使用FindComponent是要注意这一点
procedure TForm1.N1Click(Sender: TObject);
begin
if Application.FindComponent(MyFormName)=nil then
begin
Application.CreateForm(TForm2,Form2);
MyFormName:=Form2.Name;
Form2.Show;
end;
end;

还有就是在Form2的OnClose中也要加一句
procedure TForm2.FormClose(Sender: TObject;
var Action: TCloseAction);
begin
Action:=caFree;
end;
 
To: Another_eYes and eYes:
Hehe, when your application is busy, you can click a button many
times, for the windows has not pass your click to the button, the
click messages are stored in the application message squeue. When
your application is idle, it can get many mouse click message in a time.
So to disable a menu or let it invisible can not prevent user from
click it twice. You must add some flag in your code to prevent
this, :)
That's a lesson I learned from the cold fact, :). And it can be
understanded if you think about the mechanism of windows's message
system.
 
你可以在每打开一个窗口时在内存中用一个变量来记录.
在要打开一个窗口时先查一下这一个变量,如已打开的话
则用不着再打开,否则就打垲它.
具体做法为:
在主窗口中为各个窗口定义一个变量;
在每个窗口的OnCreate中为这个变量赋值.
在每个窗口的OnDestroy中为这个变量清值.
 
(转载)
Windows95的程序一般都可以重复执行,例如你按下WIN+E组合键即启动资
源管理器,如果再按WIN+E组合键又会出现一个资源管理器,这两个程序互不干
扰。有时候你可以需要制作这样一个程序:当该程序已经执行时,若用户企图
再次执行该程序则只会激活那个已执行的程序,而不是又出现一个副本。
  完成这个目的的核心就是要在程序启动时查找该程序是否已经运行,我曾
试过很多种方法,包括向“全局元素表”(Global ATOM Table)写特定字符串等
等,但最简单的方法还是下面这个:

在程序启动时将Application的Title特性字段的值暂时改变。
利用Windows API函数FindWindows()查找窗口
恢复Application的Title值

  上述步骤一般在主Form的OnCreate事件中实现,示例如下:

procedure TForm1.FormCreate(Sender: TObject);
var
ZAppName: array[0..127] of char;
Hold: String;
Found: HWND;
begin

Hold := Application.Title;
Application.Title := 'OnlyOne'
+ IntToStr(HInstance);
// 暂时修改窗口标题
StrPCopy(ZAppName, Hold);
// 原窗口标题
Found := FindWindow(nil, ZAppName);
// 查找窗口
Application.Title := Hold;
// 恢复窗口标题
if Found<>0 then
begin

// 若找到则激活已运行的程序并结束自身
ShowWindow(Found, SW_RESTORE);
Application.Terminate;
end;

end;
 

Similar threads

后退
顶部