Creating multiple tool windows

This example demonstrates how to create multiple tool windows having the same functionality and instance-dependent command handlers. In Visual Studio the Find Result 1 and Find Result 2 windows are such kind of tool windows. Their behavior is exactly the same, but they use separate toolbars and commands on the toolbar.

Package source code:

using System.Runtime.InteropServices;
using Microsoft.VisualStudio.Shell;
using VSXtra;

namespace DeepDiver.MultiToolWindow
{
  [PackageRegistration(UseManagedResourcesOnly = true)]
  [DefaultRegistryRoot("Software\\Microsoft\\VisualStudio\\9.0")]
  [InstalledProductRegistration(false, "#110", "#112", "1.0", IconResourceID = 400)]
  [ProvideLoadKey("Standard", "1.0", "MultiToolWindow", "DeepDiver", 1)]
  [ProvideMenuResource(1000, 1)]
  [XtraProvideToolWindow(typeof(NumberStackWindowPane1))]
  [XtraProvideToolWindow(typeof(NumberStackWindowPane2))]
  [Guid(GuidList.guidMultiToolWindowPkgString)]
  public sealed class MultiToolWindowPackage : PackageBase
  {
  }
}
Command handlers for displaying the tool windows and defining the tool window toolbars:

using System.Runtime.InteropServices;
using VSXtra;

namespace DeepDiver.MultiToolWindow
{
  [Guid(GuidList.guidMultiToolWindowCmdSetString)]
  public sealed class CommandGroup: CommandGroup<MultiToolWindowPackage>
  {
    [CommandId(CmdIDs.cmdidShowFirstWindow)]
    [ShowToolWindowAction(typeof(NumberStackWindowPane1))]
    public sealed class ShowStackWindow1: MenuCommandHandler {}

    [CommandId(CmdIDs.cmdidShowSecondWindow)]
    [ShowToolWindowAction(typeof(NumberStackWindowPane2))]
    public sealed class ShowStackWindow2 : MenuCommandHandler { }

    [CommandId(CmdIDs.cmdidStackWindowToolbar1)]
    public sealed class StackWindowToolbar1 : ToolbarDefinition { }

    [CommandId(CmdIDs.cmdidStackWindowToolbar2)]
    public sealed class StackWindowToolbar2 : ToolbarDefinition { }
  }
}
Tool window UI code:

using System;
using System.Windows.Forms;
using VSXtra;

namespace DeepDiver.MultiToolWindow
{
  public partial class NumberStackControl : UserControl
  {
    public NumberStackControl()
    {
      InitializeComponent();
    }

    private void TypedTextEdit_TextChanged(object sender, EventArgs e)
    {
      PushToStack.Enabled = TypedTextEdit.Text.Trim().Length > 0;
    }

    private void PushToStack_Click(object sender, EventArgs e)
    {
      PushText(TypedTextEdit.Text);
    }

    public bool HasAtLeastTwoOperands
    {
      get { return NumberStack.Items.Count > 1; }
    }

    public bool HasAtLeastOneOperand
    {
      get { return NumberStack.Items.Count > 0; }
    }

    public void PushText(string text)
    {
      string toPush = text.Trim();
      int number;
      if (Int32.TryParse(toPush, out number))
      {
        NumberStack.Items.Insert(0, number);
      }
      else
      {
        TextStack.Items.Insert(0, toPush);
      }
      VsUIShell.UpdateCommandUI();
    }

    public int PopValue()
    {
      if (NumberStack.Items.Count == 0)
        throw new InvalidOperationException("Number stack is empty.");
      var result = (int)NumberStack.Items[0];
      NumberStack.Items.RemoveAt(0);
      return result;
    }

    public int PeekValue()
    {
      if (NumberStack.Items.Count == 0)
        throw new InvalidOperationException("Number stack is empty.");
      return (int)NumberStack.Items[0];
    }

    public delegate int BinaryOperator(int x, int y);

    public void Operation(BinaryOperator oper)
    {
      var y = PopValue();
      var x = PopValue();
      try
      {
        PushText(oper(x, y).ToString());
      }
      catch (SystemException ex)
      {
        PushText(String.Format("*** {0} ***", ex.Message));
      }
    }
  }
}
Tool window pane declarations:

