Adaptation

I want my add-in to be able to respond to a mouse wheel event while a document is active in the Visual Studio text editor. My immediate goal is to bind Ctrl+WheelUp to PgUp and Ctrl+WheelDn to PgDn, but it would be nice to have this adjustable. I just don't know how to respond to a mouse wheel event.

Thanks,
Sam


Answer this question

Adaptation

  • Stijn V

    I'm actually using my implementation of this in an open-source plugin I made for VS 2005. I haven't had any problems with it at all. Here's a link to the plugin's homepage:

    http://djss.dynalias.net/trac/SamTools

     

    Here's a link to the classes that provide mouse support:

    http://djss.dynalias.net/trac/SamTools/browser/trunk/SamTools/MouseActions.cs

    http://djss.dynalias.net/trac/SamTools/browser/trunk/SamTools/MouseHook.cs

     

     

    Keep the first 17 lines of those files in mind, I don't want you to run into any problems later. :)


  • Dagaen

    When the .NET framework fails you, there lies the Platform SDK waiting to save you.

    Bare bones class to provide a MouseWheel event in the IDE:



    using System;
    using System.Collections.Generic;
    using System.Text;
    using System.Windows.Forms;
    using System.Runtime.InteropServices;

    namespace SomeNamespace
    {
        public delegate IntPtr MouseProc( int code, UIntPtr wParam, ref MOUSEHOOKSTRUCTEX lParam );

        public struct tagPoint
        {
            public int X;
            public int Y;
        }
        public struct MOUSEHOOKSTRUCT
        {
            public tagPoint pt;
            public IntPtr WindowHandle;
            public uint wHitTestCode;
            public UIntPtr dwExtraInfo;
        }
        public struct MOUSEHOOKSTRUCTEX
        {
            public MOUSEHOOKSTRUCT mstruct;
            public uint mouseData;
        }

        class MouseHook
        {
            public event MouseEventHandler MouseWheel;
            private void OnMouseWheel( MouseEventArgs e )
            {
                MouseEventHandler t = MouseWheel;
                if (t != null)
                    t( this, e );
            }

            private IntPtr mouseHandle;
            private bool enterHook;
            private MouseProc callback;

            private const int WH_MOUSE = 7;
            private const int WM_MOUSEMOVE = 0x200;
            private const int WHEEL_DELTA = 120;

            #region Imported Windows system calls
            [DllImport( "user32.dll" )]
            private extern static int UnhookWindowsHookEx( IntPtr hHook );
            [DllImport( "user32.dll" )]
            private extern static IntPtr SetWindowsHookEx( int idHook, MouseProc lpfn, IntPtr hmod, uint dwThreadId );
            [DllImport( "user32.dll" )]
            private extern static IntPtr CallNextHookEx( IntPtr hHook, int nCode, UIntPtr wParam, ref MOUSEHOOKSTRUCTEX lParam );
            [DllImport( "kernel32.dll" )]
            private extern static uint GetCurrentThreadId( );
            #endregion

            public MouseHook( )
            {
                mouseHandle = IntPtr.Zero;
                enterHook = true;
            }

            ~MouseHook( )
            {
                if (mouseHandle != IntPtr.Zero)
                {
                    UnHook( );
                }
            }

            public bool Hooked
            {
                get
                {
                    return mouseHandle != IntPtr.Zero;
                }
            }

            ushort HIWORD( uint val )
            {
                return (ushort)(val >> 16);
            }

            public void Hook( )
            {
                if (mouseHandle != IntPtr.Zero)
                    return;
                callback = MouseCallback;
                uint threadId = GetCurrentThreadId( );
                mouseHandle = SetWindowsHookEx( WH_MOUSE, callback, IntPtr.Zero, threadId );
            }

            public void UnHook( )
            {
                if (mouseHandle == IntPtr.Zero)
                    return;
                UnhookWindowsHookEx( mouseHandle );
                mouseHandle = IntPtr.Zero;
            }

            private IntPtr MouseCallback( int code, UIntPtr wParam, ref MOUSEHOOKSTRUCTEX lParam )
            {
                try
                {
                    if (enterHook)
                    {
                        enterHook = false;
                        if (code >= 0)
                        {
                            int x = lParam.mstruct.pt.X;
                            int y = lParam.mstruct.pt.Y;

                            switch (wParam.ToUInt32())
                            {
                            case WM_MOUSEWHEEL:
                                OnMouseWheel( new MouseEventArgs( MouseButtons.None, 0, x, y, ((short)HIWORD( lParam.mouseData )) / WHEEL_DELTA ) );
                                break;
                            /* feel free to add other events... i did... */
                            default:
                                /* don't do anything special */
                                break;
                            }
                        }
                    }
                    else
                    {
                        enterHook = true;
                    }
                }
                catch
                {
                    /* can't let an exception get through to the kernel */
                }
                return CallNextHookEx( mouseHandle, code, wParam, ref lParam );
            }
        }
    }

    // For some reason if you start a comment with "//" on this forum it turns
    the whole rest of the code block green

     

    And here is pretty much how to use it:


    MouseHook mouse_enhancer;

    ...

    mouse_enhancer = new MouseHook( );
    mouse_enhancer.MouseWheel += new MouseEventHandler( mouse_enhancer_MouseWheel );
    mouse_enhancer.Hook();

    ...

    void mouse_enhancer_MouseWheel( object sender, System.Windows.Forms.MouseEventArgs e )
    {
        try
        {
            if (Keyboard.GetKeyState( System.Windows.Forms.Keys.ControlKey ).IsDown &&
                applicationObject.ActiveWindow.Type == vsWindowType.vsWindowTypeDocument)
            {
                int clicks = e.Delta;
                if (e.Delta < 0)
                {
                    applicationObject.ExecuteCommand( "Edit.ScrollPageDown", "" );
                }
                else
                {
                    applicationObject.ExecuteCommand( "Edit.ScrollPageUp", "" );
                }
            }
        }
        catch (Exception exc)
        {
            MessageBox.Show( exc.ToString( ), "An exception occurred in mouse_enhancer_MouseWheel()" );
        }
    }


     

     


  • oasisman

    I have adapted the preceding code to pick up WM_LBUTTONDOWN messages. It works. However the mouse click event handler is executing twice. Which really doesn't work for what I am trying to do.


  • Steven I.

    Hai Sam,
                If your question is for VS.NET 2003, then I 'm afraid there is no events available that can help you to capture those Key Events. The available events with the DTE are as follows;


    windowsEvents = (EnvDTE.WindowEvents)events.get_WindowEvents(null);
       textEditorEvents = (EnvDTE.TextEditorEvents)events.get_TextEditorEvents(null);
       taskListEvents = (EnvDTE.TaskListEvents)events.get_TaskListEvents("");
       solutionEvents = (EnvDTE.SolutionEvents)events.SolutionEvents;
       selectionEvents = (EnvDTE.SelectionEvents)events.SelectionEvents;
       outputWindowEvents = (EnvDTE.OutputWindowEvents)events.get_OutputWindowEvents("");
       findEvents = (EnvDTE.FindEvents)events.FindEvents;
       dteEvents = (EnvDTE.DTEEvents)events.DTEEvents;
       documentEvents = (EnvDTE.DocumentEvents)events.get_DocumentEvents(null);
       debuggerEvents = (EnvDTE.DebuggerEvents)events.DebuggerEvents;
       commandEvents = (EnvDTE.CommandEvents)events.get_CommandEvents("{00000000-0000-0000-0000-000000000000}", 0);
       buildEvents = (EnvDTE.BuildEvents)events.BuildEvents;
       miscFilesEvents = (EnvDTE.ProjectItemsEvents)events.MiscFilesEvents;
       solutionItemsEvents = (EnvDTE.ProjectItemsEvents)events.SolutionItemsEvents;

       windowsEvents.WindowActivated += new _dispWindowEvents_WindowActivatedEventHandler(this.WindowActivated);
       windowsEvents.WindowClosing += new _dispWindowEvents_WindowClosingEventHandler(this.WindowClosing);
       windowsEvents.WindowCreated += new _dispWindowEvents_WindowCreatedEventHandler(this.WindowCreated);
       windowsEvents.WindowMoved += new _dispWindowEvents_WindowMovedEventHandler(this.WindowMoved);
       
       textEditorEvents.LineChanged += new _dispTextEditorEvents_LineChangedEventHandler(this.LineChanged);

       taskListEvents.TaskAdded += new _dispTaskListEvents_TaskAddedEventHandler(this.TaskAdded);
       taskListEvents.TaskModified += new _dispTaskListEvents_TaskModifiedEventHandler(this.TaskModified);
       taskListEvents.TaskNavigated += new _dispTaskListEvents_TaskNavigatedEventHandler(this.TaskNavigated);
       taskListEvents.TaskRemoved += new _dispTaskListEvents_TaskRemovedEventHandler(this.TaskRemoved);

       solutionEvents.AfterClosing += new _dispSolutionEvents_AfterClosingEventHandler(this.AfterClosing);
       solutionEvents.BeforeClosing += new _dispSolutionEvents_BeforeClosingEventHandler(this.BeforeClosing);
       solutionEvents.Opened += new _dispSolutionEvents_OpenedEventHandler(this.Opened);
       solutionEvents.ProjectAdded += new _dispSolutionEvents_ProjectAddedEventHandler(this.ProjectAdded);
       solutionEvents.ProjectRemoved += new _dispSolutionEvents_ProjectRemovedEventHandler(this.ProjectRemoved);
       solutionEvents.ProjectRenamed += new _dispSolutionEvents_ProjectRenamedEventHandler(this.ProjectRenamed);
       solutionEvents.QueryCloseSolution += new _dispSolutionEvents_QueryCloseSolutionEventHandler(this.QueryCloseSolution);
       solutionEvents.Renamed += new _dispSolutionEvents_RenamedEventHandler(this.Renamed);

       selectionEvents.OnChange += new _dispSelectionEvents_OnChangeEventHandler(this.OnChange);

       outputWindowEvents.PaneAdded += new _dispOutputWindowEvents_PaneAddedEventHandler(this.PaneAdded);
       outputWindowEvents.PaneClearing += new _dispOutputWindowEvents_PaneClearingEventHandler(this.PaneClearing);
       outputWindowEvents.PaneUpdated += new _dispOutputWindowEvents_PaneUpdatedEventHandler(this.PaneUpdated);

       findEvents.FindDone += new _dispFindEvents_FindDoneEventHandler(this.FindDone);

       dteEvents.ModeChanged += new _dispDTEEvents_ModeChangedEventHandler(this.ModeChanged);
       dteEvents.OnBeginShutdown += new _dispDTEEvents_OnBeginShutdownEventHandler(this.OnBeginShutdown);
       dteEvents.OnMacrosRuntimeReset += new _dispDTEEvents_OnMacrosRuntimeResetEventHandler(this.OnMacrosRuntimeReset);
       dteEvents.OnStartupComplete += new _dispDTEEvents_OnStartupCompleteEventHandler(this.OnStartupComplete);

       documentEvents.DocumentClosing += new _dispDocumentEvents_DocumentClosingEventHandler(this.DocumentClosing);
       documentEvents.DocumentOpened += new _dispDocumentEvents_DocumentOpenedEventHandler(this.DocumentOpened);
       documentEvents.DocumentOpening += new _dispDocumentEvents_DocumentOpeningEventHandler(this.DocumentOpening);
       documentEvents.DocumentSaved += new _dispDocumentEvents_DocumentSavedEventHandler(this.DocumentSaved);

       debuggerEvents.OnContextChanged += new _dispDebuggerEvents_OnContextChangedEventHandler(this.OnContextChanged);
       debuggerEvents.OnEnterBreakMode += new _dispDebuggerEvents_OnEnterBreakModeEventHandler(this.OnEnterBreakMode);
       debuggerEvents.OnEnterDesignMode += new _dispDebuggerEvents_OnEnterDesignModeEventHandler(this.OnEnterDesignMode);
       debuggerEvents.OnEnterRunMode += new _dispDebuggerEvents_OnEnterRunModeEventHandler(this.OnEnterRunMode);
       debuggerEvents.OnExceptionNotHandled += new _dispDebuggerEvents_OnExceptionNotHandledEventHandler(this.OnExceptionNotHandled);
       debuggerEvents.OnExceptionThrown += new _dispDebuggerEvents_OnExceptionThrownEventHandler(this.OnExceptionThrown);

       commandEvents.AfterExecute += new _dispCommandEvents_AfterExecuteEventHandler(this.AfterExecute);
       commandEvents.BeforeExecute += new _dispCommandEvents_BeforeExecuteEventHandler(this.BeforeExecute);

       buildEvents.OnBuildBegin += new _dispBuildEvents_OnBuildBeginEventHandler(this.OnBuildBegin);
       buildEvents.OnBuildDone += new _dispBuildEvents_OnBuildDoneEventHandler(this.OnBuildDone);
       buildEvents.OnBuildProjConfigBegin += new _dispBuildEvents_OnBuildProjConfigBeginEventHandler(this.OnBuildProjConfigBegin);
       buildEvents.OnBuildProjConfigDone += new _dispBuildEvents_OnBuildProjConfigDoneEventHandler(this.OnBuildProjConfigDone);

       miscFilesEvents.ItemAdded += new _dispProjectItemsEvents_ItemAddedEventHandler(this.MiscFilesEvents_ItemAdded);
       miscFilesEvents.ItemRemoved += new _dispProjectItemsEvents_ItemRemovedEventHandler(this.MiscFilesEvents_ItemRemoved);
       miscFilesEvents.ItemRenamed += new _dispProjectItemsEvents_ItemRenamedEventHandler(this.MiscFilesEvents_ItemRenamed);

       solutionItemsEvents.ItemAdded += new _dispProjectItemsEvents_ItemAddedEventHandler(this.SolutionItemsEvents_ItemAdded);
       solutionItemsEvents.ItemRemoved += new _dispProjectItemsEvents_ItemRemovedEventHandler(this.SolutionItemsEvents_ItemRemoved);
       solutionItemsEvents.ItemRenamed += new _dispProjectItemsEvents_ItemRenamedEventHandler(this.SolutionItemsEvents_ItemRenamed);


     



    sorry.... or may be try to Google for KeyEvents with the DTE Idea  



  • Adaptation