Firemonkey TFrame - how to fire ancestor TFrame events? - delphi

I created a base TFrame class, eg TBasicFrame
There is one button called btnTest, which do a simple task
showmessage('test');
On another TForm or TFrame, I place this TBasicFrame on it. However, when I click this btnTest on this or TForm/TFrame which has an instance of TBasicFrame on it, the showmessage does not run.
What must I do to ensure that all code in the ancestor frame will execute? That means the showmessage('test') will run even when new instances of TBasicFrame is placed anywhere.
Any advice?

I think, you overrided this event by simple double click on button (on object of your Frame) in IDE. So you dont have original call event.
Check *.fmx files of TBasicFrame and TForm with object TBasicFrame.

Related

Delphi control MouseDown/Move/Up passed to underlying panel

I'm using Delphi 10.2.3. I have a descendent of a TCustomPanel for which I've overridden the MouseDown/MouseMove/MouseUp events to allow selection, dragging, custom painting, etc. Descendents of this panel will have various controls - TImage, TStaticText, TStringGrid, etc. - added dynamically at runtime, and some will have several such controls. The problem is that clicking anywhere in the panel that has some other child control in that location, that control gets the mouse events rather than the underlying panel.
Is there some way of having a control pass its mouse events to its parent, without having to dynamically assign OnMouseXxx handlers for every control added to the panel?
If a child is a TGraphicControl descendant, it does not have its own HWND in the OS, so for all practical purposes it doesn't exist to the OS. Thus, a click on the child will be sent to that child's immediate parent for processing. The VCL will then look for a child at the click coordinates, and if found then send the click to that child, otherwise handle it as a click on the parent. The methods involved in this lookup and delegation are not virtual or dynamic, so you can't override them, and they are invoked before the virtual Mouse(Down|Move|Up) methods are called on the determined target control.
If a child is a TWinControl descendant, it has its own HWND in the OS, thus a click on the child will be sent directly to that child for processing. The parent will not know the child has been clicked on, unless the parent has assigned appropriate event handlers to the child, or the child otherwise notifies the parent.
So, your TCustomPanel (which is a TWinControl descendant) can handle clicks transparently but only for non-windowed children, if it handles the WM_(L|M|R)BUTTON(DOWN|UP)/WM_MOUSEMOVE messages directly before passing them along to any inherited handler. That is not the case for windowed children. To intercept those messages, you would need to use a thread-specific mouse/message hook via the Win32 SetWindowsHookEx() function, looking for HWNDs that have your Panel's HWND as their (grand)parent.

How to close an FMX TFrame shown in a TPopup from event handlers inside the TFrame

I use a frame that is shown by calling TPopup.Popup(true); method. If the frame contains a button with the ModalResult property set (to, for example, mrOK) it closes automatically when clicked by the user. But I need to close the frame in an OnClick event of a TListBox in it.
Frame does not have Close method.
I would like to avoid using message posting to the parent form as it might cause future problems when the application is ported to Android as well as I would prefer not to declare the OnClick event handler for the Frame.ListBox in the parent Form because the frame might be shown by several different forms and it will worsen the quality of parent Form code making it heavy and difficult to read.
I would highly appreciate suggestions how to do this.
I found the following way out. I call
(GetParentComponent as TPopup).IsOpen:=false;
When you inherit from TPopUp like explained here (How to make my own dialog component from Firemonkey TPopUp?) then you can call ClosePopup when an event is trigerred in your frame.

How determine that a dragging operation has ended in FireMonkey?

I want to find out if a dragged control has been released outside any target.
One would think that OnDragEnd should be used, but that event doesn't work (they forgot to call the DragEnd procedure in the FMX.Types unit).
As an alternative, I tried OnMouseUp for the dragged control. Doesn't work. Doesn't get triggered when DragMode=dmAutomatic.
As a last resort, I tried to override the MouseUp procedure of the form itself (since all mouse events go through the form before being handed down to the respective control). Surprise: the MouseUp procedure does not get called when the control's DragMode=dmAutomatic.
Amazing how something this very simple is this extremely hard to achieve, but I'm hoping someone might have found a working solution.
It is possible to override the form's DragLeave method. This method is called for all objects that get dropped outside of a target area.

