就是如何避免多次打开同一个窗口的问题(非模态窗);
方法有很多,但是有一个要求是 重新 打开的窗口必须是全新的,不是上次关闭时的状态;
我是这样写代码的,虽然功能上能实现,总觉得别扭和烦琐,而且还有隐患,如果碰到别的程序开了一个同名窗的话.......!
procedure TForm1.Button1Click(Sender: TObject);
var aaa:integer;
begin
  aaa := FindWindow('TForm2','Form2');
  showmessage(inttostr(aaa));
  if FindWindow('TForm2','Form2')= 0 then begin
    form2 := Tform2.Create(form1) ;
    form2.Show;
  end else begin
    form2.Free;
    form2 := Tform2.Create(form1) ;
    form2.Show;
  end;
end;快拿出最好的方法来吧!!!大家一起进步PS(还有一个小小问题,都知道创建为子窗口那么子窗被主窗包含了,可以把子窗创建为弹出吗?就是CreateParent方法建的)

解决方案 »

  1.   

    避免重复创建无模式窗体作者: 黄  勇
    日期: 2004-9-28
      MainForm : TMaiForm;      //主窗体
      ChildForm: TChildForm;    //子窗体.
      首先出于性能的考虑,取消所有子窗体的自动创建。菜单"project"-"options"-"Forms"。从"Auto-create forms" 列表框中移除自动创建的子窗体。
      
      第一种情况:  子窗体为全局对象。
      
    // 检查无模式窗体的实例是否已经存在
    // if ChildForm=nil then begin            // 此句用下面语句替代.
    if not Assigned(ChildForm) then begin     
      ChildForm := TChildForm.Create(Application);   // 创建窗体
      ChildForm.Show;                                // 显示无模式窗体
    end;
      
      要特别注意窗体的实例: 当通过窗体的系统菜单或者窗体上的Close 按钮关闭这个窗体时,窗体并没有真正从内存中释放。它仍然还在内存中,除非关闭了主窗体(即应用程序)。在上面这个程序示例中,then后面的语句只会执行一次,前提是这个窗体不是自动创建的。如果希望用户关闭了窗体就在内存中释放它,必须处理它的OnClose 事件,并且把Action 参数设为caFree ,这样,VCL 就会在这个窗体关闭时释放它。procedure TChildForm.OnClose(Sender: TObject; var Action: TCloseAction);
    begin
      Action := caFree;                         // 当关闭时,释放窗体实例
    end;  上述代码解决了窗体实例在内存释放的问题。不过,还有一个问题,注意下面这行代码: 
      if not Assigned(ChildForm) then begin   
      这行代码检查TChildForm 的实例是否已经由ChildForm 变量引用,这实际上就是检查ChildForm 是否为nil 。尽管第一次进入例程的时候,ChildForm 可能是nil ,但第二次进入这个例程的时候,它已经不是nil 。  这是因为VCL 并没有把ChildForm 变量设为nil 。因此,必须手工把这个变量设为nil 。与模式窗体不同的是,无法在代码中判断无模式窗体什么时候将删除。因此,无法在创建窗体实例的例程中删除窗体的实例。用户有可能在应用程序正在运行的任何时候关闭无模式窗体。因此,无模式窗体本身一定要把ChildForm变量设为nil,而且最好在处理窗体的
    OnDestroy 事件的处理过程中设置这个变量:procedure TChildForm.OnDestroy(Sender: TObject);
    begin
      ChildForm := nil;
    end;  这样就能保证每次关闭窗体时,ChildForm 变量总是被设为nil ,从而防止Assigned()函数失败。
      第二种情况: 子窗体不是全局对象。
      
      基于封装的考虑,我们删除了TChildForm 所在单元的ChildForm 全局声明:
      { 注释掉
      var  
        ChildForm: TChildForm;
      } 
      此时,第一种情况时将ChildForm 设置为nil 的方式就不能用了。  那么就可以用到 Screen.Forms了
      尽管通过Screen.Forms可以访问这些实例,但最好还是尽量避免使用上述代码,Screen上的窗体也太多了。
     
    var
      i: Integer;
      ChildForm: TChildForm;
    begin
      for i:=0 to Screen.FormCount-1 do
        if Screen.Forms[i].classtype=TChildForm then begin
          Screen.Forms[i].Show;
          Exit;
        end;
      ChildForm := TChildForm.Create(Application);
      ChildForm.Show;
    end;  更好的办法是:用MDIChildren 和 MDIChildCountvar
      ChildForm: TChildForm;
      i: Integer;
    begin
      for i:=MDIChildCount-1 downto 0 do
        if MDIChildren[i].ClassType = TChildForm then begin
          MDIChildren[i].Show;
          exit;
        end;
      ChildForm := TChildForm.Create(Application);
      ChildForm.Show;
    end;
      
      但是,有个问题,子窗体创建的子窗体,在使用上面的代码时,必须引用主窗体单元,这就涉及到相互引用。怎样避免?
      单独写一个控制器单元控制子窗体的创建与显示可以避免子窗体之间的相互引用,但控制器单元与主窗体的相互引用无法避免。另外,如果子窗体重载了Create方法,则控制器单元不能写出通用的方法。
        上面分两种情况介绍了怎样避免重复创建无模式窗体。笔者有个疑问:  Delphi 5 开发人员指南 4.4.1中有一句话"向构造器Create() 传递nil 造成在ChildForm 实例变量被覆盖后,无法再引用这个窗体实例指针。"
      即,不要采用 ChildForm := TChildForm.Create(nil) 的方式。
      什么意思,为什么?[参考] Delphi 5 开发人员指南 4.4.1 TForm类
      

  2.   

    前2天看到一个mvc 的delphi 实现,应该可以避免上面的一些问题。 还没研究。
      

  3.   

    我觉得assigned也避免不了窗口同名的问题
    如果应用程序被开启了两个实例,那么很有可能你这个窗口就打不开了
      

  4.   

    if not Assigned(Form1) then
        Application.CreateForm(TForm1,Form1);
        Form1.Show;
      

  5.   

    ————
    我觉得assigned也避免不了窗口同名的问题
    如果应用程序被开启了两个实例,那么很有可能你这个窗口就打不开了————
    后面有相应解决方法。
    ----
    PS(还有一个小小问题,都知道创建为子窗口那么子窗被主窗包含了,可以把子窗创建为弹出吗?就是CreateParent方法建的)
    ----
    不太明白。CreateParent没用过。 有这个方法吗?好像有个CreateParented
      

  6.   

    <<即,不要采用 ChildForm := TChildForm.Create(nil) 的方式。
      什么意思,为什么?
    ----------------------------------------------------------
    我也想知道 :P)
      

  7.   

    ----
    PS(还有一个小小问题,都知道创建为子窗口那么子窗被主窗包含了,可以把子窗创建为弹出吗?就是CreateParent方法建的)
    ----
    不太明白。CreateParent没用过。 有这个方法吗?好像有个CreateParented
    ----
    对,打快了 就是你说的方法
      ChildForm := TChildForm.CreateParented(MainForm );
      ChildForm.Show;怎么建立为弹出子窗口
      

  8.   

    你应该这样写:
    procedure TForm1.Button1Click(Sender: TObject);
    begin
      if IsWindow(H_main) then
        form2.Free;
      form2 := Tform2.Create(form1);
      H_main := Tform2.Handle;
      form2.Show;
    end;