I am trying to create an application with 2 forms in Delphi XE6. Depending on a ParamStr setting Form2 may or may not be shown before Form1.
In a quick test app both forms are created before Form1.Show is invoked - during which Form2 is shown or not
procedure TForm1.FormShow(Sender: TObject);
begin
if ParamStr(1) = 'foo' then
Form2.FooShow;
end;
procedure TForm2.FooShow;
begin
ShowModal;
end;
However in the "real" application I am seeing a different behaviour.
In this case Form1.Show is being called as soon as Application.CreateForm(TForm1, Form1) is called. This is before Form2 is being created, which is causing problems as Form2 doesn't exist when it is needed.
Any explanation why the behaviour would differ? Am I missing a setting buried somewhere in Project>Options
If your form is persisted with Visible set to True, then it will be shown as soon as it's created.
Setting the property to False should resolve your problem.
EDIT
PS: Just in case someone with a similar problem has their main form unexpectedly show even though Visible is set to False. This happens because by default the application will show the main form regardless of its Visible property in the call to Application.Run.
If so, the following question should help: How can I start Delphi application with the hidden main form?
EDIT2
For the sake of completeness, there are a couple other things that could cause a form to be shown as soon as it's created. However, these probably aren't applicable to this specific question.
Any code that explicitly shows the form when it's created (such as the OnCreate event) would obviously cause the form to be shown. However, one would hope that such actions don't lead to these kinds of questions.
An MDI child form can never be hidden. At best it can be minimised.
TIP
The quickest way to finding the answer to such questions is usually just a little bit of debugging.
Set a break-point in your FormShow method.
Go to Compiler Settings, and enable the option to build with debug DCU's. (You'll want to see the VCL code.)
Rebuild and run your application.
When you get to your break-point, open the Call Stack debug window.
Navigate the call-stack looking for the trigger.
In this case you should have found the following code in Forms.pas.
procedure TCustomForm.DoCreate;
begin
//...
if fsVisible in FFormState then Visible := True; //<-- The trigger
end;
And a little more investagation on fsVisible would reveal the root cause as: The Visible property is set to True.
That said, you don't want to be coding this way because you're creating dependencies via globals. This is error-prone; and your little experiment shows shows just one of many subtle things that can cause problems.
Rather avoid the globals with something like the following changes in your DPR:
begin
Application.Initialize;
ShowForms;
end;
Where ShowForms is implemented as:
procedure ShowForms;
var
LForm1: TForm1;
LForm2: TForm2;
begin
Application.CreateForm(TFrom1, LForm1);
Application.CreateForm(TFrom2, LForm2);
if (ParamStr(1) = 'foo') then
LForm2.Show
else
LForm1.Show;
end;
If you don't have any dependencies between the forms, the above will suffice.
If you do have a dependency, e.g. Form2 uses Form1: then you can explicitly pass a reference after creating the forms, bu before you start doing anything with them.
//Define a property on TForm2 E.g.
property MainForm: TForm1 read FMainForm write SetMainForm;
//Immediately after creating both forms tell form2 which to use as its main form.
LForm2.MainForm := LForm1;
Related
I have recently upgraded from XE4 to XE8 and have come across a access violation when assigning a itemindex to a radio group. I was curious why in XE4 this works and XE8 it doesn't. All of the forms have been created when the main program starts up. This code works fine when debugging but when running as a standalone it throws the exception.
with TravelBookingForm do begin
try
rg1.itemindex:=tag-1;//not sure which causes the access violation
except
on E : Exception do
begin
showMessage(E.Message); //access violation message is shown
end;
rg1.tag := 0;
end;
end;
NB: I have omitted code that isn't relevent
So after more testing I can confirm that rg1 is not nil and that it is the item causing the access violation.
If rg1 is not nil and yet leads to an access violation then the most plausible explanation is that rg1 points to memory that has been freed. This matches the observation that the error occurs sometimes (outside debugger) and not others (under debugger).
So, your program is destroying the form at some point, and then later referring to the form.
Clearly that is an error in your program and you will need to make sure that you never refer to forms after they have been destroyed. Unfortunately the IDE encourages you to create all your forms once at startup, and hold references in global variables. This makes it all too easy to have stale references.
If you used the full debug version of FastMM then that tool would be able to warn you when you attempt to access memory that has been freed.
I made a question that looked like this before and people didn't understand. I'll try to be more concise and will use a comparison with Ajax used in web applications.
I have the main form. There I have one button that will extract data from a field and send to a second form, a window that will pop up and display the data in a more organized way (A TListBox).
I would like to know if there is a way to on SecondForm.Show to send this data as parameters, like SecondForm.Show(data).
The comparison to Ajax is that when you make an Ajax Call from a html page to the server, you send encapsulated data, the server receives it and works with it.
I want my main form to send the data, the second form to receive the data and use it.
Is it possible? How?
If I were you, I'd add parameters to the form's constructor so that it has all the information it needs from the moment it exists.
constructor TSecondFrom.Create(AOwner: TComponent; AParam1: Integer; const AParam2: string);
begin
inherited Create(AOwner);
// Use or store parameters here
end;
You're welcome to write some other method instead, though, and you can call it Show, if you want. Define it to accept whatever parameters you need, and when you're ready, you can call the inherited zero-argument Show method to make the form appear.
procedure TSecondForm.Show(AParam1: Integer; const AParam2: string);
begin
// Use parameters here.
inherited Show;
end;
Decide in what form to send the data from Main to SecondFrom. For instance in a TStringList. Fill the striglist on the mainform, use it as parameter in SecondForm.Show.
This is the answer from your other question that you deleted. It still applies I believe.
Always prefer passing state in the constructor if that is viable.
Problems with state occur when it changes. Multi-threading is confounded by mutating state. Code that makes copies of state become out of sync if the state changes later.
Objects cannot be used while the constructor is executing. That's the time to mutate state since no other party can see the effects of that mutation. Once the constructor returns the newly minted object to its new owner, it is fully initialized.
It's hard with an OOP style to limit all mutation to constructors. Few, if any, libraries admit that style of coding. However, the more mutation you push into the constructor, the lower the risk of being caught out later.
In reality, for your scenario, I suspect that these risks are minimal. However, as a general principle, initialization during construction is sound and it makes sense to adhere to the principle uniformly.
TGUITestRunner form represents DUnit test results and created once by GUITestRunner.RunTest procedure:
procedure RunTest(test: ITest);
begin
with TGUITestRunner.Create(nil) do
begin
try
Suite := test;
ShowModal;
finally
Free;
end;
end;
end;
I want to extend it at runtime by writing colored status messages. It is possible, because that status messages at the bottom of GUI are placed into TRichEdit. So I need to get the pointer to this form somewhere in my TTestCase.
Can I do that without fixing the DUnit's code? Maybe you can recommend some hack?
A "decoupled" way to do it might be to use some "embedded codes" inside your status messages:
Status('<blue>Testing');
From within the dUnit test framework, you could check if the initial character of a status message is '<', and extract the arguments such as color or whatever else, and then modify dUnit to handle it.
That way, your tests will still run on an unmodified dUnit test runner. Several years from now you might want to move to the latest dUnit test, and I don't recommend making any API changes, or trying to access the test runner objects. The APIs and things you can see from within a test are strictly controlled on purpose. It's a principle of proper object oriented design, which the creators of jUnit/xUnit/dUnit believe in intensely.
The Delphi online help says that Release should be used to remove a form from memory. However, in many examples for modal forms I have seen this construct:
MyForm := TMyForm.Create(nil);
try
MyForm.ShowModal;
finally
MyForm.Free;
end;
Is Free a safe way to destroy a modal form? As I can see in the source for ShowModal, Application.HandleMessage will be called until the ModalResult is not 0. Is this the reason why Free can not interfere with pending windows messages?
Yes, it's safe to use Free after a ShowModal call.
The cases where you need to use Release are times when you're in the middle of an event handler (eg, OnClick), where further processing after the event will have to access the form. In that case calling Release instead posts a CM_RELEASE message which doesn't free the event until the event handler is done and control has returned to the message pump (ProcessMessages/Application.Run). ShowModal doesn't return until the event handler is finished and control makes it back up the stack, so calling Free afterwards is effectively the same place where the CM_RELEASE message would be processed otherwise.
It depends. Freeing the form doesn't call the event handlers that Release does, and any messages that might have been posted to the form and are queued will not be processed. So while, in many and possibly most cases calling Free (or FreeAndNil) will work fine, it may lead to some highly weird behaviour for seemingly random reasons.
The alternative that I'd suggest is in the OnClose event set the Action to caFree, like this:
procedure FormClose(Sender : TObject; Action : TCloseAction)
begin
Action := caFree;
end;
You can then write code like this:
TMyForm.Create(nil).ShowModal;
And you don't need to free the form specifically, since it'll free itself when it's done.
Absolutely, and you can also use the FreeAndNil routine. The FreeAndNil routine will only free the object if it is not already nil, and also set it to nil after the free. If you call free directly on an object which has already been freed, you get an Access Violation.
MyForm := TMyForm.Create(nil);
try
MyForm.ShowModal;
finally
FreeAndNil(MyForm);
end;
I run into this regularly, and am just looking for best practice/approach. I have a database / datamodule-containing app, and want to fire up the database/datasets on startup w/o having "active at runtime" set to true at design time (database location varies). Also run a web "check for updates" routine when the app starts up.
Given TForm event sequences, and results from various trial and error, I'm currently using this approach:
I use a "Globals" record set up in the main form to store all global vars, have one element of that called Globals.AppInitialized (boolean), and set it to False in the Initialization section of the main form.
At the main form's OnShow event (all forms are created by then), I test Globals.AppInitialized; if it's false, I run my "Initialization" stuff, and then finish by setting Globals.AppInitialized := True.
This seems to work pretty well, but is it the best approach? Looking for insight from others' experience, ideas and opinions. TIA..
I generally always turn off auto creation of all forms EXCEPT for the main form and possibly the primary datamodule.
One trick that I learned you can do, is add your datamodule to your project, allow it to auto-create and create BEFORE your main form. Then, when your main form is created, the onCreate for the datamodule will have already been run.
If your application has some code to say, set the focus of a control (something you can't do on creation, since its "not visible yet") then create a user message and post it to the form in your oncreate. The message SHOULD (no guarantee) be processed as soon as the forms message loop is processed. For example:
const
wm_AppStarted = wm_User + 101;
type
Form1 = class(tForm)
:
procedure wmAppStarted(var Msg:tMessage); message wm_AppStarted;
end;
// in your oncreate event add the following, which should result in your wmAppStarted event firing.
PostMessage(handle,wm_AppStarted,0,0);
I can't think of a single time that this message was never processed, but the nature of the call is that it is added to the message queue, and if the queue is full then it is "dropped". Just be aware that edge case exists.
You may want to directly interfere with the project source (.dpr file) after the form creation calls and before the Application.Run. (Or even earlier in case.)
This is how I usually handle such initialization stuff:
...
Application.CreateForm(TMainForm, MainForm);
...
MainForm.ApplicationLoaded; // loads options, etc..
Application.Run;
...
I don't know if this is helpful, but some of my applications don't have any form auto created, i.e. they have no mainform in the IDE.
The first form created with the Application object as its owner will automatically become the mainform. Thus I only autocreate one datamodule as a loader and let this one decide which datamodules to create when and which forms to create in what order. This datamodule has a StartUp and ShutDown method, which are called as "brackets" around Application.Run in the dpr. The ShutDown method gives a little more control over the shutdown process.
This can be useful when you have designed different "mainforms" for different use cases of your application or you can use some configuration files to select different mainforms.
There actually isn't such a concept as a "global variable" in Delphi. All variables are scoped to the unit they are in and other units that use that unit.
Just make the AppInitialized and Initialization stuff as part of your data module. Basically have one class (or datamodule) to rule all your non-UI stuff (kind of like the One-Ring, except not all evil and such.)
Alternatively you can:
Call it from your splash screen.
Do it during log in
Run the "check for update" in a background thread - don't force them to update right now. Do it kind of like Firefox does.
I'm not sure I understand why you need the global variables? Nowadays I write ALL my Delphi apps without a single global variable. Even when I did use them, I never had more than a couple per application.
So maybe you need to first think why you actually need them.
I use a primary Data Module to check if the DB connection is OK and if it doesn't, show a custom component form to setup the db connection and then loads the main form:
Application.CreateForm(TDmMain, DmMain);
if DmMain.isDBConnected then
begin
Application.CreateForm(TDmVisualUtils, DmVisualUtils);
Application.CreateForm(TfrmMain, frmMain);
end;
Application.Run;
One trick I use is to place a TTimer on the main form, set the time to something like 300ms, and perform any initialization (db login, network file copies, etc). Starting the application brings up the main form immediately and allows any initialization 'stuff' to happen. Users don't startup multiple instances thinking "Oh..I didn't dbl-click...I'll do it again.."