Table 13-3 OnConnection Method Parameters application Compile-time type is Object, but the runtime type is defined by the version you’re at.. Listing 13-2 The OnConnection method C#: p
Trang 1/// <summary>
/// Implements the Exec method of the IDTCommandTarget
/// interface This is called when the command is invoked
/// </summary>
/// <param term='commandName'>
/// The name of the command to execute
/// </param>
/// <param term='executeOption'>
/// Describes how the command should be run
/// </param>
/// <param term='varIn'>
/// Parameters passed from the caller to the command handler
/// </param>
/// <param term='varOut'>
/// Parameters passed from the command handler to the caller
/// </param>
/// <param term='handled'>
/// Informs the caller if the command was handled or not
/// </param>
/// <seealso class='Exec' />
public void Exec(
string commandName, vsCommandExecOption executeOption,
ref object varIn, ref object varOut, ref bool handled)
{
}
private DTE2 _applicationObject;
private AddIn _addInInstance;
}
}
You’ve had an overview of what the IDTExtensibility2 and IDTCommandTarget
interfaces do and reviewed the comments in Listing 13-1 In the next section, you’ll see
how to add your own code to the interface methods to make the KeystrokeFinder Add-In
perform some useful work.
Adding Functionality to an Add-In
When implementing the functionality of an Add-In, you’ll be most concerned with
capturing the call to Exec, which VS calls whenever the user selects the Tools menu item
for your Add-In This section will also cover a couple of other methods: OnConnection,
which contains a lot of initialization code, and QueryStatus, which is handy for managing
the state of the Add-In menu item We’ll look at OnConnection first so that you can see
how the Add-In is initialized.
Trang 2Reviewing the OnConnection Method
As you learned earlier, the Connect class implements various interface methods so
that VS can call into those methods to run your Add-In One of the primary methods
is OnConnection, which is a member of the IDTExtensibility2 interface VS calls
OnConnection when the Add-In loads When calling OnConnection, VS passes four
parameters that you can use to initialize the Add-In The Add-In Project Wizard, covered
in a previous section of this chapter, generates much skeleton code that uses parameter
values in OnConnection to initialize the Add-In While the example in this chapter doesn’t modify the OnConnection method, understanding the code is helpful in learning how the
Add-In initializes and how it does affect the code you will write later We’ll first take
another look at OnConnection parameters and then examine the generated code.
Understanding OnConnection Parameters
The OnConnection method has four parameters Each of the parameters are passed to the OnConnection method by VS; these parameters provide all of the information necessary for initializing the Add-In Table 13-3 lists each parameter and its purpose.
Table 13-3 OnConnection Method Parameters
application Compile-time type is Object, but
the runtime type is defined by the version you’re at For example,
on older versions of VS, the runtime type of Application was
DTE, but the runtime type of
Application in VS 2010 is DTE2.
Application is the parent object for the entire VS automation model You use this to access all of the windows, commands, and other parts of the IDE
connectMode Enum of type ext_ConnectMode Read this parameter to figure out when and how
the Add-In was loaded In a following section,
you’ll see how the OnConnection method reads this
value to figure out when the Add-In loads for the first time
addInInst The compile-time type is Object,
but runtime type is AddIn. This refers to the Add-In itself, allowing you to inspect various properties of the Add-In
custom Array These aren’t used in the current example, but
consider the fact that we’re implementing an interface Besides VS 2010, you could have another application (host) that supported Add-Ins
that implement the IDTExtensibility2 interface Those
hosts could use the custom array parameter to pass information specific to that application Therefore,
custom is another extensibility point to make the IDTExtensibility2 interface more flexible.
Trang 3Reviewing OnConnection Generated Code
You know that the purpose of the OnConnection method is to help initialize the Add-In, and
you’ve seen the parameters populated by VS and what each parameter means Listing 13-2
shows the code generated by VS after the Add-In Project Wizard completes It reflects the
result of choosing to have a command bar UI, shown in Figure 13-5 Code comments were
omitted to place more focus on the code itself.
Listing 13-2 The OnConnection method
C#:
public void OnConnection(
object application, ext_ConnectMode connectMode,
object addInInst, ref Array custom)
{
_applicationObject = (DTE2)application;
_addInInstance = (AddIn)addInInst;
if(connectMode == ext_ConnectMode.ext_cm_UISetup)
{
object []contextGUIDS = new object[] { };
Commands2 commands =
(Commands2)_applicationObject.Commands;
string toolsMenuName = "Tools";
Microsoft.VisualStudio.CommandBars.CommandBar
menuBarCommandBar = ((
Microsoft.VisualStudio.CommandBars.CommandBars)
_applicationObject.CommandBars)["MenuBar"];
CommandBarControl toolsControl =
menuBarCommandBar.Controls[toolsMenuName];
CommandBarPopup toolsPopup =
(CommandBarPopup)toolsControl;
try
{
Command command = commands.AddNamedCommand2(
_addInInstance, "KeystrokeFinder",
"KeystrokeFinder",
"Executes the command for KeystrokeFinder",
true, 59, ref contextGUIDS,
(int)vsCommandStatus
vsCommandStatusSupported+
(int)vsCommandStatus.vsCommandStatusEnabled,
Trang 4(int)vsCommandStyle
vsCommandStylePictAndText,
vsCommandControlType
vsCommandControlTypeButton);
if((command != null) &&
(toolsPopup != null))
{
command.AddControl(
toolsPopup.CommandBar, 1);
}
}
catch(System.ArgumentException)
{
}
}
}
VB:
Public Sub OnConnection(
ByVal application As Object,
ByVal connectMode As ext_ConnectMode,
ByVal addInInst As Object,
ByRef custom As Array) Implements IDTExtensibility2.OnConnection _applicationObject = CType(application, DTE2)
_addInInstance = CType(addInInst, AddIn)
If connectMode = ext_ConnectMode.ext_cm_UISetup Then
Dim commands As Commands2 =
CType(_applicationObject.Commands, Commands2)
Dim toolsMenuName As String = "Tools"
Dim commandBars As CommandBars =
CType(_applicationObject.CommandBars, CommandBars) Dim menuBarCommandBar As CommandBar =
commandBars.Item("MenuBar")
Dim toolsControl As CommandBarControl =
menuBarCommandBar.Controls.Item(toolsMenuName)
Dim toolsPopup As CommandBarPopup =
CType(toolsControl, CommandBarPopup)
Try
Trang 5Dim command As Command =
commands.AddNamedCommand2(
_addInInstance, "KeystrokeFinderVB",
"KeystrokeFinderVB",
"Executes the command for KeystrokeFinderVB",
True, 59, Nothing,
CType(vsCommandStatus.vsCommandStatusSupported,
Integer) +
CType(vsCommandStatus.vsCommandStatusEnabled,
Integer),
vsCommandStyle.vsCommandStylePictAndText,
vsCommandControlType.vsCommandControlTypeButton)
command.AddControl(toolsPopup.CommandBar, 1)
Catch argumentException As System.ArgumentException
End Try
End If
End Sub
Dissecting Listing 13-2 into its constituent parts demonstrates the role OnConnection
has and how it affects subsequent code The first part of the method obtains references to a
couple of important objects: application and addInInst The following excerpt shows how
to obtain a reference to these objects and convert them to DTE2 and AddIn, respectively
The references to _applicationObject and _addInInstance are fields of the Connect class,
which is important because now other methods of the class will be able to access these
objects.
C#:
_applicationObject = (DTE2)application;
_addInInstance = (AddIn)addInInst;
VB:
_applicationObject = CType(application, DTE2)
_addInInstance = CType(addInInst, AddIn)
The remaining code in OnConnection sets up the menu item under the Tools menu,
as directed by choosing to build a command UI, shown in Figure 13-5 However, this
only occurs one time—the first time the application runs To make sure the menu item
sets up one time, the code checks the connectMode parameter to see if it’s set to
Trang 6ext_ConnectMode.ext_cm_UISetup, as shown in the following code The remaining
code in the OnConnection method will only execute if the following condition is true:
C#:
if(connectMode == ext_ConnectMode.ext_cm_UISetup)
VB:
If connectMode = ext_ConnectMode.ext_cm_UISetup Then
The first time the code runs, the code within the preceding if statement will execute,
creating a menu item for the KeystrokeFinder Add-In in the Tools menu Code examples
that follow in this section are all contained within the preceding if statement; this is good
information to know because it shows you how to navigate the VS object model to find something.
The following code uses _applicationObject to get a list of commands, which is a list
of all the actions you can take with VS As discussed earlier, _applicationObject is type DTE2 and serves as the parent object for accessing all functionality in VS.
C#:
Commands2 commands =
(Commands2)_applicationObject.Commands;
VB:
Dim commands As Commands2 =
CType(_applicationObject.Commands, Commands2)
In the VS automation object model, a menu item is called a CommandBar So, you get
a reference to a CommandBars collection, again through _applicationObject, to reference the MenuBar, which is the main VS menu, assigned to menuBarCommandBar:
C#:
Microsoft.VisualStudio.CommandBars.CommandBar
menuBarCommandBar = ((
Microsoft.VisualStudio.CommandBars.CommandBars)
_applicationObject.CommandBars)["MenuBar"];
VB:
Dim commandBars As CommandBars =
CType(_applicationObject.CommandBars, CommandBars)
Dim menuBarCommandBar As CommandBar =
commandBars.Item("MenuBar")
Trang 7Within the CommandBars collection, menuBarCommandBar, you then look into the
Controls collection, which is a list of menus on the main menu to find the Tools menu,
assigned to toolsControl as follows:
C#:
string toolsMenuName = "Tools";
CommandBarControl toolsControl =
menuBarCommandBar.Controls[toolsMenuName];
VB:
Dim toolsMenuName As String = "Tools"
Dim toolsControl As CommandBarControl =
menuBarCommandBar.Controls.Item(toolsMenuName)
In the VS automation object model, an individual menu is a CommandBarPopup,
assigned to toolsPopup as follows:
C#:
CommandBarPopup toolsPopup =
(CommandBarPopup)toolsControl;
VB:
Dim toolsPopup As CommandBarPopup =
CType(toolsControl, CommandBarPopup)
Now you have a reference to the menu where the menu item for the Add-In must
be added You are ready to add the command, using the AddNamedCommand2 method
of the commands collection Remember that earlier code assigned these commands
from the application object to the commands variable A quick review of the arguments
to AddNamedCommand2 gives you the gist of what’s happening: The code passes a
reference to the Add-In; provides a menu item name and description; and indicates that
the status of the command is supported and enabled, the menu item will have pictures and
text, and the type of menu item is button (can be clicked) If you want all the details of
this method call, now is a good time to refer to the documentation While it’s important to
understand the major interfaces, such as OnConnection for IDTExtensibility2, memorizing
every API call might not be the most productive use of your time when you’re just starting
out The following code shows the call to AddNamedCommand2:
C#:
Command command = commands.AddNamedCommand2(
_addInInstance, "KeystrokeFinder",
"KeystrokeFinder",
"Executes the command for KeystrokeFinder",
Trang 8true, 59, ref contextGUIDS,
(int)vsCommandStatus
.vsCommandStatusSupported+
(int)vsCommandStatus.vsCommandStatusEnabled,
(int)vsCommandStyle
.vsCommandStylePictAndText,
vsCommandControlType
.vsCommandControlTypeButton);
VB:
Dim command As Command =
commands.AddNamedCommand2(
_addInInstance, "KeystrokeFinderVB",
"KeystrokeFinderVB",
"Executes the command for KeystrokeFinderVB",
True, 59, Nothing,
CType(vsCommandStatus.vsCommandStatusSupported,
Integer) +
CType(vsCommandStatus.vsCommandStatusEnabled,
Integer),
vsCommandStyle.vsCommandStylePictAndText,
vsCommandControlType.vsCommandControlTypeButton)
AddNamedCommand2 returned a Command object, command, which must be placed
into VS somewhere so that a user can click it to invoke the Add-In The next statement
accomplishes this task by adding command to the Tools menu As you may recall from
previous examples, the code searched for and obtained a reference to the Tools menu After
ensuring that both the command and toolsPopup refer to valid objects (a best practice), the
following code places command into the first position (at the top) of the Tools menu: C#:
if((command != null) &&
(toolsPopup != null))
{
command.AddControl(
toolsPopup.CommandBar, 1);
}
VB:
command.AddControl(toolsPopup.CommandBar, 1)
This completes the responsibilities of the OnConnection method If you had your own code for initializing the Add-In, the OnConnection method would be a good place to put
it The preceding example was useful because now you know how to access VS menus and commands The example also demonstrated the importance of the main application object and how it’s used as the starting point for getting to other part of VS.
Trang 9As you may recall, the OnConnection method assigned the main application object to
_applicationObject, a field of the Connect class This is important because now you have
access to the main application object, and you’ll see how it’s used in the next section,
which shows you how to execute your Add-In via the Exec method.
Implementing the Exec Method
Whenever a user starts your Add-In, VS calls the Exec method of the IDTCommandTarget interface The Exec method is important because that’s where you add your code to
implement the behavior of your Add-In The previous sections discussed code that is
generated by VS, but Listing 13-3 contains code for the Exec method that you should
enter yourself to make the KeystrokeFinder Add-In work The purpose of the Add-In
for this section is to list all VS commands and their associated shortcut keys The list of
commands and shortcuts will be displayed in the VS Output window Listing 13-3 shows
the Exec method for the KeystrokeFinder Add-In.
Listing 13-3 Implementing the Exec method
C#:
public void Exec(
string commandName, vsCommandExecOption executeOption,
ref object varIn, ref object varOut, ref bool handled)
{
handled = false;
if(executeOption ==
vsCommandExecOption.vsCommandExecOptionDoDefault)
{
if (commandName ==
"KeystrokeFinder.Connect.KeystrokeFinder")
{
OutputWindow outWin =
_applicationObject.ToolWindows.OutputWindow;
OutputWindowPane outPane =
outWin.OutputWindowPanes.Add(
"Keyboard Shortcuts");
outPane.Activate();
foreach (Command cmd in
_applicationObject.Commands)
{
object[] cmdBindings =
cmd.Bindings as object[];
Trang 10if (cmdBindings.Length > 0)
{
string bindingStr =
string.Join(", ", cmdBindings);
outPane.OutputString(
"Command: " + cmd.Name +
", Shortcut: " + bindingStr +
"\n");
}
}
handled = true;
return;
}
}
}
VB:
Public Sub Exec(
ByVal commandName As String,
ByVal executeOption As vsCommandExecOption,
ByRef varIn As Object, ByRef varOut As Object,
ByRef handled As Boolean) Implements IDTCommandTarget.Exec handled = False
If executeOption =
vsCommandExecOption.vsCommandExecOptionDoDefault Then
If commandName =
"KeystrokeFinderVB.Connect.KeystrokeFinderVB" Then Dim outWin As OutputWindow =
_applicationObject.ToolWindows.OutputWindow
Dim outPane As OutputWindowPane =
outWin.OutputWindowPanes.Add(
"Keyboard Shortcuts")
outPane.Activate()
For Each cmd As Command In _applicationObject.Commands Dim cmdBindings As Object() =
CType(cmd.Bindings, Object())
If cmdBindings.Length > 0 Then
Dim bindingStr As String =
String.Join(", ", cmdBindings)