Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to not have a MainForm in Delphi?

i've been trying to get some modeless forms in my application to appear on the taskbar - taking advantage of the new useful taskbar in Windows 7.

There's are many issues with the VCL that need to be undone before a form can exist on the taskbar.

But the final issue is that minimizing the form that the VCL has designated the main form causes all windows in the application to vanish.

Ten years ago, Peter Below (TeamB) documented these problems, and attempts to work around them. But there are some issues that cannot be solved. The issues run so deep within the VCL itself, that it's effectively impossible to make Delphi applications behave properly.

It all stems from the fact that the button you see on the toolbar does not represent the application's window; it represents the TApplications window, which is hidden and never seen. And then there is the application's MainForm, which is then imbued with special abilities where if it is minimized then it instructs the application to hide itself.

It seems to me that if i can do

Application.MainForm := nil;

then all these bugs would go away. The application can have its hidden window, and in the meantime i'll override every other form in the application, including my main form, with:

procedure TForm2.CreateParams(var params: TCreateParams ); 
begin 
   inherited CreateParams(params); 
   params.ExStyle := params.ExStyle or WS_EX_APPWINDOW; 
end; 

But in Delphi the Application.MainForm property is read-only.

How can i not have a MainForm in Delphi?

See also

  • (stackoverflow) Delphi: What is Application.Handle?
  • (newsgroup) Hiding Main Window but not child
like image 254
Ian Boyd Avatar asked Oct 21 '10 13:10

Ian Boyd


3 Answers

You cannot run a GUI project without a MainForm assigned. The main message loop will exit immediately without one. However, that does not mean that the MainForm has to run your UI. You can use a blank hidden TForm as the assigned MainForm, and then have it instantiate your real MainForm as a secondary TForm. For example:

HiddenMainFormApp.dpr:

project HiddenMainFormApp;

uses
  ..., Forms, HiddenMainForm;

begin
  Application.Initialize;
  Application.CreateForm(THiddenMainForm, MainForm);
  Application.ShowMainForm := False;
  Application.Run;
end.

HiddenMainForm.cpp:

uses
  ..., RealMainForm;

procedure THiddenMainForm.FormCreate(Sender: TObject);
begin
  RealMainForm := TRealMainForm.Create(Self);
  RealMainForm.Show;
end;

RealMainForm.cpp:

procedure TRealMainForm.FormClose(Sender: TObject; var Action: TCloseAction);
begin
  Action := caFree;
  Application.Terminate;
end;

Alternatively:

HiddenMainFormApp.dpr:

project HiddenMainFormApp;

uses
  ..., Forms, HiddenMainForm, RealMainForm;

begin
  Application.Initialize;
  Application.CreateForm(THiddenMainForm, MainForm);
  Application.ShowMainForm := False;

  RealMainForm := TRealMainForm.Create(Application);
  RealMainForm.Show;
  RealMainForm.Update;

  Application.Run;
end.

RealMainForm.cpp:

procedure TRealMainForm.FormClose(Sender: TObject; var Action: TCloseAction);
begin
  Action := caFree;
  Application.Terminate;
end;
like image 193
Remy Lebeau Avatar answered Sep 18 '22 16:09

Remy Lebeau


You can't, especially in Delphi 5.

Your quote concerning the TApplication window being the one seen on the task bar hasn't been true for several Delphi versions now (I believe D2007 changed it).

