共用方式為


擴充屬性、工作清單、輸出和選項視窗

您可以在 Visual Studio 中存取任何工具視窗。 本逐步解說示範如何將工具視窗的相關資訊整合到新的 [選項] 頁面,以及 [屬性] 頁面上的新設定,以及如何寫入至 [工作清單][輸出] 視窗。

使用工具視窗建立擴充功能

  1. 使用 VSIX 範本建立名為 TodoList 的專案,並新增名為 TodoWindow 的自訂工具視窗項目範本。

    注意

    如需使用工具視窗建立擴充功能的詳細資訊,請參閱使用工具視窗建立擴充功能

設定工具視窗

新增要在其中輸入新 ToDo 專案的 TextBox、將新專案新增至清單的 Button,以及要顯示清單上專案的 ListBox。

  1. TodoWindow.xaml 中,從 UserControl 刪除 Button、TextBox 和 StackPanel 控制項。

    注意

    這不會刪除 button1_Click 事件處理常式,您會在稍後的步驟中重複使用該事件處理常式。

  2. [工具箱][所有 WPF 控制項] 驅動,將 [畫布] 控制項拖曳至窗格。

  3. TextBoxButtonListBox 拖曳至 [畫布]。 排列元素,讓 TextBox 和 Button 位於相同的層級,而 ListBox 會填滿其下方的其餘視窗,如下圖所示。

    Finished Tool Window

  4. 在 XAML 窗格中,尋找 Button,並將其 [內容] 屬性設定為 [新增]。 藉由新增 Click="button1_Click" 屬性,將按鈕事件處理常式重新連接到 Button 控制項。 [畫布] 區塊此時應顯示如下:

    <Canvas HorizontalAlignment="Left" Width="306">
        <TextBox x:Name="textBox" HorizontalAlignment="Left" Height="23" Margin="10,10,0,0" TextWrapping="Wrap" Text="TextBox" VerticalAlignment="Top" Width="208"/>
            <Button x:Name="button" Content="Add" HorizontalAlignment="Left" Margin="236,13,0,0" VerticalAlignment="Top" Width="48" Click="button1_Click"/>
            <ListBox x:Name="listBox" HorizontalAlignment="Left" Height="222" Margin="10,56,0,0" VerticalAlignment="Top" Width="274"/>
    </Canvas>
    

自訂建構函式

  1. TodoWindowControl.xaml.cs 檔案中,新增下列 using 指示詞:

    using System;
    
  2. 將公用參考新增至 TodoWindow,並讓 TodoWindowControl 建構函式採用 TodoWindow 參數。 程式碼看起來應該類似:

    public TodoWindow parent;
    
    public TodoWindowControl(TodoWindow window)
    {
        InitializeComponent();
        parent = window;
    }
    
  3. TodoWindow.cs 中,將 TodoWindowControl 建構函式變更為包含 TodoWindow 參數。 程式碼看起來應該類似:

    public TodoWindow() : base(null)
    {
        this.Caption = "TodoWindow";
        this.BitmapResourceID = 301;
        this.BitmapIndex = 1;
    
         this.Content = new TodoWindowControl(this);
    }
    

建立 [選項] 頁面

您可以在 [選項] 對話方塊中提供頁面,讓使用者可以變更工具視窗的設定。 建立 [選項] 頁面需要一個類別,描述 TodoListPackage.csTodoListPackage.vb 檔案中的選項和項目。

  1. 新增名為 ToolsOptions.cs 的類別。 使 ToolsOptions 類別繼承自 DialogPage

    class ToolsOptions : DialogPage
    {
    }
    
  2. 新增以下 using 指示詞。

    using Microsoft.VisualStudio.Shell;
    
  3. 本逐步解說中的 [選項] 頁面只提供一個名為 DaysAhead 的選項。 將名為 daysAhead 的私人欄位和名為 DaysAhead 的屬性新增至 ToolsOptions 類別:

    private double daysAhead;
    
    public double DaysAhead
    {
        get { return daysAhead; }
        set { daysAhead = value; }
    }
    

    現在,您必須讓專案知道這個 [選項] 頁面。

