演练:集成 " 属性 " 窗口中,任务列表, " 输出 " 窗口和 " 选项 " 对话框 (第 4 部分的 4)

使用 Visual Studio SDK,可以启用代码访问在 Visual Studio的所有工具窗口。 例如,可以将项添加到 任务列表,将文本添加到 输出 窗口或集成该扩展 属性 窗口,以便用户可以通过设置属性配置该扩展。 本演练演示如何集成到一个扩展中 Visual Studio的工具窗口。

通过完成本演练,您可以了解如何执行以下操作:

  • 使用包模板,创建 VSPackage。

  • 实现生成的工具窗口。

  • 实现菜单命令处理程序。

  • 创建选项页。

  • 使数据可用于 属性 窗口。

  • 集成 " 属性 " 窗口。

  • 将文本添加到 输出 窗口和项目添加到 任务列表

本演练是指导如何扩展 Visual Studio IDE 系列的一部分。 有关更多信息,请参见 自定义的 Visual Studio 演练使用 Vspackage

系统必备

若要完成本演练,您必须安装 Visual Studio 2010 SDK。

备注

有关 Visual Studio SDK 的更多信息,请参见 扩展 Visual Studio 概述。若要查找有关中所列如何下载 Visual Studio SDK,请 Visual Studio Extensibility Developer Center 参见 MSDN 网站上。

Visual Studio 包 " 项目模板的位置

Visual Studio 包 " 项目模板可以在 新项目 对话框的三个不同位置找到:

  1. 在 Visual Basic 扩展性下。 该项的默认语言是 Visual Basic。

  2. 在 C# 扩展性下。 该项目的默认语言是 C#。

  3. 在其他项下键入扩展性。 该项的默认语言是 C++。

使用 Visual Studio 包模板,创建 VSPackage

创建 VSPackage

  1. 创建 VSPackage。 有关如何创建 VSPackage 的更多信息,请参见 演练:使用 Visual Studio 创建包模板的菜单命令

  2. 将项目命名为 TodoList,将语言对于 Visual C# 或 Visual Basic 和 选择 VSPackage 选项 在页上,选择 菜单命令工具窗口

  3. 命令选项 页上,将 命令名 到 Todo 管理器 和 命令 ID 到 cmdidTodoCommand。

  4. 工具窗口 " 选项卡 页上,将 窗口名称 到 Todo 管理器 和 命令 ID 到 cmdidTodoTool。

  5. 单击**“完成”**按钮。

实现生成的工具窗口

在用户控件的窗体中,包模板生成了一个基本的工具窗口。 但是,它没有函数。 若要为其功能,您必须将子控件和修改。 MyControl.xaml.cs 或 MyControl.vb 的代码。

工具窗口将包括键入新 ToDo 项目、 Button 将新项目添加到列表和 ListBox 显示列表中的项 TextBox 。 完成工具窗口应类似于下图类似:

已完成的工具窗口

将控件添加到工具窗口

  1. 从网格中删除按钮、文本和 StackPanel 控件。

    备注

    这不会删除 button1_Click 事件处理程序中,您将在后面的步骤中重用。

  2. 工具箱所有 WPF 控件 部分,将 canvas 控件对网格。

  3. 将文本框控件、按钮控件和 ListBox 控件绑定到画布。 使它们在上面该图中。

  4. 选择按钮。 将其 内容 属性设置为 添加。

  5. 在 XAML 窗格中,请重新连接按钮事件处理程序与按钮控件通过添加 Click= " button1_Click”属性。 XAML 发生的行应如下所示:

    Public _parent As MyToolWindow
    Public Sub New(ByVal parent As MyToolWindow)
        InitializeComponent()
        _parent = parent
    End Sub
    
    <Button Content="Add" Height="21" Name="button1" Width="50" Canvas.Left="345" Canvas.Top="6" Click="button1_Click" />
    

    保存您的工作。

默认情况下,在 MyControl.xaml.cs 或 MyControl.xaml.vb 文件的用户控件构造函数不采用参数。 但是,您可以自定义构造函数包括参数,以便您可以保存父供以后使用。

自定义构造函数

  1. 从设计器页,右击 查看代码

  2. 用下面的代码替换现有的构造函数:

    public MyToolWindow _parent;
    public MyControl(MyToolWindow parent)
    {
        InitializeComponent();
        _parent = parent;
    }
    

    这使构造函数采用类型 MyToolWindow的参数。

  3. 保存您的工作。

  4. 现在,为参数添加到调用构造函数的代码。

    解决方案资源管理器,打开 MyToolWindow.cs 或 MyToolWindow.vb。

  5. 查找在类似于以下代码的 MyToolWindow 构造函数的行。

    Me.Content = New MyControl()
    
    base.Content = new MyControl();
    
  6. 如下所述更改行。

    Me.Content = New MyControl(Me)
    
    base.Content = new MyControl(this);
    

    执行此操作的工具窗口的实例添加到用户控件。 (这在后面的步骤要求创建 ToDoItem 类的构造函数。)