Because you're using Delphi 5, you're using an outdated copy of Delphi; current versions have almost none of the things you're writing about any longer. I'd suggest you upgrade to a later version of Delphi (D5 is extremely old); Delphi 2007 if you need to avoid Unicode, Delphi XE if you can use (or don't mind having) Unicode support in the VCL and RTL.

The things you're describing are not bugs, BTW. They were intentional design decisions made at the time Delphi 1 was being designed, and through Delphi 7 worked fine with the versions of Windows that were available. Changes in later versions of Windows (XP/Vista/Win7 and the equivalent Server versions) made changes in that architecture necessary, and they were made as Delphi progressed along with Windows. Because you've chosen not to progress with your version of Delphi to keep it more recent doesn't make the things you write about magically become bugs. :-)

like image 35
Ken White Avatar answered Sep 19 '22 16:09

Ken White


Having Application.MainForm assigned seems not to be a problem here for showing another modeless form on the taskbar while minimizing the MainForm.

Project1.dpr:

program Project1;

uses
  Forms,
  Windows,
  Unit1 in 'Unit1.pas' {MainForm},
  Unit2 in 'Unit2.pas' {Form2};

{$R *.res}

var
  MainForm: TMainForm;

begin
  Application.Initialize;
  Application.CreateForm(TMainForm, MainForm);
  ShowWindow(Application.Handle, SW_HIDE);
  Application.Run;
end.

Unit1.pas:

unit Unit1;

interface

uses
  Windows, Messages, Classes, Controls, Forms, StdCtrls, Unit2;

type
  TMainForm = class(TForm)
    ShowForm2Button: TButton;
    ShowForm2ModalButton: TButton;
    procedure ShowForm2ButtonClick(Sender: TObject);
    procedure FormCreate(Sender: TObject);
    procedure ShowForm2ModalButtonClick(Sender: TObject);
  private
    FForm2: TForm2;
    procedure ApplicationActivate(Sender: TObject);
    procedure Form2Close(Sender: TObject; var Action: TCloseAction);
    procedure WMSysCommand(var Msg: TWMSysCommand); message WM_SYSCOMMAND;
  protected
    procedure CreateParams(var Params: TCreateParams); override;
  end;

implementation

{$R *.dfm}

procedure TMainForm.FormCreate(Sender: TObject);
begin
  Visible := True; //Required only for MainForm, can be set designtime
  Application.OnActivate := ApplicationActivate;
end;

procedure TMainForm.ApplicationActivate(Sender: TObject);
{ Necessary in case of any modal windows dialog or modal Form active }
var
  TopWindow: HWND;
  I: Integer;
begin
  TopWindow := 0;
  for I := 0 to Screen.FormCount - 1 do
  begin
    Screen.Forms[I].BringToFront;
    if fsModal in Screen.Forms[I].FormState then
      TopWindow := Screen.Forms[I].Handle;
  end;
  Application.RestoreTopMosts;
  if TopWindow = 0 then
    Application.BringToFront
  else
    SetForegroundWindow(TopWindow);
end;

procedure TMainForm.CreateParams(var Params: TCreateParams);
begin
  inherited CreateParams(Params);
  with Params do
  begin
    ExStyle := ExStyle or WS_EX_APPWINDOW;
    WndParent := GetDesktopWindow;
  end;
end;

procedure TMainForm.WMSysCommand(var Msg: TWMSysCommand);
begin
  if Msg.CmdType = SC_MINIMIZE then
    ShowWindow(Handle, SW_MINIMIZE)
  else
    inherited;
end;

{ Testing code from here }

procedure TMainForm.ShowForm2ButtonClick(Sender: TObject);
begin
  if FForm2 = nil then
  begin
    FForm2 := TForm2.Create(Application); //Or: AOwner = nil, or Self
    FForm2.OnClose := Form2Close;
  end;
  ShowWindow(FForm2.Handle, SW_RESTORE);
  FForm2.BringToFront;
end;

procedure TMainForm.Form2Close(Sender: TObject; var Action: TCloseAction);
begin
  Action := caFree;
  FForm2 := nil;
end;

procedure TMainForm.ShowForm2ModalButtonClick(Sender: TObject);
begin
  with TForm2.Create(nil) do
    try
      ShowModal;
    finally
      Free;
    end;
end;

end.

Unit2.pas:

unit Unit2;

interface

uses
  Windows, Messages, Classes, Controls, Forms;

type
  TForm2 = class(TForm)
  private
    procedure WMSysCommand(var Msg: TWMSysCommand); message WM_SYSCOMMAND;
  protected
    procedure CreateParams(var Params: TCreateParams); override;
  end;

implementation

{$R *.dfm}

procedure TForm2.CreateParams(var Params: TCreateParams);
begin
  inherited CreateParams(Params);
  with Params do
  begin
    ExStyle := ExStyle or WS_EX_APPWINDOW;
    WndParent := GetDesktopWindow;
  end;
end;

procedure TForm2.WMSysCommand(var Msg: TWMSysCommand);
begin
  if Msg.CmdType = SC_MINIMIZE then
    ShowWindow(Handle, SW_MINIMIZE)
  else
    inherited;
end;

end.

(Tested with D5 and D7 on XP and Win7.)

(And yes, you may flag this as being not an answer, because it isn't: There still is a MainForm. But I dó like to think this answers the question behind the question...)

like image 45
NGLN Avatar answered Sep 18 '22 16:09

NGLN