使 [選項] 頁面可供使用者使用

  1. TodoWindowPackage.cs 中,將 ProvideOptionPageAttribute 新增至 TodoWindowPackage 類別:

    [ProvideOptionPage(typeof(ToolsOptions), "ToDo", "General", 101, 106, true)]
    
  2. ProvideOptionPage 建構函式的第一個參數是您稍早建立的 ToolsOptions 類別類型。 第二個參數 「ToDo」 是 [選項] 對話方塊中的類別名稱。 第三個參數 「General」是 [選項] 對話方塊的子類別名稱,其中 [選項] 頁面可供使用。 接下來的兩個參數是字串的資源識別碼;第一個是類別的名稱,第二個是子類別的名稱。 最後一個參數會決定是否可以使用自動化來存取此頁面。

    當使用者開啟 [選項] 頁面時,它應該類似下圖。

    Options Page

    請注意 ToDo 類別和 General 子類別。

將資料提供給 [屬性] 視窗

您可以建立名為 TodoItem 的類別,將個別專案的相關資訊儲存在 ToDo 清單中,讓 ToDo 清單資訊可供使用。

  1. 新增名為 TodoItem.cs 的類別。

    當使用者可以使用工具視窗時,ListBox 中的專案將由 TodoItems 表示。 當使用者在 ListBox 中選取其中一個專案時,[屬性] 視窗會顯示專案的相關資訊。

    若要在 [屬性] 視窗中提供資料,您可以將資料轉換成具有兩個特殊屬性 (DescriptionCategory) 的公用屬性。 Description 是出現在 [屬性] 視窗底部的文字。 Category 會決定當 [屬性] 視窗顯示在 [分類] 檢視中的位置。 在下圖中,[屬性] 視窗位於 [分類] 檢視中,已選取 [待辦事項欄位] 類別中的 [名稱] 屬性,並在視窗底部顯示 [名稱] 屬性的描述。

    Properties Window

  2. 將下列 using 指示詞新增 TodoItem.cs 檔案。

    using System.ComponentModel;
    using System.Windows.Forms;
    using Microsoft.VisualStudio.Shell.Interop;
    
  3. public 存取修飾詞新增至類別宣告。

    public class TodoItem
    {
    }
    

    新增兩個屬性:NameDueDate。 我們稍後將執行 UpdateList() 和 CheckForErrors()

    public class TodoItem
    {
        private TodoWindowControl parent;
        private string name;
        [Description("Name of the ToDo item")]
        [Category("ToDo Fields")]
        public string Name
        {
            get { return name; }
            set
            {
                name = value;
                parent.UpdateList(this);
            }
        }
    
        private DateTime dueDate;
        [Description("Due date of the ToDo item")]
        [Category("ToDo Fields")]
        public DateTime DueDate
        {
            get { return dueDate; }
            set
            {
                dueDate = value;
                parent.UpdateList(this);
                parent.CheckForErrors();
            }
        }
    }
    
  4. 將私人參考新增至使用者控制項。 新增建構函式,此建構函式接受使用者控制項和此 ToDo 項目的名稱。 若要尋找 daysAhead 的值,它會取得 [選項] 頁面屬性。

    private TodoWindowControl parent;
    
    public TodoItem(TodoWindowControl control, string itemName)
    {
        parent = control;
        name = itemName;
        dueDate = DateTime.Now;
    
        double daysAhead = 0;
        IVsPackage package = parent.parent.Package as IVsPackage;
        if (package != null)
        {
            object obj;
            package.GetAutomationObject("ToDo.General", out obj);
    
            ToolsOptions options = obj as ToolsOptions;
            if (options != null)
            {
                daysAhead = options.DaysAhead;
            }
        }
    
        dueDate = dueDate.AddDays(daysAhead);
    }
    
  5. 因為 TodoItem 類別的執行個體會儲存在 ListBox 中,而且 ListBox 會呼叫 ToString 函式,因此您必須多載 ToString 函式。 在建構函式之後和類別結尾之前,將下列程式代碼新增至 TodoItem.cs

    public override string ToString()
    {
        return name + " Due: " + dueDate.ToShortDateString();
    }
    
  6. TodoWindowControl.xaml.cs 中,將虛設常式方法新增至 CheckForErrorUpdateList 方法的 TodoWindowControl 類別。 將它們放在 ProcessDialogChar 之後,並在檔案結尾之前。

    public void CheckForErrors()
    {
    }
    public void UpdateList(TodoItem item)
    {
    }
    

    CheckForError 方法會呼叫在上層物件中具有相同名稱的方法,而且該方法會檢查是否已發生任何錯誤,並正確處理錯誤。 UpdateList 方法會更新上層控制項中的 ListBox;當這個類別中的 NameDueDate 屬性變更時,會呼叫此方法。 稍後將實作它們。

