Disable Application.CreateForm with new form - delphi

I have a function which checks the settings and permissions before the start of the application, and if everything goes through, it selects what version to run and accordingly changes the main form.
function SomeControlFunction: Boolean;
var
lMainForm : TForm;
begin
if SomePermission then
Application.CreateForm(TUForm1, lMainForm)
else
Application.CreateForm(TUForm2, lMainForm);
end;
Project.dpr
Application.Initialize;
if SomeControlFunction then
Application.Run;
Unfortunately every time I create a new form in the project, it automatically adds to Project.dpr and I have to delete it every time. Is there any way to disable this behavior or is the whole process wrong and I should run the application differently?
Application.Initialize;
if SomeControlFunction then
Application.CreateForm(TUNewForm, UNewForm);
Application.Run;

There is a work around to prevent the IDE from changing the dpr-file in that way.
It seems that the Delphi IDE will explicitly look for places where the global variable Application from Vcl.Forms is used in the dpr-file and accordingly add the CreateForm calls.
The standard template code in the dpr-file looks like this:
Application.Initialize;
Application.MainFormOnTaskbar := True;
Application.CreateForm(TForm1, Form1);
<-- new forms will be added here automatically
Application.Run;
If you use an 'alias' variable - lets say App - instead, the IDE will not interfere. Replace your existing code in the dpr-file with following:
var
App: TApplication;
begin
App := Application;
App.Initialize;
if SomeControlFunction then
App.Run;
end.
Adding new Forms won't automatically add CreateForm calls in your dpr-file now.

Is there any way to disable this behavior?
In the options dialog, select the Form Designer node, and uncheck the Auto create forms & data modules option.
The documentation for this option states:
Toggles whether or not to automatically create forms. When unchecked, forms added to the project after the first one are put into the Available Forms list rather than the Auto Create list. You can change where each form is listed by choosing Project > Options > Forms.
What the documentation does not tell you is that this option is ignored when you add a new form to a project that does not already contain at least one auto-create form. So it's probably not going to be very useful for you.
By the way, SomeControlFunction does not set the return value.

Related

How to split the code for a large GUI?

I have a program with a large GUI. The GUI is split in several tabs.
The code for managing the GUI (for example one tab contains a full blown Windows Explorer clone) is pretty large.
Which will be the best approach for splitting such a large GUI in multiple files but without having the GUI split in multiple forms (at run time)?
Use embedded forms. This way you can maintain each one in a separate file, and at run-time they all appear to be part of the same GUI form.
Create a global variable to store the current form:
MyForm: TForm;
You don't want to auto-create the forms, so remove them from the project options -> Forms auto-create list. Instead, create them dynamically this way:
MyForm := TMyForm.Create(Application);
Then set the properties as needed, including the following:
I'm assuming you have a panel named something like EmbeddedMyForm_panel on the tabsheet where you want to embed the form. That's what I do, anyway. You could also probably use the TTabSheet directly.
with MyForm do begin
BorderIcons := [];
BorderStyle := bsNone;
parent := EmbeddedMyForm_panel;
Align := alClient;
Visible := true;
end;
I've worked on numerous projects that used this approach very successfully to embed separate forms on tons of tabs inside of one massive GUI.
ADDED: When I've asked why they didn't use frames instead, I was told that with a dozen or more embedded forms on the main form, loading it up with frames would take forever because the IDE would complain about not being able to find the ancestor form for virtually every frame on the form. You need to open all of the frame forms first in order to open the main form without getting any warnings from the IDE. Which is particularly annoying if you simply want to work on the main form itself (eg. edit the main menus) and don't need to deal with any of the frames at all.

how to create MDI form in delphi

