Why loaded method is ignored? - delphi

I wonder why, but I can not simply debug my simple program. Loaded method is ignored, it is never executed. No idea why. Look:
TGridObj = class (TComponent)
private
FPen1:TPen;
FBrush1:TBrush;
FChange:TNotifyEvent;
protected
procedure Loaded; override;
public
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
published
property OnChange:TNotifyEvent read FChange write FChange;
property Pen1:TPen read FPen1 write FPen1;
property Brush1:TBrush read FBrush1 write FBrush1;
end;
.
.
.
constructor TGridObj.Create(AOwner: TComponent);
begin
inherited Create(AOwner);
FPen1:=TPen.Create;
FPen1.OnChange:=FChange;
FBrush1:=TBrush.Create;
FBrush1.OnChange:=FChange;
end;
destructor TGridObj.destroy;
begin
FPen1.Free;
FBrush1.Free;
inherited;
end;
procedure TGridObj.Loaded();
begin
inherited Loaded;
ShowMessage(''); // this is never executed;
FPen1.OnChange:=FChange;
FBrush1.OnChange:=FChange;
end;
.
.
procedure TForm1.FormCreate(Sender: TObject);
begin
Grid:=TGridObj.Create(nil);
Grid.OnChange:=ev1.OnChange;
Form1.InsertComponent(Grid);
end;
Thanx

Loaded is only called when the component's properties are streamed from the form file. Since you are creating it at runtime, Loaded does not get called. This is by design.
Your code needs some work anyway to allow for the OnChange event to be modified at runtime and have that change filter down to the pen and brush. I'd do it like this:
TGridObj = class (TComponent)
private
FPen1: TPen;
FBrush1: TBrush;
FChange: TNotifyEvent;
procedure DoChange(Sender: TObject);
public
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
published
property OnChange: TNotifyEvent read FChange write FChange;
property Pen1: TPen read FPen1;
property Brush1: TBrush read FBrush1;
end;
constructor TGridObj.Create(AOwner: TComponent);
begin
inherited;
FPen1 := TPen.Create;
FPen1.OnChange := DoChange;
FBrush1 := TBrush.Create;
FBrush1.OnChange := DoChange;
end;
destructor TGridObj.Destroy;
begin
FBrush1.Free;
FPen1.Free;
inherited;
end;
procedure TGridObj.DoChange(Sender: TObject);
begin
if Assigned(FChange) then
FChange(Sender);
end;
Now there's no need for Loaded or anything like that. Because you wait until the OnChange events of the pen and brush actually fire before accessing FChange.
By the way, in your code it's a mistake to add property setters for Pen1 and Brush1 that modify the underlying fields. That leads to leaks and all sorts of mess. Also, be warned that exposing the pen and brush as public properties allows clients of TGridObj to change the OnChange event. And that subverts TGridObj.OnChange.

OnLoaded will only be executed if the component is loaded from a form resource (dfm file).
If the component is created at run time in code only, it will not be executed.
Update:
I recommend to design components so that they can be created and configured at run time too - which means I avoid overriding OnLoaded. The advantage is that no package installation / component registration is needed.

Related

How variables are catched inside anonymous method?

Is this code valid:
TMyObj = class(Tobject)
public
FThread: TThread;
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
end;
constructor TMyObj.Create(AOwner: TComponent);
begin
inherited;
FThread:= TThread.createAnonymousThread(
procedure
begin
while not FThread.CheckTerminated do sleep(10000);
end);
FThread.start;
end;
destructor TMyObj.Destroy;
begin
inherited;
end;
my question is after i do
destructor TMyObj.Destroy;
begin
inherited;
end;
I mean after the object TMyObj is free, what will be the value of FThread inside
FThread:= TThread.createAnonymousThread(
procedure
begin
while not FThread.CheckTerminated do sleep(10000);
end);
No, this code is not valid.
It is important to know that FThread is not captured, but the reference to the containing instance. So the anonymous method code rather looks like this:
FThread := TThread.createAnonymousThread(
procedure
begin
while not Self.FThread.CheckTerminated do sleep(10000);
end);
The compiler generates an object that implements the anonymous method with a field called Self that captures the reference to the TMyObj instance. If that instance gets destroyed while the anonymous method is alive within the thread, it will have a dangling reference.
A thread created with TThread.CreateAnonymousThread() is destroyed automatically when it finishes. So if you store that in a field for later access, you at least need to turn FreeOnTerminate off and not reference it via a captured variable to the thread itself, but rather with TThread.Current. Within the anonymous method, this will give you the thread that method is running in.

Forcing a child form with CreateParam's Params.WndParent?

