Calling a class method from a custom method - delphi

I'm relatively new to Delphi, so please pardon my ignorance.
As an exercise, I am writing a little MasterMind game. The idea is that, when the user derives the correct number, a custom method (Congrats) executes.
One of the options in this method is to play again. The obvious step following this is that the form must be reset to its default ("start up") state. I have created a type method for that (Resetform, declared as a method in public, as it needs to access the controls). I can't call that from Congrats, but I noticed that I can call it from other event handlers.
Is it possible to do this, and if so, how do I go about it?
Works from event handler, but not from custom procedure.

The method Resetform is a method of your form, which is why you can call it from event handler of your form, they reside in the same object.
To call your Resetform from outside the context of your form, you need a reference to your form, otherwise the code won't know which instance of your form it needs to call Resetform on.
Now, assuming you call Congrats from your form, you could add a parameter and call it like this:
Congrats(Self)
And your procedure could be implemented as
procedure Congrats(AForm : TMyForm); // "TMyForm" being the class of your form
begin
...
AForm.Resetform;
end;
But then, you could as well make Congrats a method of your form instead.
If Congrats is not called from your form, then you will need to find another way to pass your form's reference to the function, or use another approach.

Related

JS callback to Vaadin component

I have a JS component (SigPlot) that I need to read click values from. I have instantiated SigPlot inside of a VerticalLayout, where I also instantiate a DIV to pass to the SigPlot constructor. I am not sure if this is a valid way, but it works.
Now I need to read CLICKS but I am having troubles finding correct way to do this. Can someone pass some words of wisdom?
In my constructor for my VIEW, it use addAttachListener to start my JS code using.
div.addAttachListener(e->{
e.getUI().getPage().executeJs("myInit($0)",div);
});
How can I register a click listener to this?
Regards
As long as it's just a regular DOM event listener, then something like this should work:
div.getElement().addEventListener("click", event -> Notification.show("Clicked"));
If you need to do something on a more granular level, then your might want to expose callbacks as #ClientCallable and then use executeJs to run some short JavaScript snippets to set up listeners that delegate to those methods.

How to call FormClose event handler?

I try to call the method FormClose, but I have a problem with its parameters when I try:
FormName.FormClose(nil, CaFree);
Normally I can call a component's event handler with a parameter using nil or sender as TOBject. But now I get the error:
Constant object cannot be passed as var parameter
I have tried many combinations for these two TObject and TAction values. For TObject I tried `sender as TObject', and for TAction all parameters like CaFree etc.
The second parameter is a var parameter which is what the compiler error message is telling you. So you need to pass a variable. You cannot pass a literal.
var
Action: TCloseAction;
....
Action := caFree;
Name.FormClose(nil, Action);
Note that you almost certainly should not be doing this. You are not meant to call event handlers directly. The framework will call them at the appropriate time. I think it exceedingly likely that you are mistaken in thinking that you need to fire this event handler directly, or even execute the code outside the normal scenario of the form closing.
As a general rule, if you need to invoke code in an event handler directly then the normal approach is to first extract it to a separate method which can readily be called directly. Then refactor the event handler to call that separate method.

A form creates twice on application's FormCreate event

I'm trying to load a form when my main form starts. And I do it using ShowModal. Also that form is not an auto create form so I have to create it first with application.CreateForm.
My problem is when I try to load the form in FormCreate event, it load the form twice and when I close the form my whole app closes.
Here is my code:
procedure Tfrm_main.FormCreate(Sender: TObject);
var
username, password : string;
begin
username := ini.ReadString('user','username','');
if username = '' then
begin
application.CreateForm(Tfrm_user,frm_user);
frm_user.ShowModal;
end;
end;
How can I fix this problem? Thanks.
The code in your question is called from the call to Application.CreateForm that creates the main form. Then you call Application.CreateForm again, recursively, and that results in the Tfrm_user instance becoming the main form.
It's well known that the first form created by Application.CreateForm becomes the main form. Here, you call Application.CreateForm to create the main form. But before the code of Application.CreateForm gets to determine what the VCL considers to be the main form, the recursive call to Application.CreateForm executes. All the way to the end, and in doing so determines the main form to be the secondary form made with the recursive call.
You then show the secondary form modally. Later you call Application.Run which shows the VCL main form, your secondary form. Again. And then you close it. Which closes the program, because that's what happen when you close the VCL main form.
My advice is to call Application.CreateForm exactly once in the life of your program. So, in the OnCreate handler create the other form like this:
frm_user := Tfrm_user.Create(Application);
Or perhaps let the main form be the owner. And certainly consider not using the global variable frm_user. I'd remove that.
Or another option would be to show the secondary form modally before you make the call to Application.CreateForm.
You're getting bitten by an order-of-operations issue.
If you look at the code to TApplication.CreateForm, you'll see that things happen in this order:
Create a new form of the appropriate type.
If FMainForm = nil, assign this new form to it.
Later, TApplication.Run calls FMainForm.Show;
But when the program is setting up your principal form:
It first creates it, which calls the OnFormCreate event handler. Then the event handler calls TApplication.CreateForm, (before the first one has returned) which creates the new form, sees that FMainForm is not assigned and assigns this form to it, then returns.
Your FormCreate then shows this form as a modal, then returns.
Things unwind a little, and we're back to the original TApplication.CreateForm, which goes on to the next part, sees FMainForm already assigned, and does not assign it.
Then it returns, and TApplication.Run gets called, which shows the main form... the wrong one.
IF you want to create the new form, call the constructor instead: frm_user := Tfrm_user.Create(Application); Really, TApplication.CreateForm should only be used the one time, to set up the main form. It's kind of a hack that can make trouble for you if you don't know exactly how it works.

What is called after Loaded() in Delphi

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 ?

How do events in Delphi work?

I'm trying to get console output from a program using this library uZpRunConsoleApp.
It's well documented but I've not used Delphi for very long and I don't understand how events work.
From what I can tell I need to call ExecuteConsoleApp with my application, which I have working with no output. It looks like that method wants me to specify a function it can fire when an event happens but I don't understand how to do that.
I hope somebody can spread some light here.
I didn't post any code since this isn't really a code specific problem, but if somebody wants what I have so far I'll edit for them.
Yeah, an event handler is basically a reference to a function. If you've ever used callbacks, it's basically the same idea. If not, here's a quick overview:
The event type is defined like this:
TZpOnNewTextEvent = procedure(const Sender: TObject;
const aText: string) of object;
What that means is that it's a reference to an object method (of object) with a signature that looks like this:
type
TMyObject = class (TMyObjectAncestor)
//stuff here
procedure MyEventHandler(const Sender: TObject; const aText: string);
//more stuff here
end;
The of object bit is important. This is specifically a method reference and not a reference to a standalone function.
What the event handler is for is to allow you to customize the way ExecuteConsoleApp works. It's almost exactly like adding code to a button in the Form Designer. You place the button on the form, then you assign an event handler to its OnClick event that customizes the button by adding in code that executes when the button is clicked. The difference is that here, you don't have a Form Designer to wire it together for you.
Fortunately, the syntax is pretty simple. For a procedure (whatever) of object, you pass the event handler by just giving the name. Throw Self.MyEventHandler in the appropriate place in the parameter list, and it will work.

Resources