Delphi - Assign Form OnClose on an existent one - delphi

I have many Forms on my project that uses the Form OnClose event.
However, I need to add another "generic" OnClose on all forms in runtime.
There is any way to just add the new event method, instead of replace it? So, the form will trigger both OnClose events.

Only one handler can be assigned to an event at a time.
What you could do is assign the "generic" handler to each Form's OnClose event, and then have each Form override its virtual DoClose() event to do their local work. It can call the inherited DoClose() method when ready to call the generic handler. For example:
type
TMyForm = class(TForm)
protected
procedure DoClose(var Action: TCloseAction); override;
end;
procedure TMyForm.DoClose(var Action: TCloseAction);
begin
// do something here...
inherited; // <-- call OnClose handler
end;
The alternative is to implement a multicast delegate for the actual event handler, and then the delegate can call other handlers as needed. Here are a few blogs on that topic:
Multicast events using generics
MultiCast Events - Part 1
MultiCast Events - Part 2
MultiCast Events - Conclusion
Alternatively, you can ignore the OnClose event altogether and implement an Observer Pattern instead (using DoClose() to call the observers). Here are a few blogs on that topic:
Observer Design Pattern in Delphi
Delphi and the Observer Pattern
The Observer Pattern

Related

TCard class does not have public OnShow and OnHide events

In a Delphi 10.4 VCL Application, TCard (as container-item of TCardPanel) does not have public OnShow and OnHide events (like TTabSheet has).
Therefore, the TCard.OnEnter event-handler is NOT triggered when a specific TCard is ACTIVATED. The TCard.OnEnter event-handler is triggered ONLY when e.g. clicking on a control on the TCard.
Example code:
CardPanel1.ActiveCard := Card2;
In this case, the TCard.OnEnter event is NOT triggered!
Is it possible to upgrade the TCard class with public OnShow and OnHide events? Or is it possible to simulate those events?
Use the OnCardChange event of TCardPanel and compare PrevCard and/or NextCard with your actual card instances.

How to execute an ActionList item from a TListViewItem