Creating custom Hint window

I'm trying to find a way to use my 2nd form as a hint window for a component (for example a TLabel) in my 1st form.
At the moment, I'm exploring the use of THintWindow and HintWindowClass, but it is not possible to directly assign a TForm to HintWindowClass. Some examples I've seen so far use a TBitmap which is then drawn on the THintWindow.Canvas, which is not bad, but I'd still like to use some kind of integrated automatic mechanism.
Another solution that crossed my mind is to manually implement this functionality using OnMouseEnter, OnMouseMove and OnMouseLeave events of the said Tlabel.
If there actually is a way to "assign" a TForm to HintWindowClass, I'd like to ask if anyone can provide a code snippet illustrating this. Thanks.
THintWindow is a descendant of TCustomControl. TForm is not a descendant of either of those classes, so you cannot assign any TForm class to HintWindowClass. Hint windows need to descend from THintWindow. Anything you can put on a form you can also put on a THintWindow. You'll just have to instantiate it manually and assign its Parent property to make it appear.
The closest you can probably get to "visually" designing a hint window is to design a frame. Make your THintWindow descendant create an instance of the frame, and then override ActivateHint (and ActivateHintData, if you need the data) to forward the hint text and desired size to your frame.

EventHandler inside a TFrame?

I have a TForm (TVehicleEditForm) with 3 identical TFrames (TVehicleUnitFrame) inside.
The idea was that every instance of the frame handle own events by a eventhandler inside the frame. The problem is that the eventhandler is not called.
I have tried to assign the eventhandler by code inside the frame by overriding the Create method but the handler is not called in that case either.
But if I assign the eventhandler outside the frame from the form it works fine.
Like this:
fraVehicleUnitFrame1.cmdNewOwner.OnClick := fraVehicleUnitFrame1.cmdNewOwnerClick;
fraVehicleUnitFrame2.cmdNewOwner.OnClick := fraVehicleUnitFrame2.cmdNewOwnerClick;
fraVehicleUnitFrame3.cmdNewOwner.OnClick := fraVehicleUnitFrame3.cmdNewOwnerClick;
And this is only for one button!
As I have many components inside the frame this would result in many assignments...
Quite ugly code when this should be done directly in the object inspector.
I am using D2007. Any idea of the cause ?
Regards Roland
The reason it works when you do it from code, is that you replace the pointer to the event handler, regardless of what was there before.
At design-time, I have two possible places to set the handler. Suppose I have Frame1 in Unit1 and I place it on MyForm in MyUnit, I'll have the opportunity to set the event handler in both places.
In your case you want to set the event handler in the frame itself (Unit1 in my example), as the code it refers to is on the frame itself. If you do that, it should work. If you set the event handler on the place it is used (MyUnit), then the event handler will be assigned there.
Delphi is clever enough to still call the event handler from your frame as long as that event handler was assigned before you added the one in the form. If you first added it on the form and then added the handler on the frame, the last one on the frame is not called.
To make matters worse, if you remove the handler in the form it still won't call the one on the frame.
What you need to do is this:
Right-Click your form and select View as Text. Scroll down to the frame. It should be something like:
inline FrameX: fraVehicleUnitFrame1
Beneath that, look for
inherited cmdNewOwner: TButton
There you should see something like:
OnClick = FormOldClickHandler
or perhaps
OnClick = nil
Delete this OnClick assignment, view as form again and save. All should be well. If you now select the button (or whatever cmdNewOwner is) on the form, the object inspector should show nothing next to that event.
This is old issue but I had the similar problem in Rio 10.3.
On the main form you can click "Revert to inherited" option.
This option is on 'Events' tab in 'Object Inscpector'. Click right mouse on 'OnItemClick' and click option 'Revert to inherited'.

Resources