问题的出发点很简单,我想要知道应用程序中有没有弹出过模态窗体,程序中有十几个窗体会调用ShowModal弹出,每次都记录下来,代码很散乱,显然不是个好主意,开始想怎么弄,搞出三种方法来
(1)遍历程序中所有窗体法
function ApplicationHasModalForm:Boolean;
var
i:integer;
begin
  result:=false;
  for i:=0 to Application.ComponentCount-1 do
  begin
    if Application.Components[i] is TForm then
    begin
      if fsModal in (Application.Components[i] as TForm).FormState then
      begin
        result:=true;
        break;
      end;
    end;
  end;
end;
这个方法有一个最大的问题,它要求所有窗体都必须是用Application.CreateForm创建的。如果窗体是自己手工的,比如Form2:=TForm.Create(nil),就不会检测到Form2。(2)准备捣腾ShowModal的源码,打开function TCustomForm.ShowModal: Integer;看看,大部分看不懂,但是看到两句有意思的:
  Application.ModalStarted;
  Application.ModalFinished;
看看这两个函数都干吗了:
procedure TApplication.ModalStarted;
begin
  Inc(FModalLevel);
  if (FModalLevel = 1) and Assigned(FOnModalBegin) then
    FOnModalBegin(Self);
end;procedure TApplication.ModalFinished;
begin
  Dec(FModalLevel);
  if (FModalLevel = 0) and Assigned(FOnModalEnd) then
    FOnModalEnd(Self);
end;它改变了一个个变量的值,好像是计数的,这个太好了,我只需要判断FModalLevel=0的结果就能知道程序是否有模态窗口弹出。
但是这招不行,FModalLevel是一个私有变量,无法获取。
再看后面的,这个两个函数还判断了Application的OnModalBegin和OnModalEnd事件有没有写代码,如果有,就执行之。
这里提一下,Delphi帮助文件的两个错误,帮助说OnModalBegin在弹出一个模态窗体时触发,实际是不对的,通过源码我们发现它仅仅在弹出第一个模态时触发。如果窗体A模态显示窗体B,窗体B在模态显示窗体C,第2次是不会触发的。帮助说OnModalEnd在模态窗体关闭时触发,实际也是不对的,它仅仅在最后一个模态窗体关闭时才触发。这就好办了:
设一个全局变量,ApplicationHasModalForm,在Application的OnModalBegin事件中将其设为True,在OnModalEnd事件中将其设为False即可。(3)还是觉得这个方法太古怪,有没有简单的。
程序弹出一个模态窗体,应该是将其余窗体全部Disable了,这么判断可以的吗?
比如我的主窗体叫MainForm
function ApplicationHasModalForm:Boolean;
begin
  result:=MainForm.Enabled;
end;
实际这不行,弹出模态窗体后,MainForm.Enabled还是True。
这样是可行的,必须调用Windows API
function ApplicationHasModalForm:Boolean;
begin
  result:=IsWindowEnabled(MainForm.Handle);
end;
这种方式虽然简单,但是有缺陷,必须你的程序没有自己Disable主窗体的代码,如果除了弹出模态窗体外,别的情况下字节也会disable主窗体,就不能这么判断了。