Author: Jonas Bilinkevicius
How can I show the system menu of a window at the position of the mouse cursor and
not at the window's title bar?
Answer:
Solve 1:
The problem is that the system menu sends WM_SYSCOMMAND messages to the window
identified by Handle, and you are probably looking for WM_COMMAND messages.
1 r := integer(TrackPopupMenuEx(GetSystemMenu(handle, false), TPM_LEFTALIGN
2 or
3 TPM_RETURNCMD or TPM_RIGHTBUTTON or TPM_HORIZONTAL or
4 TPM_VERTICAL, x, y, handle, nil));
5 SendMessage(handle, WM_SYSCOMMAND, r, 0);
Solve 2:
Well, you can pop the system menu up where you want using code like the one below:
6 procedure TForm1.SpeedButton1Click(Sender: TObject);
7 var
8 h: HMENU;
9 begin
10 h := GetSystemMenu(handle, false);
11 TrackPopupMenu(h, TPM_LEFTALIGN or TPM_LEFTBUTTON, speedbutton1.Left +
12 clientorigin.X, speedbutton1.Top + speedbutton1.Height + clientorigin.y, 0,
13 handle, nil);
14 end;
The problem is that the menu will not work this way. If you use TrackPopupMenu to
show the menu its items will send WM_COMMAND messages to the form when clicked by
the user. But the form expects WM_SYSCOMMAND messages from the system menu. So you
have to trap the WM_COMMAND messages, figure out which of them come from the menu
(there will be lots of others, from buttons and the like) and translate them into
WM_SYSCOMMAND.
15 { ... }
16 private
17 { Private declarations }
18
19 procedure WMCommand(var msg: TWMCommand); message WM_COMMAND;
20 { ... }
21
22 procedure TForm1.WMCommand(var msg: TWMCommand);
23 begin
24 if msg.NotifyCode = 0 then {message comes from a menu}
25 if msg.ItemID >= SC_SIZE then
26 begin {looks like system menu item}
27 PostMessage(handle, WM_SYSCOMMAND, TMessage(msg).WParam,
28 TMessage(msg).LParam);
29 Exit;
30 end;
31 inherited;
32 end;
Solve 3:
There is an undocumented Windows message (Message ID:$313) that can do it:
33
34 procedure TForm1.Button1Click(Sender: TObject);
35 const
36 WM_POPUPSYSTEMMENU = $313;
37 begin
38 SendMessage(Handle, WM_POPUPSYSTEMMENU, 0,
39 MakeLong(Mouse.CursorPos.X, Mouse.CursorPos.Y));
40 end;
|