Search code examples
c#wpfvisual-studio-2019visual-studio-extensionsvsix

Visual Studio Extension Mouse Button events not working


I have a Visual Studio extension with a (async) tool window. The tool window has an UserControl, and the control has an Image which I use WriteableBitmap as a source. Basically, I am trying to use a graphics API (Vulkan) on a tool window.

Basically, my extension could look something similar to Visual Studio's 3d model editor.

My problem here is that none of mouse event handlers like MouseLeftButtonDown or PreprocessMouseLeftButtonDown gets called when I click on the tool window. I have tried adding a mouse event handler to the UserControl, Image, and even HwndHost, but I had no success.

I am aware of IMouseProcessor or other variants, but it seems to me, that they are used for text editors, not just a tool window pane or a user control.

HwndHost's WndProc method does not seem to receive any mouse events either. Simply "cheesing" it by replacing parent Hwnd's WndProc seems to work, but I did not want to create an extension that takes control of the entire Visual Studio.

== EDIT ==

public partial class MyControl : UserControl {
    public MyControl () {
        InitializeComponents ();
        MouseLeftButtonDown += MyLeftButtonDown;
    }
    ...
    private void MyLeftButtonDown (object sender, MouseButtonEventArgs e) {
        // This code never gets executed.
    }
}

Nor these.

<UserControl ...
             IsHitTestVisible="True"
             MouseLeftButtonDown="MyLeftButtonDown ">
    <Image x:Name="View" ... />
    <!--- nor this --->
    <!--- <Image x:Name="View" MouseLeftButtonDown="MyLeftButtonDown " /> --->
</UserControl>

This also doesn't work.

public partial class MyControl : UserControl {
    public MyControl () {
        InitializeComponents ();
        // View is an object of Image class.
        View.MouseLeftButtonDown += MyLeftButtonDown;
    }
    ...
    private void MyLeftButtonDown (object sender, MouseButtonEventArgs e) {
        // This code never gets executed.
    }
}
// C++/CLI
IntPtr MyHost::WndProc (IntPtr hWndPtr, int uMsg, IntPtr wParamPtr, IntPtr lParamPtr, bool % handled)
{
    auto hWnd = (HWND) hWndPtr.ToPointer ();
    auto wParam = (WPARAM) wParamPtr.ToPointer ();
    auto lParam = (LPARAM) lParamPtr.ToPointer ();

    switch (uMsg)
    {
        case WM_LBUTTONDOWN: // This doesn't get called.
            break;
        ...
        default:
            handled = false;
            return IntPtr (DefWindowProc (hWnd, uMsg, wParam, lParam));
    }
    ...
    handled = true;
    return IntPtr (0);
}

Solution

  • I have not figured out how to process mouse inputs with Image or UserControl yet, but I figured it out for HwndHost.

    Apparently, if your HwndHost is not replying to WM_NCHITTEST, then your HwndHost will never received any mouse events like WM_LBUTTONDOWN.

    // C++/CLI
    IntPtr MyHost::WndProc (IntPtr hWndPtr, int uMsg, IntPtr wParamPtr, IntPtr lParamPtr, bool % handled)
    {
        auto hWnd = (HWND) hWndPtr.ToPointer ();
        auto wParam = (WPARAM) wParamPtr.ToPointer ();
        auto lParam = (LPARAM) lParamPtr.ToPointer ();
    
        switch (uMsg)
        {
            case WM_NCHITTEST: // This message.
                SetFocus (hWnd);
                handled = true;
                return IntPtr (HTCLIENT);
            case WM_LBUTTONDOWN: // This works now.
                break;
            ...
            default:
                handled = false;
                return IntPtr (DefWindowProc (hWnd, uMsg, wParam, lParam));
        }
        ...
        handled = true;
        return IntPtr (0);
    }
    

    And then, you can put some delegates or events and call them for WndProc messages.