How can I solve this problem "Cannot Create Forms. No MDI forms are currently active". I want to make a simple program that wod require a Login Form before it can acess the main form. I got three forms: Main Form (MDI Form), Form2 (MDIChild) and Login Form (Normal). Login form would appear first then the Main Form, then when I try to call on Form 2 from the Main form, an error would display "Cannot Create Forms. No MDI forms are currently active".
I am using Delphi 7 on windows XP. I'm a beginner. Thank you very much sir.
It sounds like you're letting your LoginForm be auto-created, and it's being created first. This won't work, because the first form created by Application.CreateForm in the project file becomes the Application.MainForm. In order by be an MDI application, the MainForm must be a MDI parent window.
The solution is usually to not auto-create your login form, and instead create it yourself. To do so, you need to edit your .dpr file (Project->View Source from the IDE's main menu).
Your project source should look something like this now (obviously, using your classes in the Application.CreateForm calls):
begin
Application.Initialize;
Application.CreateForm(TLoginForm, LoginForm);
Application.CreateForm(TMainForm, MainForm);
Application.CreateForm(TChildForm, ChildForm);
Application.Run;
end.
You need to modify it so that the LoginForm isn't created first.
var
LoginOK: Boolean = False;
begin
LoginForm := TLoginForm.Create(nil);
try
// Show login form. When it closes, see if login worked.
LoginForm.ShowModal;
LoginOK := LoginForm.CanLogin; // However you know login is OK or not here
finally
LoginForm.Free;
end;
if not LoginOK then
Halt; // Login failed - terminate application.
Application.Initialize;
Application.CreateForm(TMainForm, MainForm);
{
I normally do not auto-create anything but the main form
and maybe a datamodule (which you **can** autocreate first -
it is not a form), but a MDI application is pretty useless
without at least one open child window, IMO.
}
Application.CreateForm(TChildForm, ChildForm);
Application.Run;
end.

Make 2 forms able to overlap each other?

I would like to have a seperate form that shows "along" with my main form, so it does not overlap the main form.
Here's an example:
Notice how the main program, overlaps the log? I can't figure out how to do that in Delphi.
Thanks!
The answers to this question lie in the very useful Window Features MSDN topic.
The pertinent information is:
An overlapped or pop-up window can be
owned by another overlapped or pop-up
window. Being owned places several
constraints on a window.
An owned window is always above its owner in the z-order.
The system automatically destroys an owned window when its owner is
destroyed.
An owned window is hidden when its owner is minimized.
The main form in your app is the owner (in Windows terminology rather than Delphi terminology) of the other popup windows. The first bullet point above implies that the owned windows always appear above the main form (the owner).
Try creating an app with 3 forms and show them all. The .dpr would look like this:
program OwnedWindows;
uses
Forms,
Main in 'Main.pas' {MainForm},
Popup1 in 'Popup1.pas' {PopupForm1},
Popup2 in 'Popup2.pas' {PopupForm2};
{$R *.res}
begin
Application.Initialize;
Application.MainFormOnTaskbar := True;
Application.CreateForm(TMainForm, Main);
Application.CreateForm(TPopupForm1, PopupForm1);
Application.CreateForm(TPopupForm2, PopupForm2);
PopupForm1.Show;
PopupForm2.Show;
Application.Run;
end.
You will see that the main form is always underneath the other two forms, but these other owned forms can be above or below each other. When you minimize the main form they all disappear.
You could if you want make all of your forms top-level unowned windows:
procedure TPopupForm1.CreateParams(var Params: TCreateParams);
begin
inherited;
Params.WndParent := 0;
end;
And like wise for TPopupForm2 in my example. This would result in all 3 windows having taskbar buttons.
One other approach is to revert to the pre-Vista way of things and make the Application's hidden window be the top-level owner window. You do this by making sure that Application.MainFormOnTaskbar is False. Skip all the CreateParams code and you'll now have a single window on the taskbar and any of your windows can be above any other because the top-level owner window is the hidden window Application.Handle. Of course the downside is that you lose your Aero Peek.
So, I guess what you need to do is to make the main form appear on the taskbar as usual, but ensure that the other forms are not owned (in the Windows sense) by the main form. But they need to be owned to avoid having them in the taskbar. So you can make the hidden application window be the owner using the CreateParams method, like so:
procedure TOverlappedPopupForm.CreateParams(var Params: TCreateParams);
begin
inherited;
Params.WndParent := Application.Handle;
end;
Although you state otherwise in the comments, when I do this I find that the popup form is indeed hidden when I minimize the main form. And it is shown again when the main form is restored. Thus I think this does solve your problem completely.
I haven't got Delphi open now, but would setting
mainform.formstyle := fsStayOnTop
and show the child form with
childform.show;
work?
or else try using SetWindowPos() and setting the hWndInsertAfter property to something like HWND_TOPMOST on the main form

Why would something fail in FormCreate, but work fine in FormShow?

I'm using Delphi 7. When I try to create an object in FormCreate, it fails (actually it just hangs). When I try to do the same thing in FormShow, it works. Please note, I'm not talking about creating a visual component like a TEdit. It's a database abstraction object. Any ideas why this might happen? Are there any guidelines about this topic?
1) The reason is because on FormCreate, the handle to Form is not created yet.
If your database object needs a form handle, do this:
Self.HandleNeeded; // on FormCreate time.
2) The other reason is maybe your database component needs to be connected and it's only connected on DFM?
My first guess is that you're accessing a DataModule hasn't been created yet. If your project's source looks like the following:
begin
Application.Initialize;
Application.CreateForm(TForm1, Form1);
Application.CreateForm(TDataModule1, DataModule1);
Application.Run;
end.
And your TForm1.FormCreate looks like the following:
begin
DataModule1.AddUsersToStringList(Self.ComboBox1.Items);
end;
Then FormCreate going to fail because it's is being run as part of the Application.CreateForm(TForm1, Form1); line, and your Data Module hasn't been created yet.
There's 2 solutions:
Defer your processing/initialization until after all the forms and data modules have been created.
Create all the data modules before creating any of your forms. The Application's "Main Form" in Delphi is the first TCustomForm descendant created by Application.CreateForm, not the first object.

