VSPackage Tutorial 3: How to Extend the Tool Window

In this tutorial, you add to the tool window that you created in VSPackage Tutorial 2: How to Create a Tool Window. You add a button that displays an Open File dialog box so that you can select a file to play in the Media Player. You also add to the toolbar a drop-down list that contains the names of available playlists.

This tutorial demonstrates the following tasks:

  • Adding dynamic visibility to a menu command.

  • Adding Windows Forms code to interact with a tool window.

  • Using a combo box in a tool window toolbar.

This tutorial is part of a series that teaches how to extend the Visual Studio integrated development environment (IDE). For more information, see Tutorials for Customizing Visual Studio By Using VSPackages.

Add Dynamic Visibility to a Menu Command

By default, menu commands are fixed in place and cannot be hidden after they are shown. However, the design in this tutorial is that the package decides at run time whether the command should be visible, based on whether the Media Player tool window is active.

You must first modify FirstToolWin.vsct to enable dynamic visibility, then implement logic to set the visibility in code. You create a class to hold tool window events, then modify the package to make the menu command object accessible, and then tie them together.

To enable dynamic visibility

  1. Open the "FirstToolWin" project, which you created in VSPackage Tutorial 2: How to Create a Tool Window.

  2. In Solution Explorer, open FirstToolWin.vsct.

  3. Find the <Button> element that has the ID, cmdidWindowsMedia. Between the <icon> definition and the <Strings> section, add two command flags, as follows.

    <CommandFlag>DefaultInvisible</CommandFlag>
    <CommandFlag>DynamicVisibility</CommandFlag>
    
  4. Save the file.

To create tool window events

  1. In Solution Explorer, right-click the project name, point to Add, and then click Class.

  2. In the Add New Item dialog box, name the file ToolWindowEvents.cs and then click Add.

  3. Open ToolWindowEvents.cs and add the following code after the existing using statements.

    using Microsoft.VisualStudio.Shell.Interop;
    using System.ComponentModel.Design;
    using System.Security.Permissions;
    
  4. Change the class declaration so that it is public and sealed, and inherits from the IVsWindowFrameNotify3 interface, as follows.

    public sealed class ToolWindowEvents : IVsWindowFrameNotify3
    
  5. Right-click IVsWindowFrameNotify3, point to Implement Interface, and then click Implement Interface to automatically add the methods that are required for the interface.

  6. Implement the OnShow method, as follows.

    [PrincipalPermission(SecurityAction.Demand)]
    public int OnShow(int fShow)
    {
        package.toolMenuItem1.Visible 
            = ((__FRAMESHOW)fShow 
            != __FRAMESHOW.FRAMESHOW_WinHidden);
        return Microsoft.VisualStudio.VSConstants.S_OK;
    }
    
  7. Replace the remaining throw statements by using the following return statement.

    return Microsoft.VisualStudio.VSConstants.S_OK;
    

    The added code declares a class that will receive the events for the tool window. The event used here is OnShow, in which the visibility of the menu command is set based on the value that is passed into the event. The value passed in is an enumeration of the type __FRAMESHOW. Although this enumeration has several possible values, in this case, you must test whether it is equal to FRAMESHOW_WinHidden. If it is not equal, then set Visible to true. If it is equal, then set Visible to false.

    However, notice that this code uses a variable named "package." That variable is passed in to the constructor.

  8. Add the following code to the top of the class, above the region where the IVsWindowFrameNotify3 interface members are defined.

    private FirstToolWinPackage package;
    public ToolWindowEvents(FirstToolWinPackage apackage)
    {
        package = apackage;
    }
    

    The value passed into this constructor is the package itself, the FirstToolWin instance. In the OnShow event, the menu command must be accessed. By default, this menu command is a locally scoped variable in the Initialize method of the package.

  9. Save the file.