实现菜单命令处理程序

当 TodoList 项目中已创建,它包含了菜单项的默认处理程序。 处理程序是在 TodoListPackage 文件。 现在,请将代码添加到处理程序显示工具窗口。 ,因为 TodoListPackage 已包含名为 ShowToolWindow,的函数可以执行此操作有两个步骤。

实现菜单项处理程序

  1. 打开 TodoListPackage.cs 或 TodoListPackage.vb。 请注意菜单项处理程序包含下面的代码示例。

    Private Sub MenuItemCallback(ByVal sender As Object, ByVal e As EventArgs)
        ' Show a Message Box to prove we were here 
        Dim uiShell As IVsUIShell = TryCast(GetService(GetType(SVsUIShell)), IVsUIShell)
        Dim clsid As Guid = Guid.Empty
        Dim result As Integer
        Microsoft.VisualStudio.ErrorHandler.ThrowOnFailure(uiShell.ShowMessageBox(0, clsid, "TodoList", String.Format(CultureInfo.CurrentCulture, "Inside {0}.MenuItemCallback()", Me.GetType().Name), String.Empty, 0, OLEMSGBUTTON.OLEMSGBUTTON_OK, OLEMSGDEFBUTTON.OLEMSGDEFBUTTON_FIRST, OLEMSGICON.OLEMSGICON_INFO, 0, result))
    End Sub
    
    private void MenuItemCallback(object sender, EventArgs e)
    {
        // Show a Message Box to prove we were here
        IVsUIShell uiShell = (IVsUIShell)GetService(typeof(SVsUIShell));
        Guid clsid = Guid.Empty;
        int result;
        Microsoft.VisualStudio.ErrorHandler.ThrowOnFailure(uiShell.ShowMessageBox(
                   0,
                   ref clsid,
                   "TodoList",
                   string.Format(CultureInfo.CurrentCulture, "Inside {0}.MenuItemCallback()", this.ToString()),
                   string.Empty,
                   0,
                   OLEMSGBUTTON.OLEMSGBUTTON_OK,
                   OLEMSGDEFBUTTON.OLEMSGDEFBUTTON_FIRST,
                   OLEMSGICON.OLEMSGICON_INFO,
                   0,        // false 
                   out result));
    }
    
  2. 移除所有内容在函数中,并通过调用如下替换为 ShowToolWindow 。

    Private Sub MenuItemCallback(ByVal sender As Object, ByVal e As EventArgs)
        ShowToolWindow(sender, e)
    End Sub
    
    private void MenuItemCallback(object sender, EventArgs e)
    {
        ShowToolWindow(sender, e);
    }        
    

    保存该工作,然后按 F5 以生成项目并打开在 Visual Studio 的实验性生成。 测试工具窗口是否通过单击 ToDo 管理器 打开在 工具 菜单。

    ,在继续操作之前,请关闭实验生成。

创建选项卡页

可以提供在 选项 对话框的页,以便用户可以更改工具窗口的设置。 创建选项页需要在 TodoListPackage.cs 或 TodoListPackage.vb 文件介绍的选项和项的类。

创建选项卡页

  1. 解决方案资源管理器,右击 ToDoList 项目,指向 添加,然后单击

  2. 添加新项目 对话框中,将文件命名为 ToolsOptions.cs或 ToolsOptions.vb 然后单击 添加

    Visual Studio 创建此文件名为的 ToolsOptions 类,但是,您必须修改类头,以便该类从 DialogPage派生。

    添加 Microsoft.VisualStudio.Shell 命名空间到现有 using/imports 指令,如下所示。

    Imports Microsoft.VisualStudio.Shell
    
    using Microsoft.VisualStudio.Shell;
    
  3. 修改 ToolsOptions 类声明从 DialogPage继承。

    Inherits DialogPage
    
    class ToolsOptions : DialogPage
    
  4. 在本演练中的选项卡页只提供可选名为 DaysAhead。 若要添加此选项,添加以下命名 DaysAhead 的属性。 ToolsOptions 类。

    Private _daysAhead As Double 
    
    Public Property DaysAhead() As Double 
        Get 
            Return _daysAhead
        End Get 
        Set(ByVal value As Double)
            _daysAhead = value
        End Set 
    End Property
    
    private double _daysAhead;
    
    public double DaysAhead
    {
        get { return _daysAhead; }
        set { _daysAhead = value; }
    }
    

    此类存储单个选项作为私有成员名为 _daysAhead。 类然后提供名为 DaysAhead 的公共属性访问这些选项。

  5. 保存该文件。

