Delphi control MouseDown/Move/Up passed to underlying panel - delphi

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.

Related

Is there a way to implement a bottom-up event notification in Firemonkey?

I have a container control (TLayout or TPanel or TScrollbox, etc.) which can have several other nested controls inside. Is there a way in Firemonkey to know when the container becomes "active"? I mean for example:
when any of its children get focus or
user clicks any of its non-focus controls
I am asking for a kind of event-bubble. OnClick would be enough so when user clicks any nested control, the parent container is notified. Control's HitTest property would be a solution but if I set that property to False (to let the parent manages the onlick) on a TEdit, it becomes useless obviously (I can't type on it)
Edit:
A simple layout example:
Drop a TScrollBox in a form
Drop a TPanel in form and put it as child of the TScrollBox
Drop a TEdit in form an put it as child of TPanel
Ok, I can simply do a sort of Parent.Parent.Click after receive the OnClick on TEdit but imagine now several TScrollBoxes, with different level of nesting using TPanels with a bunch of different controls inside. It doesn't make sense neither adding the onclick code for each control in the tree, nor change the hierarchy of Parent.Parent.Click every time I redesign the layout adding an intermediate parent control.
Is there any way to do this in Firemonkey (either for Windows or MacOS)?
PS: I'm using Delphi 10.2.3 (Tokyo)

What is the difference between Owner and Parent of a control?

I am bit curious about the two properties of a Delphi VCL control.
Each component has 2 properties as Owner and Parent at runtime. Can anyone help me to understand the difference between both? And how are they used by windows for displaying control or form?
Owner
Owner is a property introduced in TComponent, and Owner itself has type TComponent. The Owner is used primarily to manage the lifetime of designed components. That is, components that you place on the form designer (or indeed other design surfaces) and whose lifetime is managed entirely by the framework. The documentation says:
Indicates the component that is responsible for streaming and freeing this component.
When a form is created, the streaming framework parses the .dfm file and instantiates the components that are listed within. These components are typically created with Owner specified to be the form.
At the other end of a component's life is destruction. When a component is destroyed, it also destroys all of the components which it owns. Consider, for the sake of a concrete example, a TButton that sits on a TForm, placed there at design time. The streaming framework creates the button setting its Owner to be the form. The form, as a descendent of TComponent maintains a list of all the components that it owns. When the form is destroyed it walks over that list of owned components and destroys them. The button is destroyed in this manner.
There are many nuances to this:
Components don't need to be owned by the form. For instance, components created at run time are owned by whatever component you pass to the constructor.
Components need not have an owner, you can pass nil to the constructor. In this case, the programmer remains responsible for destroying the component.
Ownership can be changed during the lifetime of a component.
Because the streaming framework instantiates components, the constructor of TComponent is declared virtual:
constructor Create(AOwner: TComponent); virtual;
If you derived from a TComponent descendent, and you wish for that derived component to be placed on a design surface, then you must respect this virtual constructor. If you define a constructor in your TComponent descendent then it must override this virtual constructor.
It is worth pointing out that Win32 has a completely different concept of a window's owner, that should not be confused with the VCL concept of the same name. The Windows documentation says:
Owned Windows
An overlapped or pop-up window can be owned by another overlapped or
pop-up window. Being owned places several constraints on a window.
- An owned window is always above its owner in the z-order.
- The system automatically destroys an owned window when its owner is destroyed.
- An owned window is hidden when its owner is minimized.
Only an overlapped or pop-up window can be an owner window; a child
window cannot be an owner window.
In VCL terms, this concept is exposed by the PopupParent property. That property was introduced after Delphi 7 so will not be available to you. In Delphi 7, the framework sets the window owner, and gives no easy mechanism to override the framework's choice. If you do need to influence window ownership then you must override CreateParams and set Params.WndParent. Unfortunately, there are a number of problems with the VCL handling of ownership in Delphi 7 and it is sometimes necessary to grub around in these somewhat gory details.
To show how easy it is to get confused, the VCL documentation says:
WndParent: The window handle of the parent window. This is the same as the Handle property of the parent control.
This is simply wrong. For a top-level window, this is the owner rather than the parent.
Parent
Parent is a property defined in TControl and has type TWinControl. This property is used, broadly, to expose the Win32 concept of parent and child controls. The Windows documentation says:
A window can have a parent window. A window that has a parent is called a child window. The parent window provides the coordinate system used for positioning a child window. Having a parent window affects aspects of a window's appearance; for example, a child window is clipped so that no part of the child window can appear outside the borders of its parent window. A window that has no parent, or whose parent is the desktop window, is called a top-level window.
Essentially, the VCL Parent property maps directly onto the Win32 parent concept.
Note however, that Parent is defined in TControl. Now, TControl is not windowed, so a TControl is not a child control in the Win32 sense, because a Win32 window's children are themselves windows. So, a TControl with a defined Parent is a child in the VCL sense, known as a non-windowed child control. These non-windowed controls paint themselves as part of their parent's paint handlers. The canonical example of such a control is TLabel.
Note that when a windowed control, that is a TWinControl descendent, is destroyed, it also destroys all of its children.
A TWinControl descendent can enumerate its children using the ControlCount and Controls[] properties. These enumerate both windowed and non-windowed children.

