喫茶XRAYトップページ

[スレッド全体
]

[8065] ポップアップメニュー画面の上に別のポップアップメニュー返信 削除
2014/10/6 (月) 22:38:21 Mr.XRAY
ポップアップメニューを表示して,そのメニューのウィンドウの上で
マウスの右ボタンクリックで,別のポップアップメニューを表示するサンプルです.

(1) 新規にプロジェクトを作成して,保存します.
(2) Delphi のソースコードの Menu.pas をプロジェクトのフォルダにコピーします.
(3) Menu.pas に,以下の 1 行を追加します.
    TPM_RECURSE を追加すると右クリックで閉じなくなります.

    procedure TPopupMenu.Popup(X, Y: Integer);
      ...
      ...
      AFlags := AFlags or TPM_RECURSE;  //追加
      TrackPopupMenu(FItems.Handle, AFlags, X, Y, 0 { reserved }, PopupList.Window, nil);
    end;
 
(4) TPopupMenu コンポを 2 つフォームに配置して,適当に項目を作成します.
(5) TPanel コンポを 1 つ配置します.
(5) Form1 の PopupMenu プロパティを PopupMenu1 にします.
(6) テスト用のコードを以下のようにします.

面白そうなので,サブのポップアップメニューをいくつでも表示できるようにして
います.
動作確認は,Windows 7 U64(SP1) + Delphi XE(UP1) Pro です.


unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, ExtCtrls, Menus;

type
    TPopupListEx = class(TPopupList)
    protected
      procedure WndProc(var Message: TMessage) ; override;
    end;

  TForm1 = class(TForm)
    Button1: TButton;
    PopupMenu1: TPopupMenu;
    Item_0: TMenuItem;
    Item_1: TMenuItem;
    Item_2: TMenuItem;
    Item_3: TMenuItem;
    N1: TMenuItem;
    Panel1: TPanel;
    PopupMenu2: TPopupMenu;
    Item_20: TMenuItem;
    Item_21: TMenuItem;
    Item_22: TMenuItem;
    Item_23: TMenuItem;
  private
    { Private 宣言 }
  protected
    procedure WMExitMenuLoop(var Message: TWMExitMenuLoop);
      message WM_EXITMENULOOP;
    procedure WMEnterMenuLoop(var Message: TWMEnterMenuLoop);
      message WM_ENTERMENULOOP;
    procedure WMMenuSelect(var Message: TWMMenuSelect);
      message WM_MENUSELECT;
  public
    { Public 宣言 }
    procedure WMApp100(var Message: TMessage); message WM_APP+100;
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

var
  hMouseHook : HHOOK;

//-----------------------------------------------------------------------------
//  マウスフックのコールバック関数
//  Form1にメッセージを送る
//  ポップアップメニュー上のマウスカーソル位置は,フックでないと検出不可
//-----------------------------------------------------------------------------
function MouseHookProc(nCode: Integer; wPar: WPARAM; lPar:LPARAM):
  Integer; stdcall;
var
  MousePos : TPoint;
begin
  if nCode < 0 then begin
    Result := CallNextHookEx(hMouseHook, nCode, wPar, lPar);
  end else begin
    if nCode = HC_ACTION then begin
      MousePos := PMouseHookStruct(lPar)^.pt;
      PostMessage(Form1.Handle, WM_APP+100, wPar, MakeLParam(MousePos.X, MousePos.Y));
      //PopupMenuのOnClickイベントを使用しない場合はResult := -1にしてExit;
      //このコードにすると,TMenuItem.OnClickは検出できなくなる
      Result := -1;
      exit;
    end;
    Result := CallNextHookEx(hMouseHook, nCode, wPar, lPar);
  end;
end;

//-----------------------------------------------------------------------------
//  PopupMenuが表示された時のメッセージ処理
//  マウスフックを開始する
//-----------------------------------------------------------------------------
procedure TForm1.WMEnterMenuLoop(var Message: TWMEnterMenuLoop);
begin
  if hMouseHook = 0 then begin
    hMouseHook := SetWindowsHookEx(WH_MOUSE, @MouseHookProc, 0, GetCurrentThreadID);
  end;
end;

//-----------------------------------------------------------------------------
//  PopupMenuが閉じた時のメッセージ処理
//  マウスフックを終了して,PopupMeneを全て閉じる
//-----------------------------------------------------------------------------
procedure TForm1.WMExitMenuLoop(var Message: TWMExitMenuLoop);
var
  MousePos : TPoint;
