Delphi Tips 
-----------------------------

キーワード:メニュー

>> Index

06/20 システムメニューに項目を追加/削除したい
09/08 ショートカットキーに'+'を使う。
08/14 ショートカットキーのキー名を独自に設定する
02/11 表示中のポップアップメニューを消す
02/08 プログラムからWindowsのスタートメニューを表示する
02/08 PopupMenu に MainMenu のサブ項目をそのまま表示する
02/08 サブフォームがアクティブな時はメインフォームのアクセラレータキー・ショートカットキーを無効にしたい

最終更新: 7244 日前

0065  D1   D2   D3   D4   D5   D6   D7   3.1   95   98    作成: 1999/02/08 osamu rev 1.4
   B1   B3   B4   B5   B6   B7   NT3   NT4   2K   XP  更新: 2004/06/20 kakken 編集
システムメニューに項目を追加/削除したい

海外製のコンポーネントですがこういうのがあるそうです(ソース付だそうだ)
http://www.torry.ru/vcl/menus/ksysmenu.zip

実際にやってみましょう。まず削除です。
デファウルトでシステムメニューはこのようになっています

+−−−−−−−−−+
|元のサイズに戻す  |
|移動              |
|サイズ変更        |
|最小化            |
|最大化            |
|−−−−−−−−−|
|終了              |
+−−−−−−−−−+

// 上の例から移動と、終了だけ残します。
procedure TForm1.Button1Click(Sender: TObject);
var
    hSysMenu:integer;
begin
    hSysMenu := GetSystemMenu(Handle,False);
    DeleteMenu(hSysMenu, 0, MF_BYPOSITION);
    DeleteMenu(hSysMenu, 1, MF_BYPOSITION);
    DeleteMenu(hSysMenu, 1, MF_BYPOSITION);
    DeleteMenu(hSysMenu, 1, MF_BYPOSITION);
end;

MF_BYPOSITION を指定した時は、第二パラメータで位置を0..で指定します。
削除ごとに位置が変わることに注意して下さい。
MF_BYCOMMAND を指定した時は  SC_SIZE、SC_MOVE、SC_MINIMIZE、SC_MAXIMIZE、SC_CLOSE などでメニューを特定します。

次に追加です。
const
    NewMenu1_Command = 10;  // この値は重複しないように任意
    NewMenu2_Command = 11;
procedure TForm2.Button1Click(Sender: TObject);
var
    hSysMenu:integer;
begin
    hSysMenu := GetSystemMenu(Handle,False);
    // 底に(最後に)追加する
    // セパレータを追加
    AppendMenu(hSysMenu, MF_SEPARATOR,0,nil);
    // 'NewMenu_1'を追加
    AppendMenu(hSysMenu, MF_STRING, NewMenu1_Command, 'NewMenu_1');
    // セパレータ(位置=1)の上の挿入
    InsertMenu(hSysMenu, 1, MF_BYPOSITION or MF_STRING, NewMenu2_Command, 'NewMenu_2');
end;


Const で定義した値が、TMenuItemで、Commandプロパティにあたるものです。
でも、これだけでは、表示されるだけです。追加した項目をクリックしたらイベントハンドラを呼び出さねばなりません。
(ここでは、イベントハンドラではなく、メッセージボックスで代用)それには、WM_COMMANDメッセージを捕まえます。
procedure WMCOMMAND(var msg: TWMSysCommand);message WM_COMMAND;
を、フォームのPrivateに定義して、

procedure TForm1.WMCOMMAND(var msg: TWMSysCommand);
begin
    case Msg.CmdType of
        NewMenu1_Command: ShowMessage('NewMenu_1が押されました');
        NewMenu2_Command: ShowMessage('NewMenu_2が押されました');
    end;
    inherited;
end;

ですね。

たくさん作るときは、TPopupMenuを貼り付け、TMenuItemを作成してこのCaption、Command、OnClickなどのプロパティを利用した方が簡単です。
参照: [Delphi-ML:19712] [Delphi-ML:42000]

0003  D1   D2   D3   D4   D5   D6   D7   3.1   95   98    作成: 1999/02/08 osamu rev 1.3
   B1   B3   B4   B5   B6   B7   NT3   NT4   2K   XP  更新: 1999/09/08 osamu 編集
ショートカットキーに'+'を使う。

Delphi2.0 以上では、『!"#$%&'()=~|^{}`*+_?><』など、101のフルキー側(テンキーを除いた部分)で、Shiftと同時にキーを押さなければ入力できない文字はショートカットに使えない。
テンキーがついてたり、JISキーボードならばワンキーで押せるものもあるのに。

こういったキーを使うには、ShortCut プロパティにキーコードを直接セットすればいい。

たとえば、

    Test2.ShortCut:=$6B;  // <- + キーのキーコード

でも、これだとメニューの右端にキー名が表示されない。 (;_;)
対処方法は [Tips:4]
参照: [Delphi-ML:17954] [Tips:4] <コンポーネント > <Standard>

0004  D1   D2   D3   D4   D5   D6   D7   3.1   95   98    作成: 1999/02/08 osamu rev 1.2
   B1   B3   B4   B5   B6   B7   NT3   NT4   2K   XP  更新: 1999/08/14 osamu 編集
ショートカットキーのキー名を独自に設定する

例えば、テンキーの + キーをショートカットにした場合に、'Num +'とか表示させたい。
TMenuItem.AppendTo()を参照すると、

    TestMenu1.Caption:=TestMenu1.Caption+#9+'Num +'+#0;