I'm trying to execute an action (TakePhotoFromCameraAction) in a TActionList, when a TListViewItem is selected.
Neither TlistView nor TListViewItem have an Action property, so I've tried calling ActionList[0].Execute in the event, but nothing happens.
Any ideas?
Further:
The code is very simple, as it was just a test for this problem. I was focussing on the ActionList as that was what I will use (when I sort it out).
Button1 doesn't work (it always fails, even when button 2 doesn't), whereas the (new) Button2 does work OK.
type
TForm1 = class(TForm)
ActionList1: TActionList;
Memo1: TMemo;
TakePhotoFromCameraAction1: TTakePhotoFromCameraAction;
Button1: TButton;
Button2: TButton;
procedure Button1Click(Sender: TObject);
procedure Button2Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.fmx}
procedure TForm1.Button1Click(Sender: TObject);
begin
ActionList1[0].Execute;
end;
procedure TForm1.Button2Click(Sender: TObject);
begin
if TakePhotoFromCameraAction1.Execute
then
Memo1.Lines.add('Photo OK')
else
Memo1.Lines.add('Photo Fail');
end;
You can use good old tag property of TListViewItem to store pointer to TAction you want to use with this item. Of course, you can't set it in object inspector, but can do it programmaticaly in TForm.onCreate event or some other convenient place. It has type NativeInt which has the same size as pointer be it 32-bit or 64-bit architecture, so it should work properly.
Something like this:
//in formCreate or other place to initialize actions:
TakePhotoItem.Tag:=NativeInt(TakePhotoFromCameraAction);
SavePhotoItem.Tag:=NativeInt(SavePhotoAction);
//...
//onitemchange event handler
if AItem.Tag<>0 then
TAction(AItem.Tag).Execute;
Maybe it's better to introduce your own descendant of TListViewItem which has Action property, that way you'll have to populate your listview in code only, adding not basic TListViewItem, but TActionListViewItem (name of your class), that has more work to do but will yield more understandable code.
There is no difference (except being ugly) to call ActionList1[0].Execute; versus Action1.Execute;.
You didn't show the the .fmx file so I can't know what linkage you may have setup between the components, but, it seems you haven't assigned anything to the actions OnExecute event, and therefore do not get the expected response to the Execute call.
The FMX Version of the documentation is not very clear, but the VCL version is (IMO) better (In a brief test I don't see any difference in actual functionality):
From documentation :
Responds when a client control "fires".
Execute is called automatically when a client control "fires" (for
example, when the user clicks a button or selects a menu item). It
returns True if an event handler is found to handle the action, False
if there was no event handler or if the action was not enabled.
but you can ofcourse also call Execute directly as you tried. And further
Execute first ensures that the action is updated. Then, if the Enabled
property is True, it attempts to handle the action by generating an
OnExecute event on the action list that contains this action (if the
action belongs to an action list). If the action list's OnExecute
event handler does not handle the action, Execute generates an
OnActionExecute event on the application itself. If neither the action
list nor the application handles the action in response to these
events, Execute generates an OnExecute event on itself. If this action
has no OnExecute event handler, Execute instructs the application to
locate the current target control and call the ExecuteTarget method,
which is the mechanism by which predefined action classes perform
their function.
Note that you can handle the actions in TActionList.OnExecute or in the TAction.OnExecute

execute an event but the coding from my own class [duplicate]

I have one button on my form. Following is the click event of that button
procedure Form1.btnOKClick(Sender: TObject);
begin
//Do something
end;
This event will be called only when I click the button, right?
How can I call this event automatically without any user intervention?
The best way to invoke the OnClick event handler attached to a control is to call the Click method on the control. Like this:
btnOK.Click;
Calling the event handler directly forces you to supply the Sender parameter. Calling the Click method gets the control to do all the work. The implementation of the windows message handler for a button click calls the Click method.
But I second the opinion expressed in whosrdaddy's answer. You should pull out the logic behind the button into a separate method.
Do not put your businesslogic into event handlers. This will make your code unreadable when the application grows larger.
Normally you would do this:
procedure TForm1.DoSomething;
begin
// do something
end;
procedure TForm1.btnOKClick(Sender: TObject);
begin
DoSomething;
end;
then all you need to do is call DoSomething from other parts in your code
You can call this event in code like any other method.
...
btnOkClick(Self.btnOk); // Sender in this case is the btnOk
...
The Sender can be whatever object you like or nil.

Implementing Button Click in Firemonkey

I am missing TButton.Click Method in FireMonkey TButton.
Is there any way to fire click event in the code in fire monkey? If there is no such event, just use the click method to fire the action assigned to the button?
Well, you can simply write:
Button1.OnClick(Button1);
If there is an action attached to the button you can invoke it with
Button1.Action.Execute;
But that is not really to be recommended. The right way to do this is to create a method to do the work. Then call that method from either your OnClick event handler, or the other location in your code that wants to invoke this action. Like this:
procedure TForm1.DoSomething;
begin
// do whatever it is
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
DoSomething;
end;
Then anywhere in your code you can just call DoSomething. It's best to leave GUI event handlers just for handling GUI events.
You can use this method :
first declare this new type to access TButton Click procedure :
type
TButtonHack = type TButton;
Then caste your button to TButtonHack class and call Click procedure :
TButtonHack(Button1).Click;

How to call events in delphi without user intervention?

I have one button on my form. Following is the click event of that button
procedure Form1.btnOKClick(Sender: TObject);
begin
//Do something
end;
This event will be called only when I click the button, right?
How can I call this event automatically without any user intervention?
The best way to invoke the OnClick event handler attached to a control is to call the Click method on the control. Like this:
btnOK.Click;
Calling the event handler directly forces you to supply the Sender parameter. Calling the Click method gets the control to do all the work. The implementation of the windows message handler for a button click calls the Click method.
But I second the opinion expressed in whosrdaddy's answer. You should pull out the logic behind the button into a separate method.
Do not put your businesslogic into event handlers. This will make your code unreadable when the application grows larger.
Normally you would do this:
procedure TForm1.DoSomething;
begin
// do something
end;
procedure TForm1.btnOKClick(Sender: TObject);
begin
DoSomething;
end;
then all you need to do is call DoSomething from other parts in your code
You can call this event in code like any other method.
...
btnOkClick(Self.btnOk); // Sender in this case is the btnOk
...
The Sender can be whatever object you like or nil.

Resources