现在必须使该项目识别该选项页,以便将提供正确注册和用户。

若要使选项页可供用户

  1. 解决方案资源管理器,打开 TodoListPackage.cs 或 TodoListPackage.vb。

  2. 找到包含 ProvideToolWindowAttribute 属性的行,然后将在其后的一 ProvideOptionPageAttribute 属性,如下所示。

    <PackageRegistration(UseManagedResourcesOnly:=True), _
    InstalledProductRegistration("#110", "#112", "1.0", IconResourceID:=400), _
    ProvideMenuResource("Menus.ctmenu", 1), _
    ProvideToolWindow(GetType(MyToolWindow)), _
    ProvideOptionPage(GetType(ToolsOptions), "To-Do", "General", 101, 106, True), _
    Guid(GuidList.guidTodoListPkgString)> _
    Public NotInheritable Class TodoListPackage
        Inherits Package
    
    [ProvideToolWindow(typeof(MyToolWindow))]
    [ProvideOptionPage(typeof(ToolsOptions), "To-Do", "General", 101, 106, true)]
    

    备注

    您不必包含单词 “在属性声明的 Attribute。

  3. 保存该文件。

    为 ProvideOptionPage 构造函数的第一个参数是类 ToolsOptions 的类型,先前创建的。 第二个参数, “骚乱”,是类的名称。 选项 对话框中。 第三个参数, “常规”,是 选项 对话框的子类别的名称选项卡页将可用。 下面两个参数为字符串的资源 ID;第一种类别的名称,并且,第二个元素是子类别的名称。 最后一个参数将此页使用自动化,是否可以访问。

    在选项页访问时,它应类似于以下图片。

    “选项”页

    通知类别骚乱和子类别 " 常规 "。

使数据可供 " 属性 " 窗口

由好面向对象模型的以下原则,可以创建名为在存储有关各个项的信息来对内容的 ToDoItem 的类。

