I have user-friendliness in mind, so I'd like to intercept the position of each form I'm opening, to store and retrieve each time the same form is opened again.
Because I plan to port this for MAC, I decided to use FireMonkey.
Unfortunately, there is no event which is fired when a form is moved on the screen. I tried to intercept the message which should be fired when moving, but because nothing is happening, I think that Firemonkey forms do not work as VCL forms.
I'm using this declaration:
protected
procedure WndMethod(var Msg: TMessage); virtual;
and this method implementation
procedure TMYForm.WndMethod(var Msg : TMessage);
var
Handled: Boolean;
begin
// Assume we handle message
Handled := True;
case Msg.Msg of
WM_MOVE : begin
Edit_X.Text :='x - ' + MYForm.Left.ToString;
Edit_Y.Text :='y - ' + MYForm.Top.ToString;
end;
else
Handled := False;
end;
if Handled then
// We handled message - record in message result
Msg.Result := 0
else
// We didn't handle message
// pass to DefWindowProc and record result
Msg.Result := DefWindowProc(FHWnd, Msg.Msg,
Msg.WParam, Msg.LParam);
end;
I try to move the form on the screen, but no event is intercepted. I placed a breakpoint in the beginning of the method implementation, but nothing happens until I close the form. In this case, a message is intercepted.
What am I doing wrong?
First, you are not overriding any pre-existing virtual method, you are creating your own virtual method that is not hooked up to anything. That is why it never gets called.
Second, unlike VCL, FireMonkey does not dispatch window messages to a virtual WndProc
method that you can override, or to any message
handlers that you may define on your form's class. A handful of select window messages are processed internally only (in a private WndProc()
function inside the FMX.Platform.Win
unit), they are not exposed publicly. Any unprocessed messages (including WM_MOVE
) are discarded.
So, on Windows, the only way to intercept window messages in FireMonkey is to call the Win32 SetWindowSubclass()
or SetWindowLongPtr(GWLP_WNDPROC)
API to hook the form's underlying Win32 HWND
directly (see Subclassing Controls on MSDN for more details). You can override the form's virtual CreateHandle()
method to call the API. You can get the HWND
by calling the RTL's Platform.Win.FormToHWND()
function.
See How detect the mouse back and forward buttons in a Delphi FMX Windows form? for an example of such subclassing.
Needless to say, using the Win32 API will not port to MacOS, however. So, you will have to use whatever platform-specific API that MacOS exposes to hook windows. I don't know what such an API looks like. And there is no function like FormToHWND()
in the FMX.Platform.Mac
unit, but there is a WindowHandleToPlatform()
function that you can pass the form's Handle
to. On MacOS, WindowHandleToPlatform()
returns a TMacWindowHandle
object containing Wnd: NSWindow
, View: NSView
, and Handle: TOCLocal
properties which you can use with native MacOS APIs.
That being said, there is a better alternative - you do not need to hook the underlying platform window directly at all, you can use FireMonkey's Save State feature instead. In the form's OnSaveState
event, save the form's current Left
/Top
values to the form's SaveState
property as needed, and then in the form's OnCreate
event, you can restore the values from the SaveState
property, if they were previously saved.