In DPR file:
Application.CreateForm(TMain, Main);
Application.CreateForm(TCommStatus, CommStatus);
But I get an error if I want to use CommStatus in Main, because it was not instanced yet. Then inside TMain I tried:
procedure TMainWindow.FormShow(Sender: TObject);
begin
Application.CreateForm(TCommStatus, CommStatus);
CommStatus.Expand(Self);
end;
I was trying to have my LOG window to be positioned and sized according to my MainWindow position and width. But as my LOG window is created after Main Window, I can't really call it in OnCreate(), even because there is no correct positioning data in OnCreate().
Don't use Application.CreateForm to create the CommStatus form at all. Create it yourself in your MainWindow.OnCreate:
proccedure TMainForm.FormCreate(Sender: TObject);
begin
CommStatus := TCommStatus.Create(Self);
CommStatus.Expand(Self);
end;
Don't forget to remove CommStatus from the auto-create forms list (in Project->Options->Forms).
I need to know if CommStatus is Assigned before execute CreateForm.
You answered your own question - use Assigned(), eg:
uses
..., CommStatusFormUnit;
if not Assigned(CommStatus) then
Application.CreateForm(TCommStatus, CommStatus);
Or:
uses
..., CommStatusFormUnit;
if not Assigned(CommStatus) then
CommStatus := TCommStatus.Create(Application);
Global variables, like the CommStatus variable in the CommStatusFormUnit unit, are zero-initialized at program startup, thus Assinged() will return False until the form is actually created, as long as you assign the new Form instance to the global variable (as the examples above do).
But CommStatus identifier does not exists until it. So I can't use Assigned(CommStatus).
Yes, it does exist, and yes, you can use Assigned(CommStatus). If you are having errors with it, then you are not using it correctly.
Expand(Self) should use Main position information to put CommStatus beside Main in the same left position, but it doesn't.
Then you are not handling the positioning logic correctly.
Another option is to create CommStatus first in your DPR. But without using Application.CreateFom (which would then incorrectly make CommStatus you main form).
This way you'll know it has definitely been created, and not have to worry.
I.e.
CommStatus := TCommStatus.Create(Application);
Application.CreateForm(TMain, Main);
However, that fact that you're trying to Expand CommStatus in the OnShow of your main form seems a little dubious to me. You've created a hidden dependency between the two forms, which is exactly one of the reasons globals are frowned upon.
One option to clean that up would be as follows:
CommStatus := TCommStatus.Create(Application);
Application.CreateForm(TMain, Main);
CommStatus.Expand(Main);
This way the relationship between the two is explicit and less error-prone.
Related
I have a datamodule for my frame, which uses a global instance.(dmData)
The data components are linked to the datasources on the dmData instance
now I want to use a datamodule instance that is private to a frame, because I want to have multiple instances of the form which contains the frame showing at the same time.
I can't figure out how to make that happen, either in code or in designing.
in the frame, I am creating the datamodule as dmLocalData := tdmData.Create(self), but in design I don't have the option to link dmLocalData, only the option to link to dmData(so all my data controls are blank (except for that ONE that has a local datasource that gets set in code)
I mean, in code, I could manually go through each component one by one and change the datasource, but thinking there really has to be a better way, the maintenance on that would be pretty much horrendous.
Any ideas about a better way?
Actually there is a way to avoid hand-wiring the controls for a dynamically created datamodule. In short - override the datamodules CreateNew constructor like this:
constructor TMainDM.CreateNew(AOwner: TComponent; Dummy: Integer);
begin
Dummy := -1;
inherited;
end;
This avoids that multiple instances of the datamodule get different names and thus the references are resolved as expected. As the datamodules are private to the frame anyway, there is no need for them to have globally unique names.
A much longer and more detailed explanation can be found in these two articles, which use a quite similar task as an example:
Tweaking DFM Loading (update)
Tweaking DFM Loading
I have functions I want to perform after my app has finished initialising and the main form has been created. I did have the code (call it ProcedureX) in the forms OnShow event, but I have just noticed that it is being called twice, because OnShow is firing twice. It fires when the main program DPR calls:
Application.CreateForm(TMainForm, MainForm) ;
as I would expect. But after that, when I read stuff from an INI file that includes the forms on-screen position, I have a call:
MainForm.position := poScreenCenter ;
This, it would appear fires the OnShow event again.
Where can I put my call to ProcedureX, which must only be called once, and which needs the main form to be created before it can execute?
If your code only needs to run once per form creation (or per application and the form is only created once per application run), put the code in the form's OnCreate handler. It is the natural place for it to go.
Nowadays (since D3 I think) the OnCreate fires at the end of the construction process in the AfterConstruction method. Only if you were to set OldCreateOrder to True (and it is False by default), might you get in trouble as that makes the OnCreate fire at the end of the Create constructor.
The normal order of execution for a Form is :
AfterConstruction: when the form and it components are fully created with all their properties.
OnShow: whenever the Form is ready to show (and, yes, any change causing a CM_SHOWINGCHANGED can trigger an OnShow)
Activate: whenever the Form takes the Focus
So, depending on what you need in ProcedureX, AfterConstruction might be enough, and is executed only once; just override it and add ProcedureX after inherited. It'll be after OnCreate.
If it is not the case, you can post a custom message to your Form from AfterConstruction, it will be queued and will reach your custom handler after the other messages have been handled.
In both cases, you would not need a extra boolean Field.
#Sertac,
There's really no need for the FRUNOnce field; simply do OnShow=NIL as the first line of your FormShow method.
FYI, The "run once" idiom -- setting the event handler field to NIL in the first line of the event handler -- is also terribly useful for getting some code up-and-running once a form has been completely initialized. Put your code in a FormActivate method and, as the first line of the method, set OnActivate=NIL.
You can test and set a flag once you call the procedure for the first time. Like so:
type
TForm1 = class(TForm)
procedure FormShow(Sender: TObject);
private
FRunOnce: Boolean;
public
[...]
[...]
procedure TForm1.FormShow(Sender: TObject);
begin
if not FRunOnce then begin
FRunOnce := True;
ProcedureX;
end;
end;
You can add a procedure in your DPR file, after Application.CreateForm.
Put all code you need to initialize in that procedure.
Works best when you have multiple forms in your app.
Also if the initialization takes a lot, it let's the program to display the forms on the screen so the user will know that the app is loading.
Example:
PROGRAM MyProgram;
begin
Application.Initialize;
Application.CreateForm(TMyForm, MyForm);
MyForm.Show;
LateInitialize; <----------- here
Application.Run;
end.
I'm going to propose a bit different approach to this answer by Server Overflow. We will achieve almost exactly same effect, but without any edit inside the DPR file (main project source file). We will get there by using a class helper in the unit of our main form:
type
{ TAppHelper }
TAppHelper
= Class helper for TApplication
Public Procedure Run;
End;
Procedure TAppHelper.Run;
begin
Unit1.MainForm.PreRun;
inherited Run;
end;
Notice, the Unit1.MainForm.PreRun is some method in your main form, with only one caveat: if your main form is called "MainForm", then you need to prefix it with your unit's name inside the helper's method, because the TApplication class already has a member called MainForm. Incidentally, if you do leave out the prefix, this might still work, given that your Unit1.MainForm is indeed application's main form as well.
The reason why this works, is because the Unit1 is on the uses list of the DPR project, and as long as the TAppHelper is defined in the interface section (not in the implementation section), it will get loaded and by the time the Application.Run method is called in the DPR file, this will already be the helper version of it.
The beauty of this is, that it will run exactly one time, and exactly after all the forms are already created, after all their constructors have already been executed. And the fact that we're effectively customizing the Application.Run call in the DPR file, without editting the DPR file, is kind of ingenious. Again, class helpers in delphi/lazarus !
I will share one more neat trick, first take a look:
Procedure TAppHelper.Run;
begin
TTask.Run(
procedure
begin
sleep(10);
TThread.Synchronize(nil, procedure begin Unit1.MainForm.PreRun; end);
end
);
inherited Run;
end;
This is a trick I use whenever I want the code to execute with a small delay. Why? Because if your code runs before the inherited Run method, it might (depending what happens inside of that code) hang the UI momentarily, but just long enough for the form to flicker and appear not responsive during its startup. Also, we can't simply put the code behind the inherited Run method, because that won't get executed until the application gets terminated. So instead I use TTask from the System.Threading unit. The sleep(10) is probably an overkill, sleep(1) would most likely do the job, possibly even no sleep at all would work, but I do some complex initialization there, so I keep the delay generous. Bonus: if you don't update UI from your PreRun custom method, then you don't even need TThread.Synchronize wrapper, and it becomes even simpler. In case of FPC/Lazarus you can achieve the same by using TApplication.QueueAsyncCall() instead of TTask class.
I really think it's a neat trick, because I can code it entirely outside of the DPR file, in the unit of the form which defines the PreRun method, and it's guaranteed after ALL Forms are already created, not just the one where I implement my PreRun method. Also, if the class helper is in the unit of the form, instead elsewhere, then the PreRun doesn't even need to be public, it will work with protected or even private method as well! This is great for keeping this little logic away from any other part of the code.
#Sertec,
Your code won't work either if you want it to run for every unhide event (you haven't put in any code to reset the frunonce field).
So your method would need to reset frunonce field, and mine would need to set OnShow=FormShow. Same difference, except that you need an additional field.
I has a piece of code where I override TStringGrid's inplace editor and the hint window. For this, I created my own string grid based on TStringGrid and use a TEdit for inplace editor and a TPanel for tool tips. In TMyStringGrid.Create constructor I initialize them like this:
Constructor TMyStringGrid.Create();
Begin
inherited Create(AOwner);
MyEditor:= TEdit.Create(Self);
MyEditor.Parent := Self;
End;
In this case the owner (the main form) is freeing the controls. I used this for years and it worked.
The thing is that other people argue that the programmer should use NIL instead the Self when instantiation the child controls and later to manually free them in the Destroy destructor. It seems that the second alternative has gigantic advantage over the first one, especially when you dynamically create lots of child controls (not my case). Other problem with my code, they say, is that the child controls may be freed after an Application.ProcessMessages call while the application may still want to use them.
So, I should let my code unchanged or should I manually create and free the child controls?
There is a any Borland example of compound controls?
Delphi 7, Win XP
Reference: http://delphi.about.com/od/kbcurt/ss/dynamiccreateno.htm
Yes you can use your code without changing it.
There is a any Borland example of compound controls?
Best example is to check the implementation of TLabeledEdit.
It was creating the label in constructor
if Assigned(FEditLabel) then exit;
FEditLabel := TBoundLabel.Create(Self);
FEditLabel.FreeNotification(Self);
FEditLabel.FocusControl := Self;
There's no good reason to pass nil instead of Self in this situation. That AOwner parameter is there specifically for that reason. Take advantage of it.
There's a reason to pass nil when creating a control and manually destroy it, but for a completely different situation: if you're creating a control (typically a form) inside a function. This is a pretty common pattern, for example:
MyDialog := TMyDialog.Create(nil);
try
result := MyDialog.ShowModal;
finally
MyDialog.Free;
end;
There, you want to free it immediately instead of waiting around until the current form gets destroyed, which could be much later. But when it comes to sub-components, you usually want them to be destroyed at the same time as the parent component, not later, so pass Self to AOwner and let the VCL take care of it for you.
Considering that constructor, the grid instance is owned by Aowner (which is tipically a TForm or a TFrame). The inplace editor is owned and parented by the grid instance. I don't see how ProcessMessages would cause destruction of the child objects, since they will be destroyed in the destroying loop of TMyStringGrid. That's nothing I can see of wrong on that implementation - and I use that same design for the components I create. Ownership is there on VCL to easy our lifes when managing objects' lifetime. And is not the case where nil is recomended as owner, which is shown in Mason' answer.
In the pattern shown by Mason , the reason for the NIL is that, without a owner, the object destruction will not enter in a Notification loop. If you create a lot of components which destruction you handle manually, you want to make sure the owner is NIL, otherwise a lot of code get executed (without need) in each component construction/destruction.
Many moons ago, there was an excelent white paper on the web archive of (now defunct) eagle-software.com
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...
Our application used to make use of a common base form that all forms were meant to inherit from. I'd like to get rid of it for a number of reasons, ranging from the need to police that everyone uses it to several annoyances relating to Delphi's VFI implementation. It turns out that the bulk of the features it offered can be done in other, more reliable ways.
The one that I am not so sure about, is automatically positioning all forms in the center of their callers. So if I open Dialog A from my main form, it should be placed over the center of the main form. And if I then open Dialog B from Dialog A, it should be placed over the center of Dialog A and so on.
We used to take care of all this by setting the base form's Position property to poOwnerFormCenter and it worked great. But how do I do this app-wide?
I thought of using Screen.OnActiveFormChange, but I think this happens each time the form receives focus. I also thought of using Application.OnModalBegin but there doesn't seem to be an obvious way to find the form at the point this is called.
Has anyone tried this?
Well, obviously form inheritance is provided to solve exactly the problem you're trying to solve. Any solution is probably going to wind up mimicking form inheritance in some way.
Could you do something as simple as globally searching your code for "= class(TForm)" and replacing the TForm class with either your existing base form or a new, simplified base form class with only the functionality you need?
Failing that, you could try to modify the original TForm class itself to have the positioning behavior you want. Obviously, modifying the supplied classes is a little on the dangerous side.
If you are not going to go with a common base form, then I would suggest placing a non-visual component on each form. That component can inject the behaviors you want into the base form. If you want to have various different behaviors on different forms then give your component a role property that defines what role that form should have, and it can then inject different characteristics based on that role.
BTW, you can also have non-visual form inheritance, which is my preferred method of creating a common base class for all forms. It also has the advantage of adding properties to the form, and then based on those properties you can change the role or behavior of the form.
Without knowing more about your application, my advice would be to add the positioning code to each form individually - the advantages of not having a base class is that it makes it easier to have certain forms that do things slightly differently, and it keeps all the logic of a form together in one place.
I normally use the FormShow event for this, using the SetBounds() procedure.
With other non-form controls you can do the same thing by overriding the CMShowing message.
I took your idea of OnModalBegin and ran with it. The following is a "Hack", but it seems to work. To test simply drag around the form and click the button.
procedure TMainForm.Button1Click(Sender: TObject);
var
mForm: TForm;
begin
mForm := TForm.create(self);
mform.width := 300;
mform.height := 300;
mForm.ShowModal;
mForm.Free;
end;
procedure TMainForm.FormCreate(Sender: TObject);
begin
application.OnModalBegin := modalbegin;
end;
procedure TMainForm.FormShow(Sender: TObject);
begin
if Screen.FormCount>1 then begin
screen.forms[Screen.FormCount-1].left := round((screen.forms[Screen.FormCount-2].left + screen.forms[Screen.FormCount-2].width/2) - screen.forms[Screen.FormCount-1].width/2);
screen.forms[Screen.FormCount-1].top := round((screen.forms[Screen.FormCount-2].top + screen.forms[Screen.FormCount-2].height/2) - screen.forms[Screen.FormCount-1].height/2);
application.processmessages;
screen.forms[Screen.FormCount-1].Caption := inttostr(screen.forms[Screen.FormCount-1].top)+','+inttostr(screen.forms[Screen.FormCount-1].left);
end;
end;
procedure TMainForm.ModalBegin(Sender: TObject);
begin
if Screen.FormCount>=0 then
screen.forms[Screen.FormCount-1].OnShow := FormShow;
end;