I use a form with a JVWizard from Jedi (version 3.50) on it. I want to do some initialization stuff in the oncreate event, because I thought it will be the first event after creation of the form (like described here). But i found out that the onenterpage event of the welcomepage (first page in page list) event is fired before the oncreate event. I'm wondering what is wrong.
The sequence of events for a form are not absolutely guaranteed. Or rather, the events of a form are determined by the form, but this does not guarantee anything with respect to events triggered by other components.
The OnCreate event of a form is called once the form has been constructed, after any and all components and controls on that form have themselves been initialised.
If those components or controls trigger any events then these may occur before the form OnCreate event itself.
In your case, since you are using the JvWizard components and controls then if you have these controls and components placed on your form at design time then any events triggered by their initialization as they are loaded and initialised at runtime will occur before the Form.OnCreate event.
The OnEnterPage event is one of these, triggered by the JvWizard as it initialises and establishes its first page.
Without knowing the details of precisely what initialization it is you are trying to perform it is impossible to say what the correct solution in your case might be.
It might be simply to perform your form initialization later, for example in response to the forms OnShow event.
Or it may be to move some (or all) of the initialization to the wizard OnEnterPage itself.
Or it may be appropriate to instead implement your initialization as an override of the virtual Create constructor.
Overriding the constructor itself would enable you to perform some initialization before calling the inherited constructor (to initialise the form contents and eventually call OnCreate) and some initialization after the call to inherited (which will run after any Form OnCreate event handler).
constructor TMyForm.Create(Owner: TComponent);
begin
// Perform initialization BEFORE calling inherited and
// BEFORE any components or controls have initialised and
// triggered any of their events.
//
// But remember: At this point there are no form contents loaded!
// As a result the amount of useful initialization you can do
// here may be limited.
// ..
inherited Create(Owner);
// Perform further initialization here ...
//
// At this point form contents (controls/components) have been
// loaded and any events have been triggered and handled,
// including FormCreate. Any code here might even be better
// left to run in the FormCreate event handler.
end;
Hopefully with an understanding of what is going on you will be able to identify the appropriate approach in your case.
Related
I have a main form where I set a handler for the Application.OnMessage event. (Code of this handler is placed in the main form). Then, while running the program, there can be calls to SysUtils.LoadPackage that loads some bpl-package. And after that is loaded, the handler of Application.OnMessage is changed.
I couldn't find what doing this. At least there is not right such code that goes Application.OnMessage := in the package.
One more thing: in the debugger, before LoadPackage, I see OnMessage handler described as Main.TMainForm.AppMessage. All other handlers (such as OnMinimize, OnModalBegin e.t.c.) are nil. And after LoadPackage all events have handlers, described as Vcl.AppEvnts.TMultiCaster.DoMessage.
The package in question uses an internal instance of TApplicationEvents, which is a multicaster that intercepts TApplication events and delegates them to every TApplicationEvents instance in the application, allowing multiple Forms, components, etc to receive the same app events without stepping on each other's toes trying to assign handlers to TApplication directly.
So, to coexist with the package, the solution is to add a TApplicationEvents to your MainForm and assign a handler to its OnMessage event, instead of assigning a handler to the TApplication.OnMessage event directly.
I made my own UDP receiver a TComponent descendant. It has an OnReceive event. This event is used to add a line to TMemo which exists on the same form.
The problem is that when parent form is being destroyed TMemo gets destroyed first and UDP receiver continues to fire OnReceive event. Of course I get an exception when I try to mmo1.Lines.Add(S) to a non-existent Memo.
How to detect in TComponent the moment when parent form and it's components are about to be destroyed, but aren't destroyed yet? I would do then a proper receiver thread shutdown.
Why are you even trying to detect in your component when the parent form is destroyed? Wouldn't it be better to just utilize forms OnClose or OnCloseQuery events to stop your component from receiving data.
Both of these events are called before the form even begins destroying itself and its owned components. Where in OnCloseQuery event you can even prevent form from closing if certain work hasn't finished yet.
When you rely on components for your component to work but are not able to control their life-cycle, you can use TComponent's Notification to re-act when the component you are interested in gets destroyed.
First, your UDP Receiver will need to add itself to the notification list of the component with e.g.
Form.FreeNotification(Self);
to receive notifications from the TForm. Then, the UDP Receiver needs to override
procedure Notification(AComponent: TComponent; Operation: TOperation);
virtual;
There, you can listen to notifications where AComponent = Form and Operation = opRemove, that will indicate the Form is being removed.
Edit: Reading again, this might not be what you are interested in. If the OnReceive event is implemented in your form, you might check if the Form's ComponentState includes csDestroying.
Or, in case the Memo is created at design-time, or you explicitly create and free-and-nil the Memo yourself, just check if Memo <> nil.
I'm working on a component that is placed on every form of my project. Is it possible, in runtime, to have the component include code into it's owner form's OnClose event handler. In other words, the form will trigger it's own OnClose event handler but the component will also include additional event handler code to run on the owner form's OnClose event. (Is that what is called vector replacement?)
Thank you.
You need to get the component to declare a field to store the form's original OnClose. Then you can do in the component's constructor:
FOriginalFormClose := (Owner as TForm).OnClose;
(Owner as TForm).OnClose := FormClose;
Then the component's FormClose would read:
TMyComponent.FormClose(Sender: TObject; var Action: TCloseAction);
begin
// do stuff for this component
if Assigned(FOriginalFormClose) then
FOriginalFormClose(Sender, Action);
end;
Naturally the as cast ties this component to being owned by forms but if you want more flexibility you could easily cater for that.
This is a direct answer to the question that you asked, but it would be remiss of me not to question your overall design. If you want a component to live on every form in your app then surely you should derive a subclass of TForm that contains your customisations. Then make every form in your app be based on that common base form class.
That approach has many other benefits. For example, #LachlanG adds the following very apt comment with which I wholeheartedly concur:
Having a component meddle with it's owning form is undesirable. The vast majority of components should be self contained entities, altering the component owner breaks the expected contract of a Delphi component.
The common base form approach solves this by placing the code that works with the form inside the form.
If you are do go down the route of having a common base form then you should override DoClose rather than using the OnClose event. Always use the DoXXX event raisers rather than the events themselves when you are creating a common base class or a component.
I need to loop through Components and assign an event handler (for example Dynamically assigning OnClick event for all TButton to
ShowMessage('You clicked on ' + (Sender as TButton).Name);
The problem is that in some cases I already assigned the TButton OnClick event.
Is there a way to solve the problem?
Let's imagine I have Button1 for which the harcoded onclick event handler is:
ShowMessage('This is Button1');
After my "parsing" I would like that the full event handler for Button1 becomes:
ShowMessage('This is Button1'); // design time event handler code
ShowMessage('You clicked on ' + (Sender as TButton).Name); // runtime added
Note: I am looking for a soliution that allows me to use TButton as it is without inheriting from it.
you could look for an assignment of OnClick before overwriting it, persist this and use it in your new handler - basically chaining the events.
Something like this:
var original : TNotifyEvent;
original := Component.OnClick;
Component.OnClick := NewMethod;
and then in your NewMethod:
if assigned(original) then original(Sender);
you'll likely not want a single original variable but instead hold a collection keyed on the component.
If you are writing the replacement handlers yourself you could do the following:
Before assignment of the new handler, store a reference to the object and a reference to the original handler in a list or other structure.
In your replacement handler, included code to check the list to see if the current object has an entry. If it does, call the stored method before doing your own work.
This is fairly lightweight (a list where each entry is two pointers). With a little extra work (to support ordering) you could probably even chain more than one replaced handler.
You can't do that directly as Delphi does not support chained events (I'm assuming VCL Delphi).
So anything that you want to do that involves directly assigning the OnClick event will not work. What you might do is create an interceptor component, that is, something with this signature:
type
TButton = class(stdctrls.TButton)
You'll have to override the OnClick property so that the write event stores the event handler into an internal list, available to class.
property OnClick: TNotifyEvent read GetOnClick write SetOnClick;
then on the SetOnClick handler you'll be able to just store your event handler in the internal list.
Now, you will have to override the Click procedure so that it invokes every delegate (every event handler) in turn.
procedure Click; override;
And that will do it.
Note that after all you'll be kind of subclassing the TButton class, even if you're using the same name... Anyway that's the best solution I can think of. The cool thing about this is that you can still use assignments without having to know about chained events, you can just do:
MyButton.OnClick := MyHandler
and it will get automatically chained with the rest of event handlers.
I have some code that does some setup of internal objects in the Loaded() function. However, some external objects are not completely created yet, but are AFTER the Loaded() function is complete. What function does Delphi call after it calls Loaded()?
Better yet what is the creation sequence of a component?
Basically I have a TCP Server and Client. Most people will place those two components into two separate applications, some will place them in the same application for local access.
My Client tries to fetch data from the server in OnLoaded(), but the server may not be up yet! I want to know if another function is called after all the OnLoaded()'s are called.
Loaded is called immediately after the dfm is streamed in, and shouldn't be used to access the server. Your best bet is probably to post a custom message to yourself in the constructor, and have a message handler procedure that responds to that message. Posting the message puts it into the end of the message queue, and therefore it won't get processed until all the other messages ahead of it has been handled. This should delay things long enough for your components to be fully constructed for use.
I've used breakpoints in some components and firmly established that AFTERCONSTRUCTION is called BEFORE LOADED not AFTER.
I've also done the same thing on a FORM and firmly established that AFTERCONSTRUCTION is called AFTER LOADED not BEFORE.
Bear in mind that AfterConstruction is a method in TObject, but Loaded is not. It follows that Loaded is generated by code that may not necessarily put it in a specific order relative to AfterConstruction, since Loaded is not actually part of the construction sequence of a TObject and AfterConstruction is.
Indeed, if you study the RTL source, you will see that Loaded is not even invoked by any self.method of a TComponent, but is in fact invoked by a stream reader that is reading the DFM, and that will most probably be happening under the control of an "owner" component. I strongly suggest therefore that its relationship relative to the execution of AfterConstruction is not really guaranteed. The fact that it appears in a particular order for a form is because the form is most likely the component to initiate stream reading. In other words, it smacks of a convenient accident that Loaded is before AfterConstruction in a form.
Further research shows that NON-FORM components that include the following code may never call the event handler.
procedure Txxx.AfterConstruction; override;
begin
inherited AfterConstruction;
if Assigned(FOnCreate) then FOnCreate(Self);
end;
The reason is that AfterConstruction, if invoked before properties are loaded, will find FOnCreate has not been assigned yet!
In such cases, you really HAVE to use the following:
procedure Loaded; override;
begin
inherited Loaded;
if assigned(OnLoaded) then OnLoaded(self);
end;
Like I said, this will produce different outcomes for a component owned by a form than it would for the form itself! The TForm component is usually the invoker of the DFM stream reader and it is the stream reader that calls Loaded for each component it reads from the form. This process starts (fortunately) BEFORE the form's AfterConstruction, but each component that is loaded by that reader gets its AfterConstruction method called BEFORE its loaded method.
QED.
The great irony is that the Delphi 6 help file says "The AfterConstruction method implemented in TObject does nothing. Override this method when creating a class that takes some action after the object is created. For example, TCustomForm overrides AfterConstruction to generate an OnCreate event."
What it omits to say is that if you try this on anything other than a TCustomForm (which does it already), it doesn't work! Because only a form (which has it already) will load its OnCreate property before calling AfterConstruction. Any other component won't, because the DFM reader invoked by the form calls AfterConstruction before Loaded! A clear case of Borland et. al. not understanding their own code, or at best, writing a help file entry that implies something is possible when in fact it is not.
Note, if your component is not on a form and is created at runtime (even if this is as an "owned" component), its "Loaded" method will NOT be called, because there was no stream reader involved.
Another point of interest is something that "Dr" Bob Swart wrote some time ago about AfterConstruction, namely that it represents the point where virtual methods can be invoked. Evidently this is only partly true: if a form's Loaded method is invoked BEFORE AfterConstruction, then you would not be able to invoke any virtual methods from Loaded if that were true. This is not the case (obviously) because Loaded is itself a virtual method! Evidently, Loaded for a form is called between the constructor and AfterConstruction by the stream reader. It begs the question: by what method is the stream reader actually invoked? My guess is either that it runs under control of the application (not the form) and that it deliberately invokes AfterConstruction differently for a form than for other components, OR that it is the last thing the form's constructor does after having created the VMT and hence the last thing that occurs before AfterConstruction is called in the form. Accordingly all the AfterConstruction-Loaded couplets of components owned by the form are called before the form's AfterConstruction is called. Tracing the calls also shows that mostly AfterConstruction is called for ALL of those components before ALL of their loaded methods are called. I didn't however test the case where there are hierarchical "parents" (such as panels with components on them), so there may be variations on this.
Normally you would override TObject.AfterConstruction for that purpose.
The order of execution is:
each Component.AfterConstruction in creation order
(Form or DataModule).Loaded
each Component.Loaded in creation order
(Form or DataModule).AfterConstruction
Trace:
Debug Output: button AfterConstruction Process Project2.exe (4876)
Debug Output: Form Loaded Process Project2.exe (4876)
Debug Output: button Loaded Process Project2.exe (4876)
Debug Output: Form AfterConstruction Process Project2.exe (4876)
I'm not sure what you mean with
the server may not be up yet
Anyway, if the client and the server are both on the same application form or datamodule, I see alternatives:
You may "force" the system to create the server before the client and up the server in the server's OnLoad and it will be up at the client OnLoad, because documentation says:
When the streaming system loads a form or data module from its form file, it first constructs the form component by calling its constructor, then reads its property values from the form file. After reading all the property values for all the components, the streaming system calls the Loaded methods of each component in the order the components were created. This gives the components a chance to initialize any data that depends on the values of other components or other parts of itself.
Inform the "client" whenever the server is UP to let it initialize (pull data from the server). You can use a direct method call, post a message or whatever you feel comfortable with.
Let the client stand up the server inside it's own OnLoad method.
Why not use the onCreate event of the main Form ?