To access the menu command

  1. In Solution Explorer, open FirstToolWinPackage.cs so that you can modify the FirstToolWin class to make the menu command object accessible.

  2. Declare a MenuCommand object at the top of the class to represent your menu command.

    public MenuCommand toolMenuItem1;
    
  3. Scroll to the Initialize method. Locate the following two lines, which create the first command.

    MenuCommand menuItem = new MenuCommand(
     MenuItemCallback, menuCommandID);
    mcs.AddCommand(MenuItem);
    
  4. Replace the default MenuCommand object by using the public variable that was declared earlier.

    toolMenuItem1 = new MenuCommand(
        MenuItemCallback, menuCommandID);
    mcs.AddCommand(toolMenuItem1);
    
  5. Save the file.

Next, you will create an instance of the tool window and also an instance of the newly created class to receive the events. Then you can attach the class instance to the tool window instance.

To attach the events

  1. In Solution Explorer, open MyToolWindow.cs. Override the OnToolWindowCreated method by adding the following code at the end of the file, before the closing brace of the class definition.

    public override void OnToolWindowCreated()
    {
        base.OnToolWindowCreated();
    
        var handler = new ToolWindowEvents(
            (FirstToolWinPackage)this.Package);
        ((IVsWindowFrame)this.Frame).SetProperty(
            (int)__VSFPROPID.VSFPROPID_ViewHelper, handler);
    }
    

    This code creates a new instance of the class that was added earlier. It then adds the instance to the properties of the frame so that the instance can receive events. The SetProperty method accomplishes this when you pass the enumeration value VSFPROPID_ViewHelper for the first parameter, and the new handler instance for the second parameter.

  2. Save the file.

To test the code

  1. Press F5 to compile the project and run it in the experimental build of Visual Studio.

  2. Click the Tools menu.

    The WindowsMedia menu command should not appear.

  3. On the View menu, point to Other Windows and then click Windows Media Player to display the Media Player window.

  4. Click the Tools menu again.

    Now, the WindowsMedia menu command should appear because it has dynamic visibility; that is, whether it is visible is based on whether the Media Player window is visible.

  5. Close the Media Player window and then check the Tools menu again.

    Now, the WindowsMedia menu command should not appear.

  6. Close the experimental build.

Add Windows Forms Code to Interact with the Tool Window

Now, add code to the package class that responds to the menu command. This code opens an Open File dialog box and then calls the new LoadFile method to load a file in the Media Player.

To activate the tool window in code

  1. Open FirstToolWinPackage.cs.

  2. Add the System.Windows.Forms namespace just after the existing using statements, as follows.

    using System.Windows.Forms;
    
  3. Find the MenuItemCallback menu handler. This is the event handler for a click on the menu command. Replace the body of this menu handler by using the following code.

    OpenFileDialog openFileDialog = new OpenFileDialog();
    DialogResult result = openFileDialog.ShowDialog();
    if (result == DialogResult.OK)
    {
        MyToolWindow window = (MyToolWindow)(this.FindToolWindow(
            typeof(MyToolWindow), 0, true));
        if (window != null)
        {
            window.LoadFile(openFileDialog.FileName);
        }
    }
    
  4. Save your work.

This code opens an Open File dialog box so that you can browse to the media file that you want to load in the Media Player. Then it locates the MyToolWindow instance and calls the LoadFile method, which you created earlier, to load the media file.

To test the code

  1. Press F5 to compile the project and run it in the experimental build of Visual Studio.

  2. On the Tools menu, click WindowsMedia.

    The Open File dialog box should appear.

  3. In the Open File dialog box, select a valid media file and then click OK.

    The file should play in the Media Player.

  4. Close the experimental build.

Use the Combo Box in the Tool Window Toolbar

You can fill the drop-down combo box on the toolbar of Media Player tool window by adding playlist support. Then, the user can either select a playlist from the drop-down list, or type the name of a file or playlist in the drop-down box. To do this, use both handlers for the combo box, the handler for when an item is chosen or typed, and the handler that populates the list. To determine whether the user is typing the name of a file or a playlist, you compare the typed name to the items that are already in the list. If there is a match, then it is a playlist; otherwise, it is a file.

In VSPackage Tutorial 2: How to Create a Tool Window, you added two event handlers for the combo box, ComboHandler and ComboListHandler. Now you add code for playlist support to those handlers. You also add two member variables to hold the user-selected (or user-typed) file name and playlist name. When the user selects or types something in the combo box, the value is saved in the appropriate variable. When the user clicks the button, the member variables are read and the file or playlist is played.

