다음을 통해 공유


속성, 작업 목록, 출력, 옵션 창 확장

Visual Studio의 모든 도구 창에 액세스할 수 있습니다. 이 연습에서는 도구 창에 대한 정보를 새 옵션 페이지와 속성 페이지의 새 설정에 통합하는 방법과 작업 목록출력 창에 쓰는 방법을 보여 줍니다.

도구 창을 사용하여 확장 만들기

  1. VSIX 템플릿을 사용하여 TodoList라는 프로젝트를 만들고 TodoWindow라는 사용자 지정 도구 창 항목 템플릿을 추가합니다.

    참고 항목

    도구 창을 사용하여 확장을 만드는 방법에 대한 자세한 정보는 도구 창을 사용하여 확장 만들기를 참조하세요.

도구 창 설정

새 ToDo 항목을 입력할 TextBox, 목록에 새 항목을 추가하는 Button, 목록에 항목을 표시하는 ListBox를 추가합니다.

  1. TodoWindow.xaml에서 UserControl에서 Button, TextBox, StackPanel 컨트롤을 삭제합니다.

    참고 항목

    이후 단계에서 다시 사용할 button1_Click 이벤트 처리기는 삭제되지 않습니다.

  2. 도구 상자모든 WPF 컨트롤 섹션에서 캔버스 컨트롤을 그리드로 끕니다.

  3. TextBox, 단추, ListBox를 캔버스로 끕니다. 텍스트 상자와 단추가 같은 수준에 있도록 요소를 정렬하고 ListBox는 아래 그림과 같이 아래 창의 나머지 부분을 채웁니다.

    Finished Tool Window

  4. XAML 창에서 단추를 찾아 콘텐츠 속성을 추가로 설정합니다. Click="button1_Click" 특성을 추가하여 단추 이벤트 처리기를 단추 컨트롤에 다시 연결합니다. 캔버스 블록은 다음과 같습니다.

    <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.cs 또는 TodoListPackage.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에서 ProvideOptionPageAttributeTodoWindowPackage 클래스에 추가합니다.

    [ProvideOptionPage(typeof(ToolsOptions), "ToDo", "General", 101, 106, true)]
    
  2. ProvideOptionPage 생성자에 대한 첫 번째 매개 변수는 이전에 만든 클래스 ToolsOptions의 형식입니다. 두 번째 매개 변수인 “ToDo”는 옵션 대화 상자의 범주 이름입니다. 세 번째 매개 변수 “일반”은 옵션 페이지를 사용할 수 있는 옵션 대화 상자의 하위 범주 이름입니다. 다음 두 매개 변수는 문자열에 대한 리소스 ID입니다. 첫 번째는 범주의 이름이고 두 번째는 하위 범주의 이름입니다. 최종 매개 변수는 자동화를 사용하여 이 페이지에 액세스할 수 있는지 여부를 결정합니다.

    사용자가 옵션 페이지를 열면 다음 그림과 유사합니다.

    Options Page

    ToDo 범주와 일반 하위 범주를 확인하세요.

속성 창에서 데이터를 사용할 수 있도록 설정

ToDo 목록의 개별 항목에 대한 정보를 저장하는 TodoItem이라는 클래스를 만들어 사용 가능한 ToDo 목록 정보를 만들 수 있습니다.

  1. TodoItem.cs라는 클래스를 추가합니다.

    사용자가 도구 창을 사용할 수 있으면 ListBox의 항목이 TodoItems로 표시됩니다. 사용자가 ListBox에서 이러한 항목 중 하나를 선택하면 속성 창에 항목에 대한 정보가 표시됩니다.

    속성 창에서 데이터를 사용할 수 있도록 하려면 데이터를 DescriptionCategory라는 두 가지 특수 특성이 있는 퍼블릭 속성으로 전환합니다. Description속성 창 하단에 표시되는 텍스트입니다. Category속성 창이 분류 보기에 표시될 때 속성이 표시되어야 하는 위치를 결정합니다. 다음 그림에서 속성 창은 범주화된 보기에 있고, ToDo 필드 범주의 이름 속성이 선택되어 있으며 이름 속성에 대한 설명이 창 아래쪽에 표시됩니다.

    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. TodoWindowControl.xaml.cs에 다음 using 지시문을 추가합니다.

    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 범주가 표시됩니다. 범주는 사전순으로 나열되므로 T 아래를 살펴봅니다.

  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에 대한 프라이빗 참조를 추가하고 TodoWindowControl 클래스에 CreateProvider() 메서드를 추가합니다. 코드는 다음과 유사합니다.

    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에서 업데이트됩니다.