Search code examples
.netvb.netwinformscontextmenustrip

Activate ContextMenuStrip when the Form doesn't have focus


  1. I am able to display ContextMenuScript (CMS) successfully outside windows form.
  2. I can Select/Click items using mouse pointer.
  3. However, it doesn't likes keyboard control (arrow up/down, escape) when form is not focused.
  4. If the form is focused and CMS showed, then keyboard can control it but not when not focused :(.
  5. I need help with code which will help to achieve this without form being focused.

Regards

   Public Const CTRL_Key As Integer = &H2
   Public Const Hot_Key As Integer = &H312
   Public Declare Function RegisterHotKey Lib "user32" (ByVal hwnd As IntPtr, ByVal id As Integer, ByVal fsModifiers As Integer, ByVal vk As Integer) As Integer
   Public Declare Function UnregisterHotKey Lib "user32" (ByVal hwnd As IntPtr, ByVal id As Integer) As Integer

   Private Sub Hot_Key_Register() Handles MyBase.Load
    RegisterHotKey(Me.Handle, 100, CTRL_Key, Keys.NumPad1)
    RegisterHotKey(Me.Handle, 200, CTRL_Key, Keys.NumPad2)
    RegisterHotKey(Me.Handle, 300, CTRL_Key, Keys.NumPad3)
   End Sub

   Protected Overrides Sub WndProc(ByRef Window_Message As Message)

    If Window_Message.Msg = Hot_Key Then
        Dim id As IntPtr = Window_Message.WParam
        Select Case (id.ToString)
            Case "100"
                CMS_01.Show(Cursor.Position.X, Cursor.Position.Y)
            Case "200"
                CMS_02.Show(Cursor.Position.X, Cursor.Position.Y)
            Case "300"
                CMS_03.Show(Cursor.Position.X, Cursor.Position.Y)
        End Select
    End If
    MyBase.WndProc(Window_Message)
   End Sub

Solution

  • Option 1 - Using NotifyIcon

    The easiest fix which you can use is using an invisible NotifyIcon component as it handles this case in its internal code.

    Drop an instance of NotifyIcon on your form and then to use it for showing context menu, assign the context menu strip to its ContextMenuStrip property and then call its ShowContextMenu private method using reflection.

    Example

    Private Sub ShowContextMenu(menu As ContextMenuStrip)
        NotifyIcon1.Visible = False
        NotifyIcon1.ContextMenuStrip = menu
        Dim m = NotifyIcon1.GetType().GetMethod("ShowContextMenu",
            Reflection.BindingFlags.NonPublic Or Reflection.BindingFlags.Instance)
        m.Invoke(NotifyIcon1, Nothing)
    End Sub
    Protected Overrides Sub WndProc(ByRef Window_Message As Message)
        If Window_Message.Msg = Hot_Key Then
            Dim id As IntPtr = Window_Message.WParam
            Select Case (id.ToString)
                Case "100"
                    ShowContextMenu(CMS_01)
            End Select
        End If
        MyBase.WndProc(Window_Message)
    End Sub
    

    Option 2 - Using Native Window

    And here is the fix without using NotifyIcon, using NativeWindow. The following piece of code cares about the active window and if the current form is active, it doesn't use the native window, otherwise it creates and uses native window.

    Example

    Private window As NativeWindow
    Private Sub ShowContextMenu(menu As ContextMenuStrip, p As Point)
        If (Form.ActiveForm IsNot Me) Then
            If (window Is Nothing) Then
                window = New NativeWindow()
                window.CreateHandle(New CreateParams())
            End If
            SetForegroundWindow(window.Handle)
        End If
        menu.Show(p)
    End Sub
    

    And show the menu:

    ShowContextMenu(CMS_01, Cursor.Position)
    

    Just keep in mind to release the window handle when closing/disposing the form:

    If (window IsNot Nothing) Then
        window.DestroyHandle()
        window = Nothing
    End If