Delphi Changing the main form while the application is running

I have this problem. When I hide my main form, the taskbar icon of my application is also hidden. I also saw a new question about this problem, and the answers did not help. They suggested minimizing it, but I donโ€™t want to minimize the application.

Is it possible to change the main form while the application is already running?

eg. I have two forms. when I want to hide one form and show another form, the taskbar icon should remain on the taskbar, and the main form should switch to another form.

I am using Delphi XE6 and this is a VCL Forms application.

I also saw another old question about changing the main form at runtime, but it's very old and still based on Delphi 6.

+5
source share
8 answers

As David Heffernan has already said, itโ€™s impossible to change the basic form of an already running application. This is a limitation of the windows themselves.

What you can do is cheat and never change the basic shape, but just make it look like you. How do you achieve this?

Step 1: Add code to the second form to create your own taskbar button

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

Step 2: Dynamically create a second form just before switching it. After creating it, the previously added code will create a new taskbar button for your second form.

Step 3: Now hide the real base shape. Hiding will also hide his taskbar button. That way, you still show one taskbar button, and it belongs to your second form.

Step 4:. To have your second form terminate your application when it closes, call the Close your true main form method from your second Forms OnClose or OnFormCloseQuery event.
If you want to return to the true call to the main form, select the "My main form" method instead of the "Close" method.

This approach allows us to quickly change forms, so only the sharpest users will notice a short animation of the "Taskbar" button.
NOTE. If your second option is complex, and because of this it takes some time to create, you can create it hidden, and then, as soon as its creation process is completed, show it and exchange it. Otherwise, you may end up showing two taskbar buttons that I think you want to avoid.

Here is a quick example:
- LoginForm is the real basic form created when the application was launched - WorkForm is the form by which the user will spend most of the time after logging in, and this file is created during the login process.

Login Form Code:

 unit ULoginForm; interface uses Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls; type TLoginForm = class(TForm) BLogIn: TButton; procedure BLogInClick(Sender: TObject); private { Private declarations } public { Public declarations } end; var LoginForm: TLoginForm; //Global variable to tell us if we are only logging out or closing our program LoggingOut: Boolean; implementation uses Unit2; {$R *.dfm} procedure TLoginForm.BLogInClick(Sender: TObject); begin //Create second Form Application.CreateForm(TWorkForm, WorkForm); //Hide Main Form Self.Hide; //Don't forget to clear login fields end; end. 

Work form code:

 unit UWorkForm; interface uses Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls; type TWorkForm = class(TForm) BLogOut: TButton; //Used in overriding forms creating parameters so we can add its own Taskbar button procedure CreateParams(var Params: TCreateParams); override; procedure FormCloseQuery(Sender: TObject; var CanClose: Boolean); procedure BLogOutClick(Sender: TObject); private { Private declarations } public { Public declarations } end; var WorkForm: TWorkForm; implementation uses Unit1; {$R *.dfm} procedure TWorkForm.BLogOutClick(Sender: TObject); begin //Set to true so we know we are in the process of simply logging out LoggingOut := True; //Call close method to begin closing the current Form Close; end; procedure TWorkForm.CreateParams(var Params: TCreateParams); begin inherited; Params.ExStyle := Params.ExStyle or WS_EX_APPWINDOW; end; procedure TWorkForm.FormCloseQuery(Sender: TObject; var CanClose: Boolean); begin //Check to see if we are in the process of simply logging out if not LoggingOut then begin //If we are not in the process of logging out close the Main Form LoginForm.Close; //and then allow closing of current form CanClose := True; end else begin //But if we are in the process of simply logging out show the Main Form LoginForm.Show; //Reset the LoggingOut to false LoggingOut := False; //and then alow closing of current form CanClose := True; end; end; end. 
+2
source

Is it possible to change the main form while the application is already running?

It is not possible to change the main form of VCL while the program is running. This property is determined once and for all when the program starts.

One of the possible ways for you is to organize an additional form, which is not the main form, to have a button on the taskbar. Do this by making it unoccupied or using the WS_EX_APPWINDOW style of the extended window.

Update

Well, you can change Application.MainForm , but for this you need to destroy the current main form, and then create a new one.

+4
source

Unable to modify Application.MainForm after its assignment. However, you do not need this either. The simplest solution to this problem is to create an empty hidden TForm that will act like a real Application.MainForm and let it control the taskbar normally, and then, if necessary, you can show / hide any secondary TForm objects where your desired "MainForm" is secondary form, not real MainForm .