Delphi 7, How can a child react to the parent form Moving?

I'm writing a TFrame descendant that can host any sort of controls at runtime.
Among its features, under specific conditions, it should show a sort of visual dimmed "mask".
I achieved this effect overlaying a separate TCustomForm descendant class with AlphaBlend,
and for this to work the form must have no parent.
Thus, I need some extra code to keep this window anchored to the Client area of the frame, whether it's been resized or moved.
Not a big deal for resizing: I can override TMyFrame's Resize method.
But what about MOVING?
Let's say the frame is client-aligned to the main form: its Left and Top values don't change if I move the main window, so no WM_MOVE message is sent to the frame.
And I need somethig to be incapsulated INSIDE the TMyFrame unit, in order to keep it reusable.
Is there any other message I can handle in such a situation?
Thank you
Hook the parent form's WindowProc property, or subclass the parent form's window using SetWindowSubclass(), to intercept WM_WINDOWPOSCHANING and WM_WINDOWPOSCHANGED messages.

Is possible for a menu item to receive an OnClick event even when it's not enabled?

I'm trying to enable an Administrator to enable/disable menu items in the main menu of my Application by Ctrl+Clicking them. To do that I've injected the TMenuItem class in my main form with a custom version and overridden the Click virtual method, like so:
uses
Forms, Menus;
type
TMenuItem = class(Menus.TMenuItem)
public
ControlActivationState: Boolean;
procedure Click; override;
end;
TMyMainForm = class(TForm)
...
procedure TMenuItem.Click;
begin
if ControlActivationState and IsKeyPressed(VK_CONTROL) then
Self.Enabled := not Self.Enabled
else
inherited;
end;
It works, but only for the top level menu.
Why the top level menu items receives OnClick events even when they are disabled and the other menu items don't?
Is there a way to make the child menu items receive those events too?
The top level OnClick event is triggered by receipt of a WM_INITMENUPOPUP message. That message is sent even when the top level item is disabled. I'm not sure why it is sent in that scenario, but it is. And the same is true for a sub-item that has children.
However, for a sub-item without children, the OnClick is triggered by a WM_COMMAND message. But the system never even sends the message if the menu item is disabled.
What you are attempting to do cannot be readily done. The only way I can see you doing it is to handle the raw mouse and keyboard events. Personally, I would not contemplate doing so.
TMenuItem is a TComponent, i.e. it's not a windowed control and it doesn't have classical events. Instead, click events which happen on a real windowed control are delegated to a TMenuItem instance. I don't know which window control is the real host for events but even if I did I think it would be hard to determine which TMenuItem corresponds to the actual click point.
My advice is to make a dedicated window for menu editing with a tree control which generically populates its items at runtime based on the actual menu layout, and then provide enable/disable for the tree nodes which reflect on the corresponding menu items. You can then save/load menuitem list, etc. This should be much cleaner and easier then diving into the murky depths of VCL and figuring out (and overriding) how events are propagated from 'real' controls to design-time representations called TComponents...
In fact, you are trying to do it the hard way...
The easy solution to your problem would be to override the OnDrawItem() method of your TMenuItem to display it like it was disabled, and handle the OnClick event alternatively.
(Do not forget to set the .OwnerDraw property of the menu to make this solution work.)
Edit:
According to the Delphi help using the OnAdvancedDrawItem event makes it more easy because it provides intormation about the menuitem itself.

Is there anyway to stop TWebBrowser from clearing?

Is there anyway to stop TWebBrowser from clearing when I set Self.Parent := nil;?
When it gets to that line (which is necessary for a maximizing function), all the TWebBrowsers in my form clears. Why does it do that and what can I do to avoid this?
EDIT:
'Self' is the current form (in this case AnsForm) being shown on the MainForm.
Previous value of Parent is a Tab in MainForm.
I tried setting Self.Parent to something else but the same thing happens.
Reassigning the TWinControl.Parent property causes that control (in this case, your TForm object) to destroy its HWND (as a child HWND cannot exist without a parent HWND), and a new HWND is not created until the next time that control's Handle property is accessed (if no Parent is available by then, an exception is raised). When a control destroys its HWND, all of its child controls, and their child controls, and so on, destroy their own HWNDs as well. Without an HWND, there is nothing for a control to display, and any content stored in those HWNDs is lost. That is why your TWebBrowser objects get cleared.
Some components cache their current content in memory when their HWND is destroyed and then restore that content when a new HWND becomes available, but TWebBrowser does not (and cannot) do that. Your only option in this situation is to manually reload the current URL again. Otherwise, re-design your UI so the TWebBrowser objects do not reside on a parent control whose Parent property changes.

Resources