Introduction
This is a VS.NET add-in sample project that I wrote so that I could do a Find and bookmark of all occurrences of a text selection in an open code file. I'm somewhat lazy. So, I thought it would be convenient to have this functionality available on the code window context menu, instead of having to open the Find window or learn a bunch of new keyboard key sequences.
I'd rather use the mouse any day over typing things in. If you can do it with the mouse as well as the keys, this gives you more power.
Besides, I thought it would be a good way to learn about .NET add-ins and how they're coded; and this is why I wrote this article, to help others understand .NET add-ins.
It is no small task, believe me, to figure some of this stuff out. If you believe the rather hard to find documentation, it should be as easy as dirt. But when you actually start doing the code and expect it to work, the unpleasant surprises are many. I don't know how many hours I spent just trying to find how to put a menu item in the context menu of a code window. Turns out, you have to know the name of the .NET toolbar in which you want to add your own item. So, where are these built-in toolbars listed?
Nowhere that I could discover! I waded through tons of Office application docs, VS.NET docs, web sites galore, and found zero information on this. You'd think they would at least provide a list of toolbar names you need to know for adding custom menu items, right? Wrong - as usual. You have to bend over backwards to find this information.
Macro
So, I ended up writing this little macro you can use that lists all the available toolbars (CommandBars
as they are called in Office VSA):
Sub GetCommandBarList()
Dim Cmd As CommandBar
Dim CmdBars As CommandBars
Dim Doc As Document
Dim app As Application
Dim TxtDoc As TextDocument
DTE.ItemOperations.NewFile("General\Text File")
Doc = ActiveDocument
TxtDoc = Doc.Object("TextDocument")
CmdBars = DTE.CommandBars
For Each Cmd In CmdBars
If (Cmd.Name <> "") Then
TxtDoc.Selection.Text = "Name: " & Cmd.Name & vbTab & _
"Type: " & Cmd.Type.ToString() & vbLf
TxtDoc.Selection.Collapse()
End If
Next
End Sub
Existing context menus are of type MsoBarTypePopup
. The CommandBar
or toolbar you must use to add menu items to the contextual menu of the code editor windows is named "Code Window".
The important parts of the code are the Add-in Wizard added OnConnect()
- with added code for adding the new menu item to the context menu of the Code Window CommandBar
:
public void OnConnection(object application,
Extensibility.ext_ConnectMode connectMode, object addInInst,
ref System.Array custom)
{
applicationObject = (_DTE)application;
addInInstance = (AddIn)addInInst;
if( connectMode == Extensibility.ext_ConnectMode.ext_cm_UISetup )
{
object []contextGUIDS = new object[] { };
Commands commands = applicationObject.Commands;
_CommandBars commandBars = applicationObject.CommandBars;
try
{
Command command = commands.AddNamedCommand(addInInstance,
"TextFinder", "Text Finder",
"Executes the command for Text Finder",
true, 183, ref contextGUIDS,
(int)vsCommandStatus.vsCommandStatusSupported+
(int)vsCommandStatus.vsCommandStatusEnabled );
CommandBar commandBar = (CommandBar)commandBars["Code Window"];
CommandBarControl commandBarControl =
command.AddControl( commandBar, 1 );
}
catch(System.Exception )
{
}
}
}
The Add-in Wizard added QueryStatus()
method:
public void QueryStatus(string commandName,
EnvDTE.vsCommandStatusTextWanted neededText,
ref EnvDTE.vsCommandStatus status, ref object commandText)
{
if(neededText ==
EnvDTE.vsCommandStatusTextWanted.vsCommandStatusTextWantedNone)
{
if(commandName == "TextFinder.Connect.TextFinder")
{
TextSelection txt =
applicationObject.ActiveDocument.Selection as TextSelection;
if( txt.Text.Length > 0 )
status = (vsCommandStatus)vsCommandStatus.vsCommandStatusSupported
| vsCommandStatus.vsCommandStatusEnabled;
else
{
status = (vsCommandStatus)vsCommandStatus.vsCommandStatusLatched
| vsCommandStatus.vsCommandStatusSupported;
}
}
}
}
The Add-in Wizard added Exec()
method:
public void Exec(string commandName,
EnvDTE.vsCommandExecOption executeOption, ref object varIn,
ref object varOut, ref bool handled)
{
handled = false;
if( executeOption ==
EnvDTE.vsCommandExecOption.vsCommandExecOptionDoDefault )
{
if( commandName == "TextFinder.Connect.TextFinder" )
{
handled = DoFind();
return;
}
}
}
And last of all, the only method you have to add yourself, the DoFind()
method:
private bool DoFind()
{
TextSelection txt =
applicationObject.ActiveDocument.Selection as TextSelection;
this.applicationObject.Find.FindWhat = txt.Text;
applicationObject.Find.Action = vsFindAction.vsFindActionBookmarkAll;
applicationObject.Find.Target = vsFindTarget.vsFindTargetCurrentDocument;
applicationObject.Find.MatchInHiddenText = true;
applicationObject.Find.ResultsLocation =
vsFindResultsLocation.vsFindResults1;
EnvDTE.vsFindResult res = applicationObject.Find.Execute();
return ( res.Equals( vsFindResult.vsFindResultFound ) );
}
This method is what does the actual Finding and Bookmarking. It's simply taken from the existing commands available in the VS.NET text editor windows.
To use, run the setup.exe or the TextFinderSetup.msi to install the add-in. Do a merge on the .reg file to make sure all the information is correctly entered into the Registry. The .reg file writes Registry entries for both HKEY_LOCAL_MACHINE
and HKEY_CURRENT_USER
.
Hope you find this useful both as a tool and as an add-in programming example.