As David Heffernan already said it is not possible to change the Main Form of an already running application. This is the limitation of the windows itself.
What you can do is cheat and never actually change the Main Form but only make it look like you did.
How do you achieve that?
Step 1: Add code to the second Form to make its own Taskbar button
procedure TWorkForm.CreateParams(var Params: TCreateParams);
begin
inherited;
Params.ExStyle := Params.ExStyle or WS_EX_APPWINDOW;
end;
Step 2: Dynamically create the second Form just before switching to it. Upon its creation previously added code will create a new Taskbar button for your second form.
Step 3: Now hide you actual Main Form. Hiding it will also hide the Taskbar button belonging to it. So you end up still having one Taskbar button shown and it is the one belonging to your second Form.
Step 4: In order to allow your second Form to terminate your application at its closure call Close method of your true Main Form from your second Forms OnClose or OnFormCloseQuery event.
If you wanna be able to switch back to your true Main Form call Show method of your Main Form instead of Close method.
This approach allows us of swapping forms pretty quickly so only most keen users will notice short animation of Taskbar button.
NOTE: If your second for is a complex one and because of that takes some time to create you might wanna create it hidden and then once its creation process is finished show it and do the swap. Otherwise you might end up with two Taskbar buttons being shown at same time which I believe you wanna avoid.
Here is a short example:
- LoginForm is a true Main Form that is created at the application startup
- WorkForm is the Form on which user will spend most of time after logging in and this one is created in 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.
Pointer((@Application.MainForm)^) := Form2;
. – RoughneckMainForm
is a property, not a variable. You cannot get the address of a property, so you are actually getting the address of the pointer that the property returns. And then you are modifying THAT pointer to point at a differet object, you are not modifying the pointer that is inside ofTApplication
itself. – GraveyardTRttiContext.Create.GetType(TApplication).GetField('FMainForm').SetValue(Application,ANewMainForm);
but i am not shure if it is "safer" or "better". the advantage is that i set the value of the variable and not the one of the property, but it is still dependent of the tapplication implementation. – RoughneckTApplication
sinceMainForm
directly readsFMainForm
. That statement successfully writes toFMainForm
. – RodartePointer((@Application.MainForm)^) := Form2;
should be the same asTmp := Application.MainForm; Pointer((@Tmp)^) := Form2;
- modifying a temp variable, not theTApplication.FMainForm
field. The only way what you say could be true is if the compiler is optimizing away the property read so the code becomesPointer((@Application.FMainForm)^) := Form2;
. – GraveyardTCompass.Heading
is same as the code generated forTCompass.FHeading
. – Rodarte