Click here to Skip to main content
Click here to Skip to main content

Forward/Backward Code Navigation with the Mouse Thumb Buttons Inside Visual Studio 2010 (C++, Visual Basic, F#)

By , 7 Feb 2010
 
Prize winner in Competition "Visual Studio 2010 Extension Contest"

Introduction

Visual Studio 2010 lacks support to navigate in the source code using the mouse thumb buttons in the languages C++, Visual Basic and F#. The addin from this article will provide support for navigation in the source code using the mouse thumb buttons and it will show how to get mouse events inside an addin.

Background

This addin is an improved version of the addin for Visual Studio 2008: Forward/Backward navigation with the mouse thumb buttons for Visual Studio 2008 (C++). In the old addin, I used a mouse hook to react to mouse events. During the development for Visual Studio 2010, I found a much cleaner solution to listen to mouse events: NativeWindow class.

Usage

  1. Jump to a function definition (right click on the function name, "Go to definition").
  2. Jump back using the "backward button" of your mouse.

Implementation

The addin boilerplate is created with the Addin-wizard. It creates a class named Connect. The IDTExtensibility2 interface provides addin events like OnConnect or OnDisconnect. OnConnect provides the DTE2 object as parameter.

The main problem was to solve how to get mouse events. The addin API only support keyboard and high level events (and some other like focus). In this forum*, I found a clean solution to intercept the main IDE window message procedure: the NativeWindow class.

It provides low-level encapsulation of the window procedure. It is a wrapper to WinAPI subclassing using SetWindowLong() with GWL_WNDPROC. WndProc is called from the subclass window procedure. All windows within the same process could be subclassed. Since the addin is in the process of Visual Studio, NativeWindow could be used here. The implementation source code of NativeWindow can be found here.

In VS2010, the text editor area is not a child window anymore as in VS2008. It is directly paint inside the main IDE window. So the window which must be subclassed is the main IDE window. AssignHandle(HWND_IDE_WINDOW) does this step. The HWND ID from the main window is stored in DTE2.MainWindow.HWnd.

*: The other solution discussed in the forum thread IVsBroadcastMessageEvents::OnBroadcastMessage only provides the following messages: WM_WININICHANGE, WM_DISPLAYCHANGE, WM_SYSCOLORCHANGE, WM_PALETTECHANGED, WM_PALETTEISCHANGING and WM_ACTIVATEAPP.

The SubclassedWindow class which derives from NativeWindow looks like:

public class SubclassedWindow : NativeWindow
{
  private DTE2 dte;
  private const int WM_XBUTTONUP = 0x020C;
  private const int XBUTTON1 = 1;
  private const int XBUTTON2 = 2;

  public SubclassedWindow(int hWnd, DTE2 dte)
  {
    this.dte = dte;
    AssignHandle((IntPtr)hWnd);
  }

  static int HiWord(IntPtr Number)
  {
    return (ushort)((Number.ToInt32() >> 16) & 0xffff);
  }

  protected override void WndProc(ref Message m)
  {
    if (m.Msg == WM_XBUTTONUP)
    {
      if (this.dte.ActiveDocument != null &&
          (this.dte.ActiveDocument.Language == "C/C++" ||
          this.dte.ActiveDocument.Language == "Basic" ||
          this.dte.ActiveDocument.Language == "F#"))
      {
        switch (HiWord(m.WParam))
        {
          case XBUTTON1:
            {
              try { this.dte.ExecuteCommand("View.NavigateBackward", ""); }
              catch { }
            } break;

          case XBUTTON2:
            {
              try { this.dte.ExecuteCommand("View.NavigateForward", ""); }
              catch { }
            }
            break;
        }
      }
    }

    base.WndProc(ref m);
  }
}

SubclassedWindow is created inside the OnConnection method. The WndProc method intercepts all window messages from the main window. The mouse thumb buttons have the value WM_XBUTTONUP for button release. ActiveDocument.Language contains the language of the currently active document as string. If the active document contains C++, Visual Basic or F# code View.NavigateBackward or View.NavigateForward will be executed using ExecuteCommand. This will trigger the same functionality as the Navigate Forward/Backward buttons from the VS menu.

Whole Addin Source Code

/*
 *  MouseThumbButtonsVS2010
 * 
 *  Copyright 2010, Jochen Baier, email@jochen-baier
 *  License: The Code Project Open License (CPOL) 1.02
 * 
 *  Addin for VS2010 providing support for forward/backward navigation with the
 *  mouse thumb buttons in C++, Visual Basic and F#.
 *   
 */

using System;
using Extensibility;
using EnvDTE;
using EnvDTE80;
using System.Diagnostics;
using System.Windows.Forms;

namespace MouseThumbButtonsVS2010
{
  public class SubclassedWindow : NativeWindow
  {
    private DTE2 dte;
    private const int WM_XBUTTONUP = 0x020C;
    private const int XBUTTON1 = 1;
    private const int XBUTTON2 = 2;

    public SubclassedWindow(int hWnd, DTE2 dte)
    {
      this.dte = dte;
      AssignHandle((IntPtr)hWnd);
    }

    static int HiWord(IntPtr Number)
    {
      return (ushort)((Number.ToInt32() >> 16) & 0xffff);
    }

    protected override void WndProc(ref Message m)
    {
      if (m.Msg == WM_XBUTTONUP)
      {
        if (this.dte.ActiveDocument != null &&
            (this.dte.ActiveDocument.Language == "C/C++" ||
            this.dte.ActiveDocument.Language == "Basic" ||
            this.dte.ActiveDocument.Language == "F#"))
        {
          switch (HiWord(m.WParam))
          {
            case XBUTTON1:
              {
                try { this.dte.ExecuteCommand("View.NavigateBackward", ""); }
                catch { }
              } break;

            case XBUTTON2:
              {
                try { this.dte.ExecuteCommand("View.NavigateForward", ""); }
                catch { }
              }
              break;
          }
        }
      }

      base.WndProc(ref m);
    }
  }

  public class Connect : IDTExtensibility2
  {
    private DTE2 _applicationObject;
    private AddIn _addInInstance;
    private SubclassedWindow subclassedMainWindow;

    public void OnConnection(object application, 
	ext_ConnectMode connectMode, object addInInst, ref Array custom)
    {
      _applicationObject = (DTE2)application;
      _addInInstance = (AddIn)addInInst;
      subclassedMainWindow = new SubclassedWindow
	(_applicationObject.MainWindow.HWnd, _applicationObject);
    }

    public void OnDisconnection(ext_DisconnectMode disconnectMode, ref Array custom)
    {
      if (subclassedMainWindow != null)
      {
        subclassedMainWindow.ReleaseHandle();
        subclassedMainWindow = null;
      }
    }

    #region not_used
    #endregion
  }
}

Limitations

The addin will only work if the text editor 'window' is docked.

History

  • Initial release based on the addin for VS 2008

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

About the Author

Jochen Baier
Germany Germany
Member
No Biography provided

Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
You must Sign In to use this message board.
Search this forum  
    Spacing  Noise  Layout  Per page   
NewsUsing in Visual Studio 2012memberscott sanders29 Sep '12 - 15:33 
This add-in also works in Visual Studio 2012. Install as before in your Visual Studio 2012\Addins folder, edit the <Version> tag from 10.0 to 11.0, and you're set. No VS restart required. Thanks for the add-in.
GeneralRe: Using in Visual Studio 2012memberMatt Durak7 Nov '12 - 9:20 
I had to edit the .addin file and then build the solution in VS2012 before it would work. If you just change the .addin file it crashes
QuestionHad to rebuild dll for vs2010 sp1memberFarrenYoung28 Oct '11 - 9:18 
Addin was throwing error on load. So if anyone else gets this you will need to load source and rebuild and use your dll instead if this happens. Great addin! Poke tongue | ;-P
GeneralInteresting....but missing one thingmemberjerfypowell2 Aug '10 - 7:29 
This looks nice and I have been looking around for it. Now that I found it, I am happy once more. However, no idea why you didnt include C# files also. If you wish here it is and you can add this to your code....
 
I changed your happy little if statement to:
if (this.dte.ActiveDocument != null &&
(this.dte.ActiveDocument.Language == "C/C++" ||
this.dte.ActiveDocument.Language == "Basic" ||
this.dte.ActiveDocument.Language == "F#" ||
this.dte.ActiveDocument.Language == "C#"))
 
So now... it works in C#.
GeneralRe: Interesting....but missing one thingmemberJochen Baier2 Aug '10 - 10:29 
hi,
 
in c# the mouse buttons works without the plugin.
 
jochen
GeneralRe: Interesting....but missing one thing [modified]memberjerfypowell2 Aug '10 - 10:34 
You know, unless I am remembering a different version of visual Studio but for some reason they never would navigate back/forward correctly, or it would end up in some other place that I never did visit...was a bit odd.

modified on Monday, August 2, 2010 4:44 PM

GeneralThat is one serious make-somebody's-day addin !memberRaph SP14 Jun '10 - 3:31 
I didn't think it would take so little code to make me see life under a new light...
I could write a thank-you message longer than any EULA, but I'll stick with "Works as advertised. Thanks so much."
GeneralGets my '5' !!memberBill Gord16 Feb '10 - 5:42 
I'm thinking of upgrading to VS2010, so this is a real encouragement!! I just found your previous article (via this one) and will install that into my VS2005.
 
THANKS!!
GeneralMy vote of 2memberDennisAT15 Feb '10 - 23:20 
article does not make sense and does not bring anything valuable.
GeneralRe: My vote of 2memberJochen Baier16 Feb '10 - 9:08 
hi,
 
can you be more precise what does not make sense ?
 
thanks jochen
GeneralNeatomvpSacha Barber8 Feb '10 - 0:38 
Neato
Sacha Barber
  • Microsoft Visual C# MVP 2008/2009
  • Codeproject MVP 2008/2009
Your best friend is you.
I'm my best friend too. We share the same views, and hardly ever argue
 
My Blog : sachabarber.net

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Permalink | Advertise | Privacy | Mobile
Web01 | 2.6.130523.1 | Last Updated 8 Feb 2010
Article Copyright 2010 by Jochen Baier
Everything else Copyright © CodeProject, 1999-2013
Terms of Use
Layout: fixed | fluid