使数据可在 " 属性 " 窗口

  1. 解决方案资源管理器,右击 ToDoList 项目,指向 添加,然后单击

  2. 添加新项目 对话框中,将文件命名为 ToDoItem.cs 或 ToDoItem.vb,然后单击 添加

    当工具窗口可供用户使用,在列表框中的项目将被 ToDoItem 实例表示的。 当用户选择在列表框中的其中一项, 属性 窗口将显示有关项目的信息。

    通过使用两个特定属性、说明和类别,若要使数据可在 属性 窗口,使数据转换为类的公共属性然后文档它们。 声明是出现在 属性 窗口底部的文本。 类别定义属性应出现的位置 属性 窗口在 " 分类显示 " 视图中突出显示。 在图, 属性 窗口在 " 分类显示 " 视图中,在 骚乱字段 类别的 名称 选择属性时,因此, 名称 属性声明显示在窗口的底部。

    “属性”窗口

  3. 添加以下命名空间。 ToDoItem.cs 或 ToDoItem.vb 文件的顶部,,在现有 using/imports 语句之后。

    Imports System.ComponentModel
    Imports System.Windows.Forms
    Imports Microsoft.VisualStudio.Shell.Interop
    
    using System.ComponentModel;
    using System.Windows.Forms;
    using Microsoft.VisualStudio.Shell.Interop;
    
  4. 按如下方式实现 ToDoItem 类的开始。 确保添加 public 访问修饰符到类声明。

    Private _name As String
    <Description("Name of the To-Do item")> _
    <Category("To-Do Fields")> _
    Public Property Name() As String 
        Get 
            Return _name
        End Get 
        Set(ByVal value As String)
            _name = value
            _parent.UpdateList(Me)
        End Set 
    End Property 
    
    Private _dueDate As Date
    <Description("Due date of the To-Do item")> _
    <Category("To-Do Fields")> _
    Public Property DueDate() As Date 
        Get 
            Return _dueDate
        End Get 
        Set(ByVal value As Date)
            _dueDate = value
            _parent.UpdateList(Me)
            _parent.CheckForErrors()
        End Set 
    End Property
    
    public class ToDoItem
    {
        private string _name;
        [Description("Name of the To-Do item")]
        [Category("To-Do Fields")]
        public string Name
        {
            get { return _name; }
            set
            {
                _name = value;
                _parent.UpdateList(this);
            }
        }
    
        private DateTime _dueDate;
        [Description("Due date of the To-Do item")]
        [Category("To-Do Fields")]
        public DateTime DueDate
        {
            get { return _dueDate; }
            set
            {
                _dueDate = value;
                _parent.UpdateList(this);
                _parent.CheckForErrors();
            }
        }
    }
    

    请注意此代码具有两个属性、名称和 DueDate。 如上一个图所示,这些是将出现在 属性 窗口中的两个属性,。 每个属性在声明和类别特性后,在 属性 窗口中显示提供信息。 检查名称属性的这两个属性;字符串应与上图中显示图片。

  5. 添加下面的构造函数在类的顶部。

    Private _parent As MyControl
    Public Sub New(ByVal parent As MyControl, ByVal name As String)
        _parent = parent
        _name = name
        _dueDate = Date.Now
    
        Dim daysAhead As Double = 0
        Dim package As IVsPackage = TryCast(_parent._parent.Package, IVsPackage)
        If package IsNot Nothing Then 
            Dim obj As Object
            package.GetAutomationObject("To-Do.General", obj)
    
            Dim options As ToolsOptions = TryCast(obj, ToolsOptions)
            If options IsNot Nothing Then
                daysAhead = options.DaysAhead
            End If 
        End If
    
        _dueDate = _dueDate.AddDays(daysAhead)
    End Sub
    
    private MyControl _parent;
    public ToDoItem(MyControl parent, string name)
    {
        _parent = parent;
        _name = name;
        _dueDate = DateTime.Now;
    
        double daysAhead = 0;
        IVsPackage package = _parent._parent.Package as IVsPackage;
        if (package != null)
        {
            object obj;
            package.GetAutomationObject("To-Do.General", out obj);
    
            ToolsOptions options = obj as ToolsOptions;
            if (options != null)
            {
                daysAhead = options.DaysAhead;
            }
        }
    
        _dueDate = _dueDate.AddDays(daysAhead);
    }
    

    首先,此代码声明一个私有成员授予 _parent,对应于用户控件包含文本框、按钮和 ListBox 控件之前创建。 构造函数获取用户作为参数,与名称此 ToDo 项的字符串。 在构造函数中的前三行保存用户控件、名称和当前日期和时间。

    您可以根据启用您先前创建的选项卡页的 DaysAhead 选项使用当前的日期和时间。 由于当前日期和时间通常不用作到期日期,可以在选项卡页指定的天数提升当前日期。 .

    代码声明使用在 DaysAhead 选项,的值设置的局部变量调用 daysAhead 。 下一行获取用户控件的父级和因此,包成员。 (这是您使用添加到 MyControl.xaml.cs 类之前) 的 _parent 成员的位置

    如果此包成员不为空,将保存 ToolsOptions 实例的对象声明。 获取实例,代码调用包的 GetAutomationObject 成员并将该类别和子类别的名称,一个点分隔的字符串,用于 Do.General。 结果作为输出参数返回到 obj 变量。

    obj 变量在名为 options的变量然后转换为 ToolsOptions 类并保存。 如果此变量不为空,代码获取 DaysAhead 成员并将其保存到 _daysAhead 变量。

    转发使用 AddDays 方法中,代码通过+几+天数然后提升 _duedate 变量。

  6. 由于 ToDoItem 类的实例在列表框中将存储,并且 ListBox 将调用此类从基 Object 类继承检索该字符串为项显示的 ToString 功能,必须重载 ToString 功能。

    将以下代码添加到 ToDoItem.cs,构造函数之后但在类结束之前。

    Public Overloads Overrides Function ToString() As String 
        Return (_name & " Due: ") + _dueDate.ToShortDateString()
    End Function
    
    public override string ToString()
    {
        return _name + " Due: " + _dueDate.ToShortDateString();
    }
    
  7. 打开 MyControl.xaml.cs 或 MyControl.xaml.vb。

  8. 存根方法添加到 CheckForError 和 UpdateList 方法的 MyControl 类。 置于 ProcessDialogChar 之后且在文件的末尾。

    Public Sub CheckForErrors()
    
    End Sub 
    
    Public Sub UpdateList(ByVal item As ToDoItem)
    
    End Sub
    Public Sub CheckForErrors()
    
    End Sub 
    
    Public Sub UpdateList(ByVal item As ToDoItem)
    
    End Sub
    
    public void CheckForErrors()
    {
    }
    public void UpdateList(ToDoItem item)
    {
    }
    

    CheckForError 调用方法具有相同的名称在父对象,并且,方法检查的方法任何错误是否正确进行和处理这些事件。 UpdateList 方法将更新父控件的 ListBox;,在此类中的 Name 和 DueDate 属性更改时,将调用方法。 您将执行在后面的步骤将这些方法。