How do I avoid to call Application.CreateForm twice?

I stumbled on this page Why shouldn’t I call Application.CreateForm.
Now I have some code like this:
SplashForm := TSplashForm.Create(Application);
SplashForm.Show;
SplashForm.Update; // force update
Application.Initialize;
Application.CreateForm(TClientData, ClientData);
SplashForm.Update; // force update
Application.CreateForm(TClientMainForm, ClientMainForm);
Application.ShowHint := True;
Application.Run;
ClientMainForm.ServerConnected := false;
FreeAndNil(ClientMainForm);
FreeAndNil(ClientData);
First a splashform is created, then a datamodule and last the main form. The page says that Application.CreateForm should not be called twice. Should the code above be changed?
There is nothing wrong with using Application.CreateForm multiple times. But this introduces global variables for each form which can be a code smell.
Unfortunately the IDE creates one for each form. Although you can remove them if you like.
A better way is to create a form when you need it and release it when you are ready with it. So you only use Application.CreateForm for the main form.
A main datamodule can be created by the main form. But it can be global too, just a matter of taste.
So to answer the question, you can avoid Application.CreateForm by creating and releasing the forms locally.
The article mentions the side effect of Application.CreateForm (the first completed form is the main form).
So there can be unexpected side effects if the main form creates other forms using Application.CreateForm.
So just to avoid any nastyness, you should limit yoursef to a single call. Which is done using only one global form.
If TClientData is a Data Module and TClientMainForm is a form, then no (except perhaps the two FreeAndNil calls at the end - not really needed). But take care. Because as it says Rob Kennedy in his post, the Application.CreateForm does other things behind (it sets the MainForm variable), so I would advise to set up your project file according to the following rules:
Create all the forms which you want to create at startup using a single call to Application.CreateForm - usually this is done by the IDE.
Remove from the project file the forms which you want to create dynamically (on-demand) in your program. (In Project | Options | Forms...) - move them from 'Auto-Create Forms' to 'Available Forms'
Create your forms in your code using TmyForm.Create(Owner) (etc.) and not with Application.CreateForm(...). As an aside, if you are sure that you will free the form, then it is better (in order to speed the things up) to call TmyForm.Create(nil) - iow without any owner.
If you want to do some kind of initialization at startup you can have a procedure / method in the project file tied to a form / data module already created and run it before application run.
For example:
begin
Application.Initialize;
Application.MainFormOnTaskbar := True;
Application.CreateForm(TdmoMain, dmoMain); //<--this is a data module
Application.CreateForm(TfrmMain, frmMain); //<--this will became the main form
Application.CreateForm(TfrmAbout, frmAbout);
//... other forms created here...
frmMain.InitEngine; //<--initialization code. You can put somewhere else, according with your app architecture
Application.Run;
end.
In this way you will have the project file clean and you will know exactly which is which.
HTH
When I wrote that article, I was thinking primarily of code outside the DPR file. People see the form-creation code generated by the IDE in the DPR file and think that's the best way to create forms generally, so they use that elsewhere in their programs. They sometimes use it in the main form's OnCreate event handler to create other forms their program needs, and then they hit problems because the program's main form isn't what they think it is.
In the code you provided, it's easy to call CreateForm just once. Use it for the main form, and for nothing else. The data module isn't a main form, so you don't need CreateForm's magic there.
SplashForm := TSplashForm.Create(Application);
SplashForm.Show;
SplashForm.Update; // force update
Application.Initialize;
// Change to this.
ClientData := TClientData.Create(Application);
SplashForm.Update; // force update
Application.CreateForm(TClientMainForm, ClientMainForm);
Application.ShowHint := True;
Application.Run;
ClientMainForm.ServerConnected := false;
// Remove these.
FreeAndNil(ClientMainForm);
FreeAndNil(ClientData);
You really shouldn't free the objects you've created here because you don't own them. They're owned by the global Application object, so let it take care of freeing them: Remove the two calls to FreeAndNil.
The article you refer to is incorrect. There are a number of valid reasons why you would want multiple calls to Application.CreateForm
1) Datamodules: You probably want these available all of the time. Best way to do this is Application.CreateForm. I know of applications with several themed Datamodules e.g. Customer, Invoice, Address to handle different areas of the database & encapsulate the functionality neatly. All of these are created in the .dpr
2) Big, slow loading stuff (which is a bad idea in & of itself but these things happen and are often inherited by support programmers...). Move the load time into the application startup exactly like your example code along with the splash screen updating. The users expect applications to take a while to get going thanks to the stirling efforts of our collegues working on Microsoft Office to lower the bar of expectations for the rest of us :)
So, in summary, don't worry your code is fine - but you can lose the "FreeAndNil" stuff. However small quick hitting Dialog type stuff is best invoked by:
with TMyform.Create(nil) do
try
//Setup
case ShowModal of
// Whatever return values you care about (if any)
end;
finally
Free;
end;
Short, sweet, to the point & minimises memory usage...

Resources