Inside Microsoft® Visual Studio® .NET 2003 [Electronic resources] نسخه متنی

اینجــــا یک کتابخانه دیجیتالی است

با بیش از 100000 منبع الکترونیکی رایگان به زبان فارسی ، عربی و انگلیسی

Inside Microsoft® Visual Studio® .NET 2003 [Electronic resources] - نسخه متنی

Brian Johnson

| نمايش فراداده ، افزودن یک نقد و بررسی
افزودن به کتابخانه شخصی
ارسال به دوستان
جستجو در متن کتاب
بیشتر
تنظیمات قلم

فونت

اندازه قلم

+ - پیش فرض

حالت نمایش

روز نیمروز شب
جستجو در لغت نامه
بیشتر
لیست موضوعات
توضیحات
افزودن یادداشت جدید










Creating an Add-in Command


Now that you know how commands are named, found, and run, it's time to create your own command. As we saw earlier, when a command built into Visual Studio .NET is invoked, the add-on program for that command is located because of the GUID assigned to the command, and it is asked to handle the command invocation. Likewise, commands that you create need a target that handles the command invocation. Commands can be dynamically created and removed, but creating them requires that an add-in be associated with the new command so Visual Studio .NET can find and use that add-in as the target. The method to create a command, AddNamedCommand, can be found on the DTE.Commands collection object; here is its signature:


public EnvDTE.Command AddNamedCommand(EnvDTE.AddIn AddInInstance,
string Name, string ButtonText, string Tooltip, bool MSOButton,
int Bitmap = 0, ref object[] ContextUIGUIDs,
int vsCommandDisabledFlagsValue = 16)

Here are the arguments:


AddInInstance

The AddIn object that will act as the command invocation target.


Name

The name of the command. The name can contain only alphanumeric characters and the underscore character. Any invalid characters that are used are mapped to the underscore character.


ButtonText

The text that is displayed on any user interface elements such as buttons for the command that are placed on menus or command bars.


ToolTip

Descriptive text providing users information about the command.


MSOButton

True if the bitmap to display on user interface elements for this command should use the predefined command bar button graphics. If False, then the graphic for the button is retrieved from the satellite DLL that is specified in the registration information for the add-in.


Bitmap

If the MSOButton argument is True, then it is the index of the predefined command bar button graphic. See the HTML page in the CommandUIBmps folder included with the book's sample files for a listing of available images. If MSOButton is False, then this is the resource identifier of the bitmap picture in the satellite DLL.


ContextUIGUIDs

This parameter is unused for Visual Studio .NET 2003 and is reserved for a future version. An empty array of type System.Object should be passed for this value.


vsCommandDisabledFlagsValue

This is the default availability state of the button. If the add-in that handles the command invocation has not yet been loaded, rather than forcing the add-in to load to find how the command should be displayed, this argument provides a default availability state. This argument value is used in place of the value returned through the StatusOption argument of the IDTExtensibility2.QueryStatus method, which we'll discuss later in this chapter.


When called, this method adds an item to the internal list of commands maintained by Visual Studio .NET. The full name of the command, which you can use in the Command Window or as an argument to the ExecuteCommand method, is constructed by taking the ProgID of the add-in and concatenating a period, followed by the value of the Name parameter. So, for example, if the name you provide to the AddNamedCommand method is MyCommand and the ProgID of the add-in is MyAddin.Connect, the name of the command that's created is MyAddin.Connect.MyCommand.

All commands added with this method also have a GUID and ID pair assigned to them. The GUID that is used for all commands created with AddNamedCommand is defined by the constant EnvDTE.Constants.vsAddInCmdGroup; the ID value starts at the index 1 for the first call to AddNamedCommand, and it is incremented by 1 every time the AddNamedCommand method is called. Because of the data type used for IDs, a total of 4,294,967,296 commands can be created before conflicts arise between two add-in created commands.


Handling a Command


With a newly created command, our code now needs to provide a way for Visual Studio .NET to call back to the add-in to let it know when the command is invoked. Usually, when an add-in or macro wants to be informed when the user has performed an action, an event connection is made. But command handlers work a bit differently: rather than connecting to an event source, your add-in must implement a specific interface. The reason for not using events is simple. When an add-in command is invoked, if the add-in that handles that command hasn't been loaded, the code for the add-in is loaded into memory and run by calling the OnConnection and other appropriate IDTExtensibility2 methods, just as if you were to go into the Add-in Manager dialog box and select the check box for that add-in. Because the add-in is demand-loaded (loaded when the command is run), code within that add-in could not have been run to connect to an event handler.

The interface to handle command invocations, named IDTCommandTarget, is modeled on the IOleCommandTarget interface of the Win32 SDK but has been changed to be IDispatch-compatible and easier to use. This is its signature:


public interface IDTCommandTarget
{
public void Exec(string CmdName,
EnvDTE.vsCommandExecOption ExecuteOption, ref object VariantIn,
ref object VariantOut, ref bool Handled);
public void QueryStatus(string CmdName,
EnvDTE.vsCommandStatusTextWanted NeededText,
ref EnvDTE.vsCommandStatus StatusOption,
ref object CommandText);
}

When invoked, all commands that your add-in creates are dispatched through this interface, particularly through the Exec method. The Exec method has the following arguments:


CmdName

The full name of the command. Your add-in should do a case-sensitive compare on this string to determine which command is being asked to run because all commands that the add-in creates are sent to this method for handling.


vsCommandExecOption

For most situations, the value passed to this parameter is the vsCommandExecOptionDoDefault enumeration value, informing your add-in that it should do the work defined for that command.


VariantIn

As you'll see later in this chapter, commands can be passed data as an argument. If any arguments are passed to your command, they are passed through this argument. If your command is invoked through the user interface on a menu or a toolbar, the value of this parameter is null or Nothing (depending on the programming language used to write the add-in).


VariantOut

This argument is used to pass data from your add-in to the caller. However, Visual Studio .NET will ignore any value that your command returns.


Handled

This argument allows your add-in to pass back data to Visual Studio .NET signaling whether your add-in handled the command. If a true value is returned, it is assumed that no further processing for the command is necessary. If this value is set to false on return, Visual Studio .NET continues searching for a handler for the command. The search should fail because no other command handler will accept the same GUID and ID pair for the command your add-in has created.



Command State


A command and its user interface don't always need to be enabled and available to the user. For example, your add-in's command might be available only when a text editor is the currently active window. You can control whether your command is enabled, disabled, or in the latched state (which means a check mark is drawn next to the button if it is a menu item or appears with a box drawn around it if it is on a toolbar). You control this state by using the QueryStatus method of the IDTCommandTarget interface. If your add-in hasn't yet been loaded, the default status, or value passed as the last argument of AddNamedCommand, is used to control the default behavior. However, once you've loaded the add-inby executing the command or manually through the Add-in Manager dialog boxQueryStatus is called to determine the state. The QueryStatus method has the following arguments:


CmdName

This argument has the same meaning as the CmdName argument passed to the Exec method of the IDTCommandTarget interface.


NeededText

This parameter is always vsCommandStatusTextWantedNone. Your add-in should always verify that this value is passed because the other values are reserved for future versions of Visual Studio .NET.


StatusOption

Your add-in should fill in this parameter, which lets Visual Studio .NET know whether the add-in command is supported (vsCommandStatusSupported) or unsupported (vsCommandStatus­Unsupported), whether the command is enabled and can be called (vsCommandStatusEnabled), whether the command user interface can't be seen (vsCommandStatusInvisible), or whether the user interface is drawn in the selected state (vsCommandStatusLatched). You can logically OR these values together to create the current status of the command and pass it back through this argument.


CommandText

This value currently isn't used by Visual Studio .NET, and shouldn't be modified.


Periodically, such as when the focus changes from one window to another or when a menu is displayed that contains an add-in command, Visual Studio .NET calls QueryStatus for that command to ensure that the user interface is synchronized with the command state. It is important to keep the code that implements QueryStatus as efficient as possible; otherwise, the user interface might become sluggish. Suppose you create a command that copies the currently active file to a network share. You might be tempted to have the Query­Status method of a command check to ensure that a network connection is available and the share location can be found. If the network and shared location can be found, the command is enabled; otherwise, it is disabled. Testing for these connections can be time-consuming, taking up to a minute or more to complete. A user who has to wait more than a minute for your command to update itself would be much happier if the command were always enabled and he or she would receive an error message when the command was invoked.

Programmatically Determining Command State


At times, you might need to programmatically determine whether a command is enabled, such as when you want to invoke a command using DTE.ExecuteCommand. All commands, whether a macro command, one created by an add-in, or one built into Visual Studio .NET, support a QueryStatus method. When you invoke the DTE.ExecuteCommand and the command isn't enabled because the QueryStatus method returned a value indicating that isn't currently available, you'll get an exception if you're using a language supported by the .NET Framework.

To check whether a command is enabled and avoid this error condition, you can use the Command.IsAvailable property. For example, to make sure that the Build.BuildSolution command can be called before you invoke it, you can use the following code:


Sub CheckAvailability()
If (DTE.Commands.Item("Build.BuildSolution").IsAvailable = True) Then
DTE.ExecuteCommand("Build.BuildSolution")
End If
End Sub


How an Add-in Command Handler Is Found


When a user invokes your command, Visual Studio .NET needs to know which add-in handles that command so it can call the methods of the IDTCommandTarget interface. It first inspects the command name; as noted earlier, the first part of the full command name is the ProgID of the add-in, and the remainder is the value passed for the Name parameter of the AddNamedCommand method. To locate the add-in, Visual Studio .NET extracts the ProgID from the command name and then checks the add-in corresponding to that ProgID to see whether it's loaded; if it isn't, it is told to load. Visual Studio .NET looks for the IDTCommandTarget interface (which must be implemented on the same object that implements IDTExtensibility2) on the add-in object instance, and then it calls the Exec method, passing the name of the command as the first parameter.

If, during this process, the add-in can't be found, the user is presented with the message box shown in Figure 7-2.


Figure 7-2. The message box displayed by Visual Studio .NET when a command's add-in doesn't load



If the user clicks the Yes button, the command is removed using the Command.Delete method, and any user interface elements for that command are removed. If the add-in is loaded but the IDTCommandTarget interface can't be found on the add-in object, the command is treated as if the QueryStatus method had returned the vsCommandStatusUnsupported flag.


/ 118