集成 " 属性 " 窗口

现在编写管理 ListBox,将附加到 属性 窗口的代码。

必须将句柄读取文本框中,创建 ToDoItem 实例,并向 sharepoint 添加到列表框的按钮。

与集成 " 属性 " 窗口

  1. 为 MyControl.xaml 切换到 " 设计 " 视图,然后双击按钮控件

  2. ,使用以下代码替换现有 button1_Click 处理程序函数。

    Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.Windows.RoutedEventArgs) Handles Button1.Click
        If TextBox1.Text.Length > 0 Then 
            Dim item = New ToDoItem(Me, TextBox1.Text)
            ListBox1.Items.Add(item)
            TrackSelection()
            CheckForErrors()
        End If 
    End Sub
    
    [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Globalization", "CA1300:SpecifyMessageBoxOptions")]
    private void button1_Click(object sender, EventArgs e)
    {
        if (textBox1.Text.Length > 0)
        {
            var item = new ToDoItem(this, textBox1.Text);
            listBox1.Items.Add(item);
            TrackSelection();
            CheckForErrors();
        }
    }
    

    此代码创建新 ToDoItem 实例并通过用户控件实例作为参数与用户在文本框控件中输入的文本。 然后,代码将项目添加到列表框。 (ListBox 在列表框中将调用 ToDoItem 实例的 ToString 方法检索该字符串显示在中。)然后,代码调用 TrackSelection 功能,您将在后面的步骤中编写。 最后,错误的代码检查。

  3. 添加到代码的 MyControl.xaml 切换到 " 设计 " 视图新项目的处理用户选择在列表框中的。

  4. 单击 ListBox 控件。 在 属性 窗口中,双击 SelectionChanged 事件。 执行此操作添加 SelectionChanged 处理程序的存根并将其分配给事件。

  5. 按如下方式填充 SelectionChanged 在调用的方法的处理程序和存根。

    Private Sub ListBox1_SelectionChanged(ByVal sender As System.Object, ByVal e As System.Windows.Controls.SelectionChangedEventArgs) Handles ListBox1.SelectionChanged
        TrackSelection()
    End Sub 
    
    Private Sub TrackSelection()
    
    End Sub
    
    private void listBox1_SelectionChanged(object sender, EventArgs e)
    {
        TrackSelection();
    }
    private void TrackSelection()
    {
    }
    
  6. 保存您的工作。 可以生成项目并查找排印错误。

  7. 现在,请填写 TrackSelection 功能,则将提供与的 属性 窗口。 ,当用户向列表框或单击在列表框中时,的项目此函数调用。

    既然有 属性 窗口可以使用的类,您可以将具有以下工具窗口的 属性 窗口。 当用户单击在列表框中的项在工具窗口时,应相应地更新 属性 窗口。 同样,,当用户更改在 属性 窗口中的某个 ToDo 项目,该关联的项应更新。

    备注

    或者,可以直接通过实现接口 INotifyPropertyChanged 生成 PropertyChanged 事件。

    将更新的 属性 windows 代码在 TrackSelection 功能。 执行此操作 ToDoItem 对象附加到 属性 windows;,当用户更改在 属性 窗口一个值时,您不必编写修改 ToDoItem 的任何其他代码。 属性 窗口自动调用 set 属性访问器更新的值。 但是,必须为您完成创建的 UpdateList 方法时要为 ToDoItem 类编写代码。

  8. 以下命名空间声明添加到 MyControl.xaml.cs 或 MyControl.vb 文件的顶部,,在现有 using/imports 语句之后。

    Imports System
    Imports System.Runtime.InteropServices
    Imports Microsoft.VisualStudio.Shell.Interop
    Imports Microsoft.VisualStudio
    Imports Microsoft.VisualStudio.Shell
    
    using System.Runtime.InteropServices;
    using Microsoft.VisualStudio.Shell.Interop;
    using Microsoft.VisualStudio;
    using Microsoft.VisualStudio.Shell;
    
  9. 按如下方式实现 TrackSelection 功能。

    Private mySelContainer As SelectionContainer
    Private mySelItems As System.Collections.ArrayList
    Private frame As IVsWindowFrame = Nothing 
    Private Sub TrackSelection()
        If frame Is Nothing Then 
            Dim shell = TryCast(GetService(GetType(SVsUIShell)), IVsUIShell)
            If shell IsNot Nothing Then 
                Dim guidPropertyBrowser = New Guid(ToolWindowGuids.PropertyBrowser)
                shell.FindToolWindow(CUInt(__VSFINDTOOLWIN.FTW_fForceCreate), guidPropertyBrowser, frame)
            End If 
        End If 
        If frame IsNot Nothing Then
            frame.Show()
        End If 
        If mySelContainer Is Nothing Then
            mySelContainer = New SelectionContainer()
        End If
    
        mySelItems = New System.Collections.ArrayList()
    
        Dim selected = TryCast(listBox1.SelectedItem, ToDoItem)
        If selected IsNot Nothing Then
            mySelItems.Add(selected)
        End If
    
        mySelContainer.SelectedObjects = mySelItems
    
        Dim track = TryCast(GetService(GetType(STrackSelection)), ITrackSelection)
        If track IsNot Nothing Then
            track.OnSelectChange(mySelContainer)
        End If 
    End Sub
    
    private SelectionContainer mySelContainer;
    private System.Collections.ArrayList mySelItems;
    private IVsWindowFrame frame = null;
    
    private void TrackSelection()
    {
        if (frame == null)
        {
            var shell = GetService(typeof(SVsUIShell)) as IVsUIShell;
            if (shell != null)
            {
                var guidPropertyBrowser = new
                    Guid(ToolWindowGuids.PropertyBrowser);
                shell.FindToolWindow((uint)__VSFINDTOOLWIN.FTW_fForceCreate,
                    ref guidPropertyBrowser, out frame);
            }
        }
        if (frame != null)
        {
            frame.Show();
        }
        if (mySelContainer == null)
        {
            mySelContainer = new SelectionContainer();
        }
    
        mySelItems = new System.Collections.ArrayList();
    
        var selected = listBox1.SelectedItem as ToDoItem;
        if (selected != null)
        {
            mySelItems.Add(selected);
        }
    
        mySelContainer.SelectedObjects = mySelItems;
    
        var track = GetService(typeof(STrackSelection))
            as ITrackSelection;
        if (track != null)
        {
            track.OnSelectChange(mySelContainer);
        }
    }        
    
  10. 将 TrackSelection 函数后的末尾的代码。

    Protected Function GetService(ByVal service As Type) As Object 
        Dim obj As Object = Nothing 
        If _parent IsNot Nothing Then
            obj = _parent.GetVsService(service)
        End If 
        Return obj
    End Function
    
    protected object GetService(Type service)
    {
        if (_parent != null)
        {
            return _parent.GetVsService(service);
        }
        return null;
    }
    

    此代码调用 GetService 功能。 此函数首先尝试通过调用其 GetService 函数从父工具窗口的服务。 如果该操作失败,将尝试获取它从对象的 GetService 功能。 由于在父工具窗口的 GetService 函数不是公共的,代码调用 GetVsService。 必须将 GetVsService 功能。

  11. 打开 MyToolWindow.cs 或 MyToolWindow.vb。 将以下代码添加到类的末尾,在文件之前结束。

    Friend Function GetVsService(ByVal service As Type) As Object 
        Return GetService(service)
    End Function
    
    internal object GetVsService(Type service)
    {
        return GetService(service);
    }
    
  12. 保存该文件。

    第一次 TrackSelection 功能运行时,将调用 GetService 获取 Visual Studio shell 的实例。 然后实例获取 属性 窗口的对象的使用。 获取 属性 窗口对象,该对象表示 属性 窗口的代码首先使用 GUID。 (工具窗口的 GUID 为 ToolWindowGuids80 类的成员。)代码通过 GUID 然后调用 shell 的 FindToolWindow 功能,,获取 属性 窗口对象。 这样做将其保存在框架变量,这样,当函数再次调用时,此过程获取 属性 窗口不必重复。

    接下来,该方法调用框架变量的显示方式显示 属性 窗口。

    代码收集在列表框中的选定的项。 未配置 ListBox 启用多重选择。 若要将选定项。 属性 窗口,则必须使用容器。 因此,代码在 ArrayList 在一个容器收集选定项并将其放入,然后将该 ArrayList 类型 SelectionContainer。

    然后,代码调用 GetService 获取 ITrackSelection 实例,是 Visual Studio 对象跟踪选择了用户界面的对象 (UI)并显示它们的属性。 然后代码直接调用 ITrackSelection OnSelectChange 事件处理程序,并通过保存选定项的 SelectionContainer。 结果为 属性 窗口显示选定项的属性。

    当用户更改在 属性 窗口中 ToDoItem 对象, 属性 窗口自动调用 ToDoItem 对象的 set 访问器函数。 该更新对象,但是,仍然必须更新 ListBox。

  13. 在早期的步骤中,您将 set 访问器函数的代码能够调用 MyControl.xaml.cs 或 MyControl.xaml.vb 的一个 UpdateList 功能。 现在,添加 UpdateList 函数代码的其余部分。

  14. 从 MyControl.xaml.cs 或 MyControl.xaml.vb,如下所示请执行 UpdateList 方法。

    Public Sub UpdateList(ByVal item As ToDoItem)
        Dim index As Integer = ListBox1.SelectedIndex
        listBox1.Items.RemoveAt(index)
        listBox1.Items.Insert(index, item)
        ListBox1.SelectedItem = index
    End Sub
    
    public void UpdateList(ToDoItem item)
    {
        var index = listBox1.SelectedIndex;
        listBox1.Items.RemoveAt(index);
        listBox1.Items.Insert(index, item);
        listBox1.SelectedItem = index;
    }
    

    此代码决定为项目选择和对应于修改的 ToDoItem。 代码从列表框中移除该项,然后重新插入。 执行此更新在列表框中的行该项的。 然后代码将选定内容添加到相同的项目。

  15. 保存您的工作。

将文本添加到 " 输出 " 窗口,并对的项目任务列表

若要添加字符串。 任务列表输出 窗口,必须首先获得引用这两个窗口的对象。 然后,可以调用对象的方法。 对于 任务列表,您创建类型的任务新的对象,然后通过调用添加到 任务列表 的该任务对象其添加方法。 写入 输出 窗口,可调用其 GetPane 方法获取窗格对象,然后调用窗格对象的 OutputString 方法。

若要将文本添加到输出窗口和任务列表

  1. 打开 MyControl.xaml.cs 或 MyControl.xaml.vb。

  2. 通过插入以下代码扩展 button1_Click 方法在调用之前对 TrackSelection()。

    Dim outputWindow = TryCast(GetService(GetType(SVsOutputWindow)), IVsOutputWindow)
    Dim pane As IVsOutputWindowPane
    Dim guidGeneralPane As Guid = VSConstants.GUID_OutWindowGeneralPane
    outputWindow.GetPane(guidGeneralPane, pane)
    If pane IsNot Nothing Then
        pane.OutputString(String.Format("To Do item created: {0} \r\n", item.ToString()))
    End If
    
    private void button1_Click(object sender, EventArgs e)
    {
        if (textBox1.Text.Length > 0)
        {
            var item = new ToDoItem(this, textBox1.Text);
            listBox1.Items.Add(item);
    
            //Insert this section------------------ 
            var outputWindow = GetService(
                typeof(SVsOutputWindow)) as IVsOutputWindow;
            IVsOutputWindowPane pane;
            Guid guidGeneralPane =
                VSConstants.GUID_OutWindowGeneralPane;
            outputWindow.GetPane(ref guidGeneralPane, out pane);
            if (pane != null)
            {
                pane.OutputString(string.Format(
                    "To Do item created: {0}\r\n",
                    item.ToString()));
            }
            //-------------------------------------
    
            TrackSelection();
            CheckForErrors();
        }
    

    此代码获取 输出 窗口中的对象。 对象公开 IVsOutputWindow 接口。 代码随后获取由 OutputString 功能,最终写入 输出 窗口中 IVsOutputWindowPane 对象。

  3. 现在即可执行 CheckForErrors 方法,如下所示。

    Public Sub CheckForErrors()
        For Each item As ToDoItem In ListBox1.Items
            If item.DueDate < DateTime.Now Then
                ReportError("To Do Item is out of date: " & item.ToString())
            End If 
        Next 
    End Sub
    
    public void CheckForErrors()
    {
        foreach (ToDoItem item in listBox1.Items)
        {
            if (item.DueDate < DateTime.Now)
            {
                ReportError("To Do Item is out of date: "
                    + item.ToString());
            }
        }
    }
    

    此代码与帮助将项添加到 任务列表的其他一些方法的调用 ReportError 方法,接下来将创建,。

  4. 将以下代码添加到类的末尾,这两个右大括号的前面。

    <Guid("72de1eAD-a00c-4f57-bff7-57edb162d0be")> _
    Public Class MyTaskProvider
        Inherits TaskProvider
        Public Sub New(ByVal sp As IServiceProvider)
            MyBase.New(sp)
        End Sub 
    End Class 
    Private _taskProvider As MyTaskProvider
    Private Sub CreateProvider()
        If _taskProvider Is Nothing Then
            _taskProvider = New MyTaskProvider(_parent)
            _taskProvider.ProviderName = "To Do" 
        End If 
    End Sub 
    Private Sub ClearError()
        CreateProvider()
        _taskProvider.Tasks.Clear()
    End Sub 
    Private Sub ReportError(ByVal p As String)
        CreateProvider()
        Dim errorTask = New Task()
        errorTask.CanDelete = False
        errorTask.Category = TaskCategory.Misc
        errorTask.Text = p
    
        _taskProvider.Tasks.Add(errorTask)
    
        _taskProvider.Show()
    
        Dim taskList = TryCast(GetService(GetType(SVsTaskList)), IVsTaskList2)
        If taskList Is Nothing Then 
            Exit Sub 
        End If 
    
        Dim guidProvider = GetType(MyTaskProvider).GUID
        taskList.SetActiveProvider(guidProvider)
    End Sub
    
    [Guid("72de1eAD-a00c-4f57-bff7-57edb162d0be")]
    public class MyTaskProvider : TaskProvider
    {
        public MyTaskProvider(IServiceProvider sp)
            : base(sp)
        {
        }
    }
    private MyTaskProvider _taskProvider;
    private void CreateProvider()
    {
        if (_taskProvider == null)
        {
            _taskProvider = new MyTaskProvider(_parent);
            _taskProvider.ProviderName = "To Do";
        }
    }
    private void ClearError()
    {
        CreateProvider();
        _taskProvider.Tasks.Clear();
    }
    private void ReportError(string p)
    {
        CreateProvider();
        var errorTask = new Task();
        errorTask.CanDelete = false;
        errorTask.Category = TaskCategory.Misc;
        errorTask.Text = p;
    
        _taskProvider.Tasks.Add(errorTask);
    
        _taskProvider.Show();
    
        var taskList = GetService(typeof(SVsTaskList))
            as IVsTaskList2;
        if (taskList == null)
        {
            return;
        }
    
        var guidProvider = typeof(MyTaskProvider).GUID;
        taskList.SetActiveProvider(ref guidProvider);
    }
    

    此代码的开头是一种特殊的 TaskProvider 类包含 GUID 的命名 MyTaskProvider。 接下来此新类类型的成员变量,后跟创建新实例的方法,以便在需要时。

    接下来是两个重要方法、 ClearError,清除现有任务项和 ReportError,将项添加到 任务列表

    ReportError 方法创建任务的新实例,初始化实例,然后添加例程。 任务列表。 ,当用户在中选择 ToDo 项的下拉列表中 任务列表项部时,新 任务列表 项才可见。 代码中的最后两行自动选择 ToDo 项目从下拉列表并将新任务项视图。 需要 GUID,当 TaskProvider 类继承时,因为 SetActiveProvider 方法要求 GUID 作为参数。

它尝试

测试扩展

  1. 按 CTRL+F5 打开 Visual Studio的实验性生成。

  2. 在实验性生成,在 工具 菜单上,单击 ToDo 管理器

    您设计的工具窗口将打开。

  3. 键入一些在文本框然后单击 添加

    您应该会看到该项目添加到列表框。

  4. 键入其他然后再次单击 添加

    当添加项,早期日期设置为当前日期和时间。 这将触发一个错误并项。 任务列表

  5. 视图 菜单上,单击打开 输出 窗口的 输出

    请注意在每次添加一个项目,消息。 任务列表 窗格中显示。

  6. 单击其中一个在列表框中的项。

    属性 窗口显示项目的两个属性。

  7. 更改其中一个属性然后按 enter。

    该项目在列表框中进行更新。

接下来的内容

在本演练中创建的与集成在 Visual Studio的另一个工具窗口的工具窗口。 Visual Studio 具有可以处理,并且,这些 GUID 可以在 ToolWindowGuids 类中的多个工具窗口。 还将创建了一个包含属性 属性 窗口访问的类。 您提供 属性 windows 使用的访问器函数。 在 set 访问器函数,则调用自己的代码对 属性 窗口使其进行处理。 执行此操作提供了一种双向通信结构。 最后,您将了解如何将项目添加到 任务列表,如何将项视图以及如何将文本添加到 输出 窗口。

请参见

概念

输出窗口 (Visual Studio SDK)

任务列表

其他资源

命令、菜单和工具栏

工具窗口

属性 " 窗口和 " 属性页