とすればよいことが分かる。

キャプションとショートカット名との間にはさんだタブ文字と、自動で追加されるショートカットを隠すために最後にくっつけられた #0 とが味噌。
参照: [Delphi-ML:17989] <コンポーネント > <Standard>

0135  D1   D2   D3   D4   D5   D6   D7   3.1   95   98    作成: 1999/02/11 osamu rev 1.1
   B1   B3   B4   B5   B6   B7   NT3   NT4   2K   XP  更新: 1999/02/11 osamu 編集
表示中のポップアップメニューを消す

>プログラム上でポップアップメニューを表示するにはPopup メソッドを
>使えば出来るのですが、逆に非表示させるにはどうしたらよいのでしょう。
>よろしくお願いします。

キーボードイベントを送って非表示にする、というのはどうでしょう。

procedure TForm1.FormCreate(Sender: TObject);
begin
  Timer1.Interval := 1000;
  Timer1.Enabled := False;
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
  Timer1.Enabled := True;
  PopupMenu1.Popup(Button1.ClientOrigin.x,
    Button1.ClientOrigin.y + Button1.Height);
end;

procedure TForm1.Timer1Timer(Sender: TObject);
begin
  keybd_event(VK_ESCAPE, 0, 0, 0);
  keybd_event(VK_ESCAPE, 0, KEYEVENTF_KEYUP, 0);
  Timer1.Enabled := False;
end;

以上のコードで実行すると、ボタンを押すとポップアップしますが、約1秒後にポップアップが非表示になります。
参照: [Delphi-ML:24269] <Standard> <コンポーネント >

0062  D1   D2   D3   D4   D5   D6   D7   3.1   95   98    作成: 1999/02/08 osamu rev 1.1
   B1   B3   B4   B5   B6   B7   NT3   NT4   2K   XP  更新: 1999/02/08 osamu 編集
プログラムからWindowsのスタートメニューを表示する

以下の方法でメニューを出すことができるようです。

 HWND taskbar=FindWindow("Shell_TrayWnd","");
 if(taskbar) PostMessage(taskbar,WM_USER+260,0,0);
参照: [builder:4947] <Windows>

0066  D1   D2   D3   D4   D5   D6   D7   3.1   95   98    作成: 1999/02/08 osamu rev 1.1
   B1   B3   B4   B5   B6   B7   NT3   NT4   2K   XP  更新: 1999/02/08 osamu 編集
PopupMenu に MainMenu のサブ項目をそのまま表示する

一つのメニューアイテムを、同時に二つのメニューに結び付ける事は出来ないようなので、実行時に、MainMenu と PopupMenu 、それぞれが開く時点で、共有するアイテムをそのメニューに結びつけ直す、という方法をとりました。ある位置から下を全部共有アイテムとする仕様です。

結び付け直すタイミングですが、PopupMenu の方は、OnPopup イベントで良いのですが、MainMenu の方は、(エディットメニュー以下をすべて使う場合に) Edit1 のOnClick だと、位置がずれたりしてうまくないので、WndProc をオーバーライドして、WM_INITMENU メッセージに応答させました。

以下は PASCAL 翻訳版

const COMMON_ITEM_START = 3;   // 共有アイテムの始まる位置

procedure TForm1.PopupMenu1Popup(Sender: TObject);
var Item: TMenuItem;
begin
  while Edit1.Count > COMMON_ITEM_START do begin
    Item:= Edit1.Items[COMMON_ITEM_START];
    Edit1.Remove(Item);
    PopupMenu1.Items.Add(Item);
  end;
end;

procedure TForm1.WndProc(var Msg: TMessage);
var Item: TMenuItem;
begin
  if (Msg.Msg=WM_INITMENU) and (Msg.WParam=Menu.Handle) then begin
    while PopupMenu1.Items.Count > COMMON_ITEM_START do begin
      Item:= PopupMenu1.Items[COMMON_ITEM_START];
      PopupMenu1.Items.Remove(Item);
      Edit1.Add(Item);
    end;
  end;
  inherited WndProc(Msg);
end;

参照: [builder:5012] <コンポーネント > <Standard>

0109  D1   D2   D3   D4   D5   D6   D7   3.1   95   98    作成: 1999/02/08 osamu rev 1.1
   B1   B3   B4   B5   B6   B7   NT3   NT4   2K   XP  更新: 1999/02/08 osamu 編集
サブフォームがアクティブな時はメインフォームのアクセラレータキー・ショートカットキーを無効にしたい

メインフォームのメニューに指定されたアクセラレータキー([File(&F)]とした時の 'F')は、サブフォームがアクティブであっても、そのサブフォームがメインメニューを持たない場合には、メインフォームのメニューを起動してしまいます。
また、メインフォームのメニューに指定されたショートカットキー([Open(&O)... Ctrl+O] とした時の 'Ctrl+O')は、サブフォームがアクティブである場合にも有効です。

これらを回避するには以下のようにします。

function TForm1.Hook(var Message: TMessage): Boolean;
begin
  case Message.Msg of
  CM_APPKEYDOWN:
     Result := True;  // ショートカットを無効にする
  CM_APPSYSCOMMAND:
     Result := True;  // アクセラレータを無効にする
  else
     Result := False;
  end;
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
  Application.HookMainWindow(Hook);
end;
参照: [Delphi-ML:21685] <フォーム>

[新規作成] [最新の情報に更新]

How To
Lounge
KeyWords

Tips
Delphi
Home
Osamu Takeuchi osamu@big.or.jp