Search code examples
windowsdelphiwinapidelphi-10.2-tokyo

Changing window order in multi-form application


I have an application with some non-modal forms and each with its own icon. I need the icons of all forms on the taskbar that don't disappear on minimize/restore and after some testing, this is my solution.

Application

Application.Initialize;
Application.CreateForm(TForm1, Form1);
Application.Run;

TForm1 - Main form with one TButton

procedure TForm1.btn1Click(Sender: TObject);
begin
  TForm2.Create(Application).Show;
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
  SetWindowLong(Handle, GWL_EXSTYLE, WS_EX_APPWINDOW);
  Application.OnRestore := FormShow;
end;

procedure TForm1.FormShow(Sender: TObject);
begin
  ShowWindow(Application.Handle, SW_HIDE);
end;

TForm2

procedure TForm2.FormCreate(Sender: TObject);
begin
  SetWindowLong(Handle, GWL_EXSTYLE, WS_EX_APPWINDOW);
end;

This will create 2 icons on taskbar and 2 windows in Alt + Tab, both working as expected, except one thing...switching application moves all previous application windows before the current application window, not just one window.

For example, my application has main form and other non-modal form. If I'm in Google Chrome and press Alt + Tab, then this will appper, which is fine.

enter image description here

But this will move all of my application windows before Google Chrome and on next Alt + Tab I see this, so I have to pressAlt + 2x Tab to get back to the Chrome.

enter image description here

I would like to achieve this behavior as if I had more applications and not one with multiple windows.

enter image description here

I'm not sure how exactly it works, but I assume there are multiple lists in the background, one for all applications and one for the windows of the application, so when I switch the application, it moves in the list before the previous one and therefore all of its windows.

If this is how it works, is there an option to switch applications and not just windows? If not, is it possible to change the behavior to not move all the windows but only one active window or my whole process is wrong and the same effect can be achieved different way, where it works as it should?


Solution

  • Now that I understand your problem better, the problem is that TApplication also has a window and plays a role in the behaviour that you see. The solution is rather simple, make sure that ALL your top level windows have WS_EX_APPWINDOW EXCEPT TApplication. The second problem is that TApplication is the parent of these windows, not the desktop so you need to specify this.

    Form1:

    unit Unit1;
    
    interface
    
    uses
      Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
      Vcl.Controls, Vcl.Forms, Vcl.Dialogs;
    
    type
      TForm1 = class(TForm)
        procedure FormCreate(Sender: TObject);
      private
      strict protected
        procedure CreateParams(var Params: TCreateParams); override;
      public
      end;
    
    var
      Form1: TForm1;
    
    implementation
    
    {$R *.dfm}
    
    uses
      Unit2;
    
    { TForm1 }
    
    procedure TForm1.CreateParams(var Params: TCreateParams);
    begin
      inherited;
      Params.ExStyle := Params.ExStyle or WS_EX_APPWINDOW;
      // make desktop the owner, not the TApplication window
      Params.WndParent := GetDesktopWindow;
    end;
    
    procedure TForm1.FormCreate(Sender: TObject);
    begin
     SetWindowLong(Application.Handle, GWL_EXSTYLE,  GetWindowLong(Application.Handle,GWL_EXSTYLE) and not WS_EX_APPWINDOW or WS_EX_TOOLWINDOW);
     Form2 := TForm2.Create(Application);
     Form2.Show;
    end;
    
    end.
    

    Form2:

    unit Unit2;
    
    interface
    
    uses
      Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
      Vcl.Controls, Vcl.Forms, Vcl.Dialogs;
    
    type
      TForm2 = class(TForm)
      strict protected
        procedure CreateParams(var Params: TCreateParams); override;
      end;
    
    var
      Form2: TForm2;
    
    implementation
    
    {$R *.dfm}
    
    { TForm2 }
    
    procedure TForm2.CreateParams(var Params: TCreateParams);
    begin
      inherited;
      Params.ExStyle := Params.ExStyle or WS_EX_APPWINDOW;
      // make desktop the owner, not the TApplication window
      Params.WndParent := GetDesktopWindow;
    end;
    
    end.