Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

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

0.00/5 (No votes)
7 Feb 2010 1  
Addin to navigate in your code using the thumb buttons of your mouse

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 has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here