In some legacy D7 code I inherited and am migrating to XE5, I found the code below.
The comment states it's tricking windows into thinking it's a child form if it's created from a non-WinControl. There is one place in the code base where Create is called with AOwner as nil. (A form is available at the time of that call, so not sure why they did that...)
Any suggestions as to what the programmer's goal was?
private
FParentWinControl: TWinControl; {Don't mess with! Used in CreateParams}
procedure TFormX.CreateParams(var params: TCreateParams); override;
public
constructor TFormX.Create( AOwner: TComponent); reintroduce;
end;
constructor TFormX.Create( AOwner: TComponent);
begin
if AOwner IS TWinControl then
FParentWinControl := TWinControl(AOwner)
else
FParentWinControl := NIL;
inherited Create(AOwner);
end; { Create }
procedure TFormX.CreateParams(var params: TCreateParams);
begin
inherited CreateParams(params);
if (NOT fCreateParamsHasBeenRun) then
begin
fCreateParamsHasBeenRun := TRUE;
if Assigned(FParentWinControl) then
Params.WndParent := FParentWinControl.Handle; {tricks windows into thinking it's a child form}
end;
end;
This code predates and loosely mimics the PopupMode and PopupParent properties that were added to TCustomForm in Delphi 8. Assuming AOwner is another Form, use those properties in modern Delphi versions, eg:
constructor TFormX.Create( AOwner: TComponent);
begin
inherited Create(AOwner);
if AOwner Is TCustomForm then
PopupParent := TCustomForm(AOwner);
end;
Also, the use of fCreateParamsHasBeenRun was wrong. CreateParams() is called every time the Form's window is (re)created, so the WndParent needed to be applied every time, not conditionally. If you need to keep the CreateParams() logic (such as if AOwner is a non-TCustomForm windowed control), you need to remove fCreateParamsHasBeenRun.

Delphi Web Script (DWScript) link a script method to an external control event

I'm wondering if DWScript supports using a script method as an event handler for a control on a Delphi form. For example I want to link a TButton OnClick event to a method that exists in script.
I am able to do this with the RemObjects Delphi script engine by calling GetProcMethod which returns a TMethod object. I then use SetMethodProp to assign the script method to the OnClick event of a button.
procedure LinkMethod(SourceMethodName: String; Instance: TObject; ScriptMethodName: String);
var
ScriptMethod: TMethod;
begin
ScriptMethod := ScriptEngine.GetProcMethod(ScripMethodName);
SetMethodProp(Instance, SourceMethodName, ScriptMethod);
end;
I would like to do this in DWScript instead of the Rem objects script engine as it does some other stuff that I need.
I decided to go with RemObjects instead. It was the easiest to use and does what I need.
AFAIK DWScript doesn't support directly what you're trying to achieve but it could be implemented in different manner.
I'll try to post some source code how it could be implemented but you will probably need to adapt it to your needs.
First, declare a little wrapper class which should be separate for each script method:
type
TDwsMethod = class
private
FDoExecute: TNotifyEvent;
FScriptText: string;
FDws: TDelphiWebScript;
FLastResult: string;
FMethod: TMethod;
protected
procedure Execute(Sender: TObject);
public
constructor Create(const AScriptText: string); virtual;
destructor Destroy; override;
property Method: TMethod read FMethod;
property LastResult: string read FLastResult;
published
property DoExecute: TNotifyEvent read FDoExecute write FDoExecute;
end;
constructor TDwsMethod.Create(const AScriptText: string);
begin
inherited Create();
FDoExecute := Execute;
FScriptText := AScriptText;
FDws := TDelphiWebScript.Create(nil);
FMethod := GetMethodProp(Self, 'DoExecute');
end;
destructor TDwsMethod.Destroy;
begin
FDws.Free;
inherited Destroy;
end;
procedure TDwsMethod.Execute(Sender: TObject);
begin
ShowMessage('My Method executed. Value: ' + FDws.Compile(FScriptText).Execute().Result.ToString);
end;
Now we must create an instance of this class somewhere in our code (e.g. in form's create event):
procedure TMainForm.FormCreate(Sender: TObject);
begin
FDWSMethod := TDwsMethod.Create('PrintLn(100);'); //in constructor we pass script text which needs to be executed
//now we can set form's mainclick event to our DWS method
SetMethodProp(Self, 'MainClick', FDWSMethod.Method);
end;
procedure TMainForm.FormDestroy(Sender: TObject);
begin
FDWSMethod.Free;
end;
Now when we call MainClick our script is compiled and executed:

Can TTimer object be a field of a Delphi class?

I started learning Delphi two days ago but I got stuck. I broke down because nothing goes my way so I decided to write here. I wanted to create class that would have a field with its own TTimer object and which will perform some action at some time interval. Is it even possible? Suppose we have such code:
Sth = class
private
public
clock:TTimer;
procedure clockTimer(Sender: TObject);
constructor Create();
end;
constructor Sth.Create()
begin
clock.interval:=1000;
clock.OnTimer := clockTimer;
end;
procedure Sth.clockTimer(Sender: TObject);
begin
//some action on this Sth object at clock.interval time...
end;
My similar code copiles but it doesn't work properly. When I call the constructor the program crashes down (access violation at line: clock.interval:=1000;). I don't know what
Sender:TObject
does but I think that's not the problem. Is it possible to create such class I want to?
You have not created the timer. Declaring a variable is not enough. You do need to create the timer.
constructor Sth.Create()
begin
clock := TTimer.Create(nil);
clock.interval:=1000;
clock.OnTimer := clockTimer;
end;
And you should destroy it too. Add a destructor to the class
destructor Destroy; override;
and implement it like this
destructor Sth.Destroy;
begin
clock.Free;
inherited;
end;
I would also recommend that you make your clock field have private visibility. It's not good to expose the internals of a class like that.
TMyClass = class
private
FClock: TTimer;
procedure ClockTimer(Sender: TObject);
public
constructor Create;
destructor Destroy; override;
end;
....
constructor TMyClass.Create
begin
inherited;
FTimer := TTimer.Create(nil);
FTimer.Interval := 1000;
FTimer.OnTimer := ClockTimer;
end;
destructor TMyClass.Destroy;
begin
FTimer.Free;
inherited;
end;
Note that I have included calls to the inherited constructor and destructor. These are not necessary in this class since it derives directly from TObject and the constructor and destructor for TObject is empty. But if you change the inheritance at some point, and make your class derive from a different class, then you will need to do this. So, in my view, it is good practise to include these calls always.

How to simulate an OnDestroy event on a TFrame in Delphi?

How can i simulate an OnDestroy event for a TFrame in Delphi?
i nievely added a constructor and destructor to my frame, thinking that is what TForm does:
TframeEditCustomer = class(TFrame)
...
public
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
...
end;
constructor TframeEditCustomer.Create(AOwner: TComponent)
begin
inherited Create(AOwner);
//allocate stuff
end;
destructor TframeEditCustomer.Destroy;
begin
//cleanup stuff
inherited Destroy;
end;
The problem with this is that by the time my destructor runs, controls on the frame have been destroyed and are no longer valid.
The reason for this is in the containing form's destructor, which it uses to fire an OnDestroy event:
destructor TCustomForm.Destroy;
begin
...
if OldCreateOrder then DoDestroy; //-->fires Form's OnDestroy event; while controls are still valid
...
if HandleAllocated then DestroyWindowHandle; //-->destroys all controls on the form, and child frames
...
inherited Destroy; //--> calls destructor of my frame
...
end;
The destructor of my frame object is being called when the form's destructor runs. Problem with this is that it's too late. The form calls DestroyWindowHandle, which asks Windows to destroy the form's window handle. This recursively destroys all child windows - including those on my frame.
So when my frame's destructor runs, i attempt to access controls that are no longer in a valid state.
How can i simulate an OnDestroy event for a TFrame in Delphi?
See also
Simulating OnCreate and OnDestroy for a Frame?
How to Implement the OnCreate event for a Delphi TFrame object
Embargadero QC#1767: TFrame misses OnCreate, OnDestroy, OnShow
You need to add a WM_DESTROY handler and check for csDestroying in the ComponentState so it's only caught when actually destroying, and not when recreating the handle.
type
TCpFrame = class(TFrame)
private
FOnDestroy: TNotifyEvent;
procedure WMDestroy(var Msg: TWMDestroy); message WM_DESTROY;
published
property OnDestroy: TNotifyEvent read FOnDestroy write FOnDestroy;
end;
procedure TCpFrame.WMDestroy(var Msg: TWMDestroy);
begin
if (csDestroying in ComponentState) and Assigned(FOnDestroy) then
FOnDestroy(Self);
inherited;
end;
That will only work if the frame's window handle has actually been created. There isn't another good hook point, so if you want to ensure it's always called you'll need to set a flag in WMDestroy and fall back to calling it in the destructor if that isn't hit.
The window handles themselves are all cleared in WM_NCDESTROY, which is called after all of the descendant WM_DESTROY messages return, so the form and all of its childens' handles should still be valid at this point (ignoring any that were freed in the form's OnDestroy).
Sounds more like OnClose than OnDestroy.
Anyway, I just inherited all my frames and forms from a base ancestor, and the form's onclose calls then all frames in the component hierarchy.
(It's just an idea but I haven't got the time right now to construct a proof of concept, but I'll share it none the less:)
If it's a problem with the Windows handle(s), you should check wether you're able to attach a Windows' event callback pointer that gets called when the frame's Windows handle ceases to exists. Perhaps with a function like RegisterWaitForSingleObject
Another option is to override AfterConstruction and BeforeDestruction
Something like this:
TMyFrame = class(TFrame)
private
FOnCreate: TNotifyEvent;
FOnDestroy: TNotifyEvent;
protected
procedure DoCreate; virtual;
procedure DoDestroy; virtual;
public
procedure AfterConstruction; override;
procedure BeforeDestruction; override;
property OnCreate: TNotifyEvent read FOnCreate write FOnCreate;
property OnDestroy: TNotifyEvent read FOnDestroy write FOnDestroy;
end;
implementation
procedure TMyFrame.AfterConstruction;
begin
inherited;
DoCreate;
end;
procedure TMyFrame.BeforeDestruction;
begin
inherited;
DoDestroy;
end;
procedure TMyFrame.DoCreate;
begin
if Assigned(FOnCreate) then
FOnCreate(Self);
end;
procedure TMyFrame.DoDestroy;
begin
if Assigned(FOnDestroy) then
FOnDestroy(Self);
end;

Resources