Top / .NET備忘録 / 06.ContextMenu / 08.各コントロールでの使用

各コントロールでは、デザインモードでなければ、ContextMenuStrip プロパティに代入しておきます。

VBTextBox

 public VBTextBox()
 {
     base.ForeColor = DefaultForeColor;
     base.BackColor = DefaultBackColor;
     if (!base.DesignMode) base.ContextMenuStrip = VBContextMenu.GetDefaultInstance();
 }

VBComboBox

 public VBComboBox()
 {
     base.BackColor = SystemColors.Window;
     if (!base.DesignMode) base.ContextMenuStrip = VBContextMenu.GetDefaultInstance();
 }

WM_CONTEXTMENU

Control.cs を見ると、以下のようになっています。

 internal void WmContextMenu(ref Message m, Control sourceControl) {
     ContextMenu contextMenu             = Properties.GetObject(PropContextMenu) as ContextMenu; 
     ContextMenuStrip contextMenuStrip   = (contextMenu != null) ? null /*save ourselves a property fetch*/
                                                                 : Properties.GetObject(PropContextMenuStrip) as ContextMenuStrip; 
 
     if (contextMenu != null || contextMenuStrip != null) {
         int x = NativeMethods.Util.SignedLOWORD(m.LParam); 
         int y = NativeMethods.Util.SignedHIWORD(m.LParam);
         Point client;
         bool keyboardActivated = false;
         // lparam will be exactly -1 when the user invokes the context menu 
         // with the keyboard.
         // 
         if (unchecked((int)(long)m.LParam) == -1) { 
             keyboardActivated = true;
             client = new Point(Width/2, Height/2); 
         }
         else {
             client = PointToClientInternal(new Point(x, y));
         }
 
         // VisualStudio7 # 156, only show the context menu when clicked in the client area
         if (ClientRectangle.Contains( client )) { 
             if (contextMenu != null) {
                 contextMenu.Show(sourceControl, client);
             }
             else if (contextMenuStrip != null) { 
                 contextMenuStrip.ShowInternal(sourceControl, client, keyboardActivated);
             } 
             else { 
               Debug.Fail("contextmenu and contextmenustrip are both null... hmm how did we get here?");
               DefWndProc( ref m ); 
             }
         }
         else {
             DefWndProc( ref m ); 
         }
     } 
     else { 
         DefWndProc(ref m);
     } 
 }

どういう事かというと、クライアント領域内でマウスの右ボタンを押し、マウスポインタを移動して、クライアント領域外で右ボタンを離すと、設定したコンテキストメニューでなく、デフォルトのコンテキストメニューが表示されます。

コメントから察するに、なにか不都合があったものと思われますが、このままだとまずいので、事前に同様の判定を行って、マウスポインタがクライアント領域外ならマウスキャプチャを終了し、コンテキストメニューを表示しないようにします。

API の宣言

 [DllImport("user32")]
 [return: MarshalAs(UnmanagedType.Bool)]
 private static extern bool ReleaseCapture();
 
 private const int WM_CONTEXTMENU = 0x007B;

WndProc

 case WM_CONTEXTMENU:
     if (VBContextMenu.IsMouseInClientArea(this, m.LParam))
         base.WndProc(ref m);
     else
         ReleaseCapture();
     break;

VBContextMenu のスタティックメソッド

 public static bool IsMouseInClientArea(Control target, IntPtr mousePointer)
 {
     // キーボードによる起動(Shift+ F10) は True
     if (unchecked((int)(long)mousePointer) == -1) return true;
 
     // マウスポインタがクライアントエリア内にあるかチェック
     Point client;
     int x = NativeMethods.LOWORD(mousePointer);
     int y = NativeMethods.HIWORD(mousePointer);
     client = target.PointToClient(new Point(x, y));
     return target.ClientRectangle.Contains(client);
 }



トップ   編集 凍結 差分 バックアップ 添付 複製 名前変更 リロード   新規 一覧 単語検索 最終更新   最終更新のRSS
Last-modified: 2013-03-16 (土) 03:47:29 (1615d)