To add playlist support to the Media Player

  1. Open MyToolWindow.cs.

  2. Add the following line of code to the top of the class body.

    WMPLib.IWMPPlaylist playlist = null;
    
  3. Replace the existing ComboHandler and ComboListHandler methods by using the following code.

    private void ComboHandler(object sender, EventArgs arguments)
    {
        var eventArgs = arguments as OleMenuCmdEventArgs;
        if (eventArgs != null)
        {
            var output = eventArgs.OutValue;
            var input = eventArgs.InValue;
            if (input != null)
            {
                // Save away the value from the combobox.
                comboValue = input.ToString();
                playlist = null;
    
                // Check if string is the name of a playlist. 
    
                // Declare the playlist  
                // and playlistarray variables.
                WMPLib.IWMPPlaylist aplaylist;
                WMPLib.IWMPPlaylistArray playlistarray;
    
                // Get the array of playlists from the  
                // media player.
                playlistarray = control.MediaPlayer.
                 playlistCollection.getAll();
    
                // Iterate through the array.
                Int32 i, count;
                count = playlistarray.count;
                if (count > 0)
                {
                    for (i = 0; i < count - 1; i+)
                    {
                        aplaylist = playlistarray.Item(i);
                        if (aplaylist.name == comboValue)
                        {
                            playlist = aplaylist;
                        }
                    }
                }
            }
            else if (output != null)
            {
             Marshal.GetNativeVariantForObject(
                 comboValue, output);
            }
        }
    }
    
    private void ComboListHandler(object sender, 
        EventArgs arguments)
    {
        var eventArgs = arguments as OleMenuCmdEventArgs;
        if (eventArgs != null)
        {
            IntPtr output = eventArgs.OutValue;
            if (output != null)
            {
                // Declare the playlist and  
                // playlistarray variables.
                WMPLib.IWMPPlaylist aplaylist;
                WMPLib.IWMPPlaylistArray playlistarray;
    
                // Get the array of playlists from the  
                // media player.
                playlistarray = control.MediaPlayer.
                    playlistCollection.getAll();
    
                // Iterate through the array
                Int32 i, count;
                count = playlistarray.count;
                if (count > 0)
                {
                    string[] names = new string[count];
                    for (i = 0; i < count - 1; i+)
                    {
                        aplaylist = playlistarray.Item(i);
                        names[i] = aplaylist.name;
                    }
                    Marshal.GetNativeVariantForObject(
                        names, output);
                }
            }
        }
    }
    

    ComboHandler executes when the user selects an item in the combo box, or when the user types something in the combo box and presses ENTER. The code caches the string that was in the combo box, and then checks whether the string matches a name in the list of playlists. If it matches, it caches the matching playlist.

    ComboListHandler executes when the user clicks the arrow in the combo box and populates the drop-down list with the current playlist.

  4. Replace the existing ButtonHandler method by using the following code.

    private void ButtonHandler(object sender, 
        EventArgs arguments)
    {
        if (playlist != null)
        {
            control.MediaPlayer.currentPlaylist = playlist;
        }
        else
        {
            if (comboValue != null)
            {
                LoadFile(comboValue);
            }
        }
    }
    

    This code replaces the more basic button-handler code that was added in Tutorial 3.

Now you can try the project in the experimental build by doing the following things:

  • Select a playlist in the drop-down list and then click the button.

  • Type a full path and file name in the combo box, press ENTER, and then click the button.

  • Click the WindowsMedia menu command to use the Open File dialog box.

What's Next

This tutorial shows how to add menu commands that have dynamic visibility. It also shows how to add controls to a tool window that interact with other controls in the tool window. These concepts apply in general to tool windows. The controls that you add to tool windows closely resemble the user controls that you would use in a Windows Forms application.

In the next tutorial, VSPackage Tutorial 4: How to Integrate into the Properties Window, Task List, Output Window, and Options Dialog Box, you can learn how to integrate your tool window together with the existing tool windows in Visual Studio, such as the Properties window, the Output window, and the Task List window.

See Also

Other Resources

Visual Studio Integration SDK