begin
  GetCursorPos(MousePos);

  //1つでも閉じれば全て閉じることにする
  SendMessage(Handle, WM_APP+100, WM_LBUTTONUP, MakeLParam(MousePos.X, MousePos.Y));
  if hMouseHook <> 0 then begin
    UnhookWindowsHookEx(hMouseHook);
    hMouseHook := 0;
  end;
end;

//-----------------------------------------------------------------------------
//  PopupMenuで項目を選択された時のメッセージ処理
//  選択されている項目名を表示
//  実際に選択したかで項目を取得するには,WM_LBUTTONUPがいいかも知れない
//  ただし,どの項目を選択したかは,このWM_MENUSELECTまたは
//  TPopupList.WndProc内でないと取得は困難だろう
//-----------------------------------------------------------------------------
procedure TForm1.WMMenuSelect(var Message: TWMMenuSelect);
var
  i        : Integer;
  MenuItem : TMenuItem;
begin
  if (Message.MenuFlag = $FFFF) and (Message.Menu = 0) then exit;

  Panel1.Caption := '';
  for i := 0 to ComponentCount - 1 do begin
    if Components[i].ClassName = 'TPopupMenu' then begin
      if TPopupMenu(Components[i]).Handle = Message.Menu then begin
        MenuItem := TPopupMenu(Components[i]).FindItem(Message.IDItem, fkCommand);
        Panel1.Caption := MenuItem.Name;
        Break;
      end;
    end;
  end;
end;

//-----------------------------------------------------------------------------
//  ローカルマウスフックのメッセージを受信
//-----------------------------------------------------------------------------
procedure TForm1.WMApp100(var Message: TMessage);
var
  X         : Integer;
  Y         : Integer;
  hPopupWnd : HWND;
begin
  X := LOWORD(Message.LParam);
  Y := HIWORD(Message.LParam);

  //左ボタンUP(項目を選択した時)はTPopupMenuを全て閉じることにする
  if (Message.WParam = WM_LBUTTONUP) then begin
    while True do begin
      hPopupWnd := FindWindow('#32768', nil);
      if hPopupWnd = 0 then break;

      SendMessage(hPopupWnd, WM_CLOSE, 0, 0);
      Application.ProcessMessages;
      Sleep(1);
    end;
  end else
  //右ボタンUPでは新しくTPopupMenuを表示する
  if (Message.WParam = WM_RBUTTONUP) then begin
    PopupMenu2.Popup(X, Y);
  end;
end;


//-----------------------------------------------------------------------------
//  PopupMenu内部のPopupListのメッセージ処理
//  これらのメッセージを受信したら,アプリのアクティブなフォームにメッセージを送る
//-----------------------------------------------------------------------------

{ TPopupListEx }

procedure TPopupListEx.WndProc(var Message: TMessage);
begin
  if Screen.Activeform <> nil then begin
    if Message.Msg = WM_ENTERMENULOOP then begin
      Screen.ActiveForm.Perform(WM_ENTERMENULOOP, Message.WParam, Message.LParam);
    end else
    if Message.Msg = WM_EXITMENULOOP then begin
      Screen.ActiveForm.Perform(WM_EXITMENULOOP, Message.WParam, Message.LParam);
    end;
    if Message.Msg = WM_MENUSELECT then begin
      Screen.ActiveForm.Perform(WM_MENUSELECT, Message.WParam, Message.LParam);
    end;
  end;
  inherited;
end;

initialization
  Popuplist.Free;
  PopupList:= TPopupListEx.Create;

end.


下図は,設計時と実行した画面です.
Panel1 に,(左ボタンDOWNまたは矢印キー等で)選択の項目名を表示します.


[8067] Re:ポップアップメニュー画面の上に別のポップアップメニュー返信 削除
2014/10/6 (月) 23:12:35 Mr.XRAY
マウスのフックのコールバック関数に以下のコードがあります.

>       //このコードにすると,TMenuItem.OnClickは検出できなくなる
>       Result := -1;
>       exit;


これは,次のウィンドウにマウスフックのメッセージを送らないためです.
その結果,TMenuItem.OnClick はマウスのメッセージを取得できなくなります.
代りに,マウスフックのメッセージで受け取るようにしています.

このようなコードをグロバールフックでやってしまうと...
他のウィンドウがマウスメッセージを受け取れなくなってしまうことがあります.
ご注意くださいませ.


[▼次のスレッド]
INCM/CMT
Cyclamen v3.82