整合到 [屬性] 視窗

現在,撰寫管理 ListBox 的程式碼,這會繫結至 [屬性] 視窗。

您必須變更按鈕,按一下處理常式以讀取 TextBox、建立 TodoItem,並將它新增至 ListBox。

  1. 將現有的 button1_Click 函式取代為建立新 TodoItem 的程式碼,並將它新增至 ListBox。 它會呼叫 TrackSelection(),稍後會加以定義。

    private void button1_Click(object sender, RoutedEventArgs e)
    {
        if (textBox.Text.Length > 0)
        {
            var item = new TodoItem(this, textBox.Text);
            listBox.Items.Add(item);
            TrackSelection();
            CheckForErrors();
        }
    }
    
  2. 在 [設計] 檢視中,選取 ListBox 控制項。 在 [屬性] 視窗中,按一下 [事件處理常式] 按鈕並尋找 SelectionChanged 事件。 在文字方塊中填入 listBox_SelectionChanged。 這樣做會為 SelectionChanged 處理常式新增虛設常式,並將它指派給該事件。

  3. 實作 TrackSelection() 方法。 因為您需要取得 SVsUIShellSTrackSelection 服務,因此您需要讓 TodoWindowControl 可存取 GetService。 將下列方法新增至 TodoWindow班級:

    internal object GetVsService(Type service)
    {
        return GetService(service);
    }
    
  4. 將下列 using 指示詞新增至 TodoWindowControl.xaml.cs

    using System.Runtime.InteropServices;
    using Microsoft.VisualStudio.Shell.Interop;
    using Microsoft.VisualStudio;
    using Microsoft.VisualStudio.Shell;
    
  5. 填入 SelectionChanged 處理常式,如下所示:

    private void listBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
    {
        TrackSelection();
    }
    
  6. 現在,填入 TrackSelection 函式,以提供與 [屬性] 視窗的整合。 當使用者將專案新增至 ListBox 或按一下 ListBox 中的專案時,就會呼叫此函式。 它會將 ListBox 的內容新增至 SelectionContainer,並將 SelectionContainer 傳遞至 [屬性] 視窗的 OnSelectChange 事件處理常式。 TrackSelection 服務會追蹤使用者介面 (UI) 中選取的物件,並顯示其屬性

    private SelectionContainer mySelContainer;
    private System.Collections.ArrayList mySelItems;
    private IVsWindowFrame frame = null;
    
    private void TrackSelection()
    {
        if (frame == null)
        {
            var shell = parent.GetVsService(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 = listBox.SelectedItem as TodoItem;
        if (selected != null)
        {
            mySelItems.Add(selected);
        }
    
        mySelContainer.SelectedObjects = mySelItems;
    
        ITrackSelection track = parent.GetVsService(typeof(STrackSelection))
                                as ITrackSelection;
        if (track != null)
        {
            track.OnSelectChange(mySelContainer);
        }
    }
    

    現在,您已有 [屬性] 視窗可以使用的類別,您可以將 [屬性] 視窗與工具視窗整合。 當使用者在工具視窗中的 ListBox 中按一下專案時,應該據以更新 [屬性] 視窗。 同樣地,當使用者在 [屬性] 視窗中變更 ToDo 專案時,應更新相關聯的專案。

  7. 現在,在 TodoWindowControl.xaml.cs中新增 UpdateList 函式程式碼的其餘部分。 它應該從 ListBox 卸除並重新新增修改過的 TodoItem。

    public void UpdateList(TodoItem item)
    {
        var index = listBox.SelectedIndex;
        listBox.Items.RemoveAt(index);
        listBox.Items.Insert(index, item);
        listBox.SelectedItem = index;
    }
    
  8. 測試您的程式碼。 建置此專案並開始偵錯。 應該會出現實驗執行個體。

  9. 開啟 [工具]>[選項] 頁面。 您應該會在左窗格中看到 ToDo 類別。 類別會依字母順序列出,因此請在 Ts 底下查看。

  10. [Todo] 選項頁面上,您應該會看到 DaysAhead 屬性設定為 0。 將其變更為 2

  11. [檢視 / 其他視窗] 功能表上,開啟 TodoWindow。 在文字方塊中輸入 EndDate,然後按一下 [新增]

  12. 在清單方塊中,您應該會在兩天後看到比今天晚兩天的日期。

將文字新增至 [輸出] 視窗,並將項目新增至 [工作清單]

針對 [工作清單],您可以建立類型為 [工作] 的新物件,然後藉由呼叫其 Add 方法,將該 [工作] 物件新增至 [工作清單]。 若要寫入 [輸出] 視窗,您可以呼叫其 GetPane 方法來取得窗格物件,然後呼叫窗格物件的 OutputString 方法。

  1. TodoWindowControl.xaml.csbutton1_Click 方法中,新增程式碼以取得 [輸出] 視窗的 [一般] 窗格 (這是預設值),並寫入其中。 方法看起來應如下所示:

    private void button1_Click(object sender, EventArgs e)
    {
        if (textBox.Text.Length > 0)
        {
            var item = new TodoItem(this, textBox.Text);
            listBox.Items.Add(item);
    
            var outputWindow = parent.GetVsService(
                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();
        }
    }
    
  2. 若要將項目新增至工作清單,您需要將巢狀類別新增至 TodoWindowControl 類別。 巢狀類別必須衍生自 TaskProvider。 將下列代碼新增至 TodoWindowControl 類別的結尾:

    [Guid("72de1eAD-a00c-4f57-bff7-57edb162d0be")]
    public class TodoWindowTaskProvider : TaskProvider
    {
        public TodoWindowTaskProvider(IServiceProvider sp)
            : base(sp)
        {
        }
    }
    
  3. 接下來,將 私人參考新增至 TodoTaskProvider,並將 CreateProvider() 方法新增至 TodoWindowControl 類別。 程式碼看起來應該類似:

    private TodoWindowTaskProvider taskProvider;
    private void CreateProvider()
    {
        if (taskProvider == null)
        {
            taskProvider = new TodoWindowTaskProvider(parent);
            taskProvider.ProviderName = "To Do";
        }
    }
    
  4. 新增 ClearError(),它會清除 [工作清單] 和 ReportError(),它會將專案新增至 [工作清單],新增至 TodoWindowControl 類別。

    private void ClearError()
    {
        CreateProvider();
        taskProvider.Tasks.Clear();
    }
    private void ReportError(string p)
    {
        CreateProvider();
        var errorTask = new Task();
        errorTask.CanDelete = false;
        errorTask.Category = TaskCategory.Comments;
        errorTask.Text = p;
    
        taskProvider.Tasks.Add(errorTask);
    
        taskProvider.Show();
    
        var taskList = parent.GetVsService(typeof(SVsTaskList))
            as IVsTaskList2;
        if (taskList == null)
        {
            return;
        }
    
        var guidProvider = typeof(TodoWindowTaskProvider).GUID;
         taskList.SetActiveProvider(ref guidProvider);
    }
    
  5. 現在,實作 CheckForErrors 方法如下。

    public void CheckForErrors()
    {
        foreach (TodoItem item in listBox.Items)
        {
            if (item.DueDate < DateTime.Now)
            {
                ReportError("To Do Item is out of date: "
                    + item.ToString());
            }
        }
    }
    

試試看

  1. 建置此專案並開始偵錯。 隨即出現實驗執行個體。

  2. 開啟 TodoWindow ([檢視]>[其他視窗]>TodoWindow)。

  3. 在文字方塊中輸入內容,然後按一下 [新增]

    到期日 (在今天之後的 2 天後) 會新增至清單方塊。 不會產生任何錯誤,而且 [工作清單] ([檢視]>[工作清單]) 不應該有任何項目。

  4. 現在,將 [工具]>[選項]>[待辦事項] 頁面上的設定從 2 變更回 0

  5. TodoWindow 中輸入其他內容,然後再次按一下 [新增]。 這會觸發錯誤,也會觸發 [工作清單] 中的項目。

    當您新增專案時,初始日期會設定為現在加上 2 天。

  6. [檢視] 功能表上,按一下 [輸出] 以開啟 [輸出] 視窗。

    請注意,每次新增專案時,都會在 [工作清單] 窗格中顯示訊息。

  7. 按兩下 ListBox 中的其中一個項目。

    [屬性] 視窗會顯示項目的兩個屬性。

  8. 變更其中一個屬性,然後按 Enter 鍵。

    項目會在 ListBox 中更新。