+3
source

If you set Application.MainFormOnTaskbar to false in your launcher (in the .dpr file), then VCL will create a hidden form whose sole purpose is to provide a taskbar icon. This is an older approach and is usually not recommended, but while other windows are visible, it will allow you to hide the main form without the application disappearing from the taskbar.

You can also provide an Application.OnGetMainFormHandle event handler to change Application.MainFormHandle at run time (but not Application.MainForm ). MainFormHandle affects things like the owner of modal popup dialogs.

More information about Application.MainFormOnTaskbar and the disadvantages of disabling it: it gets complicated in a hurry. A short version is in the VCL docs , which explains that some of the new Windows features (such as taskbar thumbnails) that were introduced in Vista require MainFormOnTaskbar := True . There's a lot more background reading in this SO discussion .

+2
source

I implemented this in the same way that @DavidHeffernan had already suggested, and so far I have not encountered any problems, this may not be the best way, but it worked for me and what I was trying to achieve, which had a โ€œnormal" "feeling of behavior when the user minimized their MainWork form.

The code looked something like this:

 procedure TfrmLogin.btnLoginClick(Sender: TObject); begin frmMainWork := TfrmMain.Create(Application); try Pointer((@Application.MainForm)^) := frmMainWork; frmLogin.Hide; frmMainWork.ShowModal; finally Pointer((@Application.MainForm)^) := frmLogin; frmLogin.Show; FreeAndNil(frmMainWork); end; end; 

hope this helps :-)

+2
source

You can change the main form. Make the variable F: ^ TForm, then set it to @ Application.MainForm. After that, you can set the main form as through F ^: = YourAnotherForm.

0
source

I had an additional problem with Delphi XE2 MDI applications, which are also COM servers. My main Main form calls the secondary MenuForm form, which contains common icons and pop-up menus for the entire application.

In my example, the application works fine when run offline.

Main is created, and then a MenuForm is created at the end of the FormCreate. All is well.

But when called from the COM server, Main.FormCreate is first called, but somehow, MenuForm is assigned to Application.MainForm. There is a race condition in basic RTL. This leads to chaos when trying to create the first SDI file, because Application.MainForm is not an MDI.

I tried to get around this with Main.FormCreate, sending a message back to myself to defer the creation of MenuForm until Main.FormCreate returns.

But there is still a race condition - MenuForm was still assigned to Application.MainForm.

In the end, I managed to get around this with the code to poll Application.MainForm every 10 ms, maximum 10 seconds. I also had to remove any link in the Main to MenuForm list (in .dfm) until the MenuForm was created explicitly - otherwise the MenuForm would be created implicitly at the end of MainForm.create.

Hope this helps someone!

 const CM_INITIAL_EVENT = WM_APP + 400; TmainForm = class(TForm) ... procedure afterCreate(var Message: TMessage); message CM_INITIAL_EVENT; ... end; procedure TmainForm.FormCreate(Sender : TObject); begin ... ...standard init code ... postmessage( handle, CM_INITIAL_EVENT, 0, 0 ); End; procedure TmainForm.AfterCreate(var Message: TMessage); var i: Integer; begin //must assign these AFTER menuform has been created if menuForm = nil then begin //wait for mainform to get assigned //wait up to 10*1000ms = 10 seconds for i := 0 to 1000 do begin if Application.Mainform = self then break; sleep(10); end; Application.CreateForm(TmenuForm, menuForm); menuForm.parent := self; end; //NOW we can assign the icons Linktothisfilterfile1.SubMenuImages := menuForm.treeIconList; ActionManager.Images := menuForm.treeIconList; allFilters.Images := menuForm.treeIconList; MainMenu.Images := menuForm.treeIconList; ... end; 
0
source

In the current Delphi implementation, I am sure that there are no consequences with a pointer to a change in Application.MainForm.

If you look in the TApplication class, you will see that FMainForm is only used to check if the application has at least one form and iterates through the main loop inside the TApplication.Run method while FMainForm exists. If you do not want to use a pointer to crack this property, you can implement your own TApplication class, for example TMyApplication, copy all the routines inside it and define the MainForm property for reading and writing

0
source

Source: https://habr.com/ru/post/1201824/


All Articles