using System.Runtime.InteropServices;
using System.Windows.Forms;
using Microsoft.VisualStudio;
using Microsoft.VisualStudio.Shell;
using VSXtra;

namespace DeepDiver.MultiToolWindow
{
  public abstract class NumberStackWindowPane: ToolWindowPane<MultiToolWindowPackage, NumberStackControl>
  {
    [CommandStatusMethod]
    [PromoteCommand]
    [CommandId(CmdIDs.cmdidAdd1)]
    [CommandId(CmdIDs.cmdidSubtract1)]
    [CommandId(CmdIDs.cmdidMultiply1)]
    [CommandId(CmdIDs.cmdidDivide1)]
    protected void OperationStatus(OleMenuCommand command)
    {
      command.Enabled = UIControl.HasAtLeastTwoOperands;
    }

    [CommandExecMethod]
    [CommandId(CmdIDs.cmdidAdd1)]
    protected void AddExec(OleMenuCommand command)
    {
      UIControl.Operation((x, y) => { checked { return x + y; } });
    }

    [CommandExecMethod]
    [CommandId(CmdIDs.cmdidSubtract1)]
    protected void SubtractExec(OleMenuCommand command)
    {
      UIControl.Operation((x, y) => { checked { return x - y; } });
    }

    [CommandExecMethod]
    [CommandId(CmdIDs.cmdidMultiply1)]
    protected void MultiplyExec(OleMenuCommand command)
    {
      UIControl.Operation((x, y) => { checked { return x * y; } });
    }

    [CommandExecMethod]
    [CommandId(CmdIDs.cmdidDivide1)]
    protected void DivideExec(OleMenuCommand command)
    {
      UIControl.Operation((x, y) => { checked { return x / y; } });
    }

    [CommandStatusMethod]
    [VsCommandId(VSConstants.VSStd97CmdID.Cut)]
    [VsCommandId(VSConstants.VSStd97CmdID.Copy)]
    protected void CutCopyStatus(OleMenuCommand command)
    {
      command.Enabled = UIControl.HasAtLeastOneOperand;
    }

    [CommandStatusMethod]
    [VsCommandId(VSConstants.VSStd97CmdID.Paste)]
    protected void PasteStatus(OleMenuCommand command)
    {
      command.Enabled = Clipboard.ContainsText();
    }

    [CommandExecMethod]
    [VsCommandId(VSConstants.VSStd97CmdID.Cut)]
    protected void CutExec(OleMenuCommand command)
    {
      int value = UIControl.PopValue();
      Clipboard.SetText(value.ToString());
    }

    [CommandExecMethod]
    [VsCommandId(VSConstants.VSStd97CmdID.Copy)]
    protected void CopyExec(OleMenuCommand command)
    {
      int value = UIControl.PeekValue();
      Clipboard.SetText(value.ToString());
    }

    [CommandExecMethod]
    [VsCommandId(VSConstants.VSStd97CmdID.Paste)]
    protected void PasteExec(OleMenuCommand command)
    {
      UIControl.PushText(Clipboard.GetText());
    }
  }

  [Guid("B097B9C4-D98C-45fe-B355-DE4865E77DCF")]
  [InitialCaption("Stack Window #1")]
  [BitmapResourceId(300, 1)]
  [ToolbarLocation(ToolbarLocation.Right)]
  [Toolbar(typeof(CommandGroup.StackWindowToolbar1))]
  public sealed class NumberStackWindowPane1 : NumberStackWindowPane
  {
  }

  [Guid("7B5DC947-D280-4255-B525-3AA62AAA52D4")]
  [InitialCaption("Stack Window #2")]
  [BitmapResourceId(300, 2)]
  [ToolbarLocation(ToolbarLocation.Right)]
  [Toolbar(typeof(CommandGroup.StackWindowToolbar2))]
  [CommandMap(CmdIDs.cmdidAdd1, CmdIDs.cmdidDivide1, (int)(CmdIDs.cmdidAdd2 - CmdIDs.cmdidAdd1))]
  public sealed class NumberStackWindowPane2 : NumberStackWindowPane
  {
  }
}
Screenshots:

MultiToolWindow1.png

Last edited Aug 5, 2008 at 8:00 AM by INovak, version 3

Comments

No comments yet.