2005. november 10., csütörtök

Show the system menu of a window at the current mouse cursor position


Problem/Question/Abstract:

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.

r := integer(TrackPopupMenuEx(GetSystemMenu(handle, false), TPM_LEFTALIGN or
  TPM_RETURNCMD or TPM_RIGHTBUTTON or TPM_HORIZONTAL or
  TPM_VERTICAL, x, y, handle, nil));
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:

procedure TForm1.SpeedButton1Click(Sender: TObject);
var
  h: HMENU;
begin
  h := GetSystemMenu(handle, false);
  TrackPopupMenu(h, TPM_LEFTALIGN or TPM_LEFTBUTTON, speedbutton1.Left +
    clientorigin.X, speedbutton1.Top + speedbutton1.Height + clientorigin.y, 0,
      handle, nil);
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.

{ ... }
private
{ Private declarations }

procedure WMCommand(var msg: TWMCommand); message WM_COMMAND;
{ ... }

procedure TForm1.WMCommand(var msg: TWMCommand);
begin
  if msg.NotifyCode = 0 then {message comes from a menu}
    if msg.ItemID >= SC_SIZE then
    begin {looks like system menu item}
      PostMessage(handle, WM_SYSCOMMAND, TMessage(msg).WParam,
        TMessage(msg).LParam);
      Exit;
    end;
  inherited;
end;


Solve 3:

There is an undocumented Windows message (Message ID:$313) that can do it:

procedure TForm1.Button1Click(Sender: TObject);
const
  WM_POPUPSYSTEMMENU = $313;
begin
  SendMessage(Handle, WM_POPUPSYSTEMMENU, 0,
    MakeLong(Mouse.CursorPos.X, Mouse.CursorPos.Y));
end;

Nincsenek megjegyzések:

Megjegyzés küldése