다음을 통해 공유


연습: 편집기 확장에서 셸 명령 사용

VSPackage에서 메뉴 명령과 같은 기능을 편집기에 추가할 수 있습니다. 이 연습에서는 메뉴 명령을 호출하여 편집기에서 텍스트 보기에 도구 영역을 추가하는 방법을 보여 줍니다.

이 연습에서는 VSPackage를 MEF(Managed Extensibility Framework) 구성 요소 부분에서 사용하는 방법을 보여 줍니다. VSPackage를 사용하여 Visual Studio 셸에 메뉴 명령을 등록해야 합니다. 또한 명령을 사용하여 MEF 구성 요소 부분에 액세스할 수 있습니다.

메뉴 명령을 사용하여 확장 만들기

도구 메뉴에 도구 영역 추가라는 메뉴 명령을 배치하는 VSPackage를 만듭니다.

  1. MenuCommandTest라는 C # VSIX 프로젝트를 만들고 사용자 지정 명령 항목 템플릿 이름 AddAdornment를 추가합니다. 자세한 내용은 메뉴 명령으로 확장 만들기를 참조하세요.

  2. MenuCommandTest라는 솔루션이 열립니다. MenuCommandTestPackage 파일에는 메뉴 명령을 만들고 도구 메뉴에 배치하는 코드가 있습니다. 이때 이 명령은 메시지 상자만 표시합니다. 이후 단계에서는 주석 도구 영역을 표시하도록 메시지 상자를 변경하는 방법을 보여 줍니다.

  3. VSIX 매니페스트 편집기에서 source.extension.vsixmanifest 파일을 엽니다. Assets 탭에는 MenuCommandTest라는 Microsoft.VisualStudio.VsPackage에 대한 행이 있어야 합니다.

  4. source.extension.vsixmanifest 파일을 저장하고 닫습니다.

명령 확장에 MEF 확장 추가

  1. 솔루션 탐색기에서 솔루션 노드를 마우스 오른쪽 단추로 클릭하고 추가를 클릭한 다음, 새 프로젝트를 클릭합니다. 새 프로젝트 대화 상자에서 Visual C# 아래에서 확장성을 클릭한 다음, VSIX 프로젝트를 클릭합니다. 프로젝트 이름을 CommentAdornmentTest로 지정합니다.

  2. 이 프로젝트는 강력한 이름의 VSPackage 어셈블리와 상호 작용하므로 어셈블리에 서명해야 합니다. VSPackage 어셈블리에 대해 이미 만든 키 파일을 다시 사용할 수 있습니다.

    1. 프로젝트 속성을 열고 서명 탭을 선택합니다.

    2. 어셈블리 서명을 클릭합니다.

    3. 강력한 이름 키 파일 선택에서 MenuCommandTest 어셈블리에 대해 생성된 Key.snk 파일을 선택합니다.

VSPackage 프로젝트의 MEF 확장을 참조하세요.

VSPackage에 MEF 구성 요소를 추가하므로 매니페스트에서 두 종류의 자산을 모두 지정해야 합니다.

참고 항목

MEF에 대한 자세한 내용은 MEF(Managed Extensibility Framework)를 참조하세요.

VSPackage 프로젝트의 MEF 구성 요소를 참조하려면

  1. MenuCommandTest 프로젝트의 VSIX 매니페스트 편집기에서 source.extension.vsixmanifest 파일을 엽니다.

  2. 자산 탭에서 새로 만들기를 선택합니다.

  3. 형식 목록에서 Microsoft.VisualStudio.MefComponent를 선택합니다.

  4. 소스 목록에서 현재 솔루션의 프로젝트를 선택합니다.

  5. 프로젝트 목록에서 CommentAdornmentTest를 선택합니다.

  6. source.extension.vsixmanifest 파일을 저장하고 닫습니다.

  7. MenuCommandTest 프로젝트에 CommentAdornmentTest 프로젝트에 대한 참조가 있는지 확인합니다.

  8. CommentAdornmentTest 프로젝트에서 어셈블리를 생성하도록 프로젝트를 설정합니다. 솔루션 탐색기에서 프로젝트를 선택하고 OutputDirectory에 빌드 출력 복사 속성에 대한 속성 창을 보고 true로 설정합니다.

주석 도구 영역 정의

주석 도구 영역 자체는 선택한 텍스트를 추적하는 ITrackingSpan과 작성자 및 텍스트 설명을 나타내는 일부 문자열로 구성됩니다.

주석 도구 영역을 정의하려면

  1. CommentAdornmentTest 프로젝트에서 새 클래스 파일을 추가하고 이름을 CommentAdornment로 지정합니다.

  2. 다음 참조를 추가합니다.

    1. Microsoft.VisualStudio.CoreUtility

    2. Microsoft.VisualStudio.Text.Data

    3. Microsoft.VisualStudio.Text.Logic

    4. Microsoft.VisualStudio.Text.UI

    5. Microsoft.VisualStudio.Text.UI.Wpf

    6. System.ComponentModel.Composition

    7. PresentationCore

    8. PresentationFramework

    9. WindowsBase

  3. 다음 using 지시문을 추가합니다.

    using Microsoft.VisualStudio.Text;
    
  4. 파일에는 CommentAdornment라는 클래스가 포함되어야 합니다.

    internal class CommentAdornment
    
  5. ITrackingSpan에 대한 CommentAdornment 클래스에 작성자 및 설명 필드를 추가합니다.

    public readonly ITrackingSpan Span;
    public readonly string Author;
    public readonly string Text;
    
  6. 필드를 초기화하는 생성자를 추가합니다.

    public CommentAdornment(SnapshotSpan span, string author, string text)
    {
        this.Span = span.Snapshot.CreateTrackingSpan(span, SpanTrackingMode.EdgeExclusive);
        this.Author = author;
        this.Text = text;
    }
    

도구 영역에 대한 시각적 요소 만들기

도구 영역에 대한 시각적 요소를 정의합니다. 이 연습에서는 WPF(Windows Presentation Foundation) 클래스 Canvas에서 상속되는 컨트롤을 정의합니다.

  1. CommentAdornmentTest 프로젝트에서 클래스를 만들고 이름을 CommentBlock으로 지정합니다.

  2. 다음 using 지시문을 추가합니다.

    using Microsoft.VisualStudio.Text;
    using System;
    using System.Windows;
    using System.Windows.Controls;
    using System.Windows.Media;
    using System.Windows.Shapes;
    using System.ComponentModel.Composition;
    using Microsoft.VisualStudio.Text.Editor;
    using Microsoft.VisualStudio.Utilities;
    
  3. CommentBlock 클래스가 Canvas에서 상속되도록 합니다.

    internal class CommentBlock : Canvas
    { }
    
  4. 일부 프라이빗 필드를 추가하여 도구 영역의 시각적 측면을 정의합니다.

    private Geometry textGeometry;
    private Grid commentGrid;
    private static Brush brush;
    private static Pen solidPen;
    private static Pen dashPen;
    
  5. 주석 도구 영역을 정의하고 관련 텍스트를 추가하는 생성자를 추가합니다.

    public CommentBlock(double textRightEdge, double viewRightEdge,
            Geometry newTextGeometry, string author, string body)
    {
        if (brush == null)
        {
            brush = new SolidColorBrush(Color.FromArgb(0x20, 0x00, 0xff, 0x00));
            brush.Freeze();
            Brush penBrush = new SolidColorBrush(Colors.Green);
            penBrush.Freeze();
            solidPen = new Pen(penBrush, 0.5);
            solidPen.Freeze();
            dashPen = new Pen(penBrush, 0.5);
            dashPen.DashStyle = DashStyles.Dash;
            dashPen.Freeze();
        }
    
        this.textGeometry = newTextGeometry;
    
        TextBlock tb1 = new TextBlock();
        tb1.Text = author;
        TextBlock tb2 = new TextBlock();
        tb2.Text = body;
    
        const int MarginWidth = 8;
        this.commentGrid = new Grid();
        this.commentGrid.RowDefinitions.Add(new RowDefinition());
        this.commentGrid.RowDefinitions.Add(new RowDefinition());
        ColumnDefinition cEdge = new ColumnDefinition();
        cEdge.Width = new GridLength(MarginWidth);
        ColumnDefinition cEdge2 = new ColumnDefinition();
        cEdge2.Width = new GridLength(MarginWidth);
        this.commentGrid.ColumnDefinitions.Add(cEdge);
        this.commentGrid.ColumnDefinitions.Add(new ColumnDefinition());
        this.commentGrid.ColumnDefinitions.Add(cEdge2);
    
        System.Windows.Shapes.Rectangle rect = new System.Windows.Shapes.Rectangle();
        rect.RadiusX = 6;
        rect.RadiusY = 3;
        rect.Fill = brush;
        rect.Stroke = Brushes.Green;
    
            Size inf = new Size(double.PositiveInfinity, double.PositiveInfinity);
            tb1.Measure(inf);
            tb2.Measure(inf);
            double middleWidth = Math.Max(tb1.DesiredSize.Width, tb2.DesiredSize.Width);
            this.commentGrid.Width = middleWidth + 2 * MarginWidth;
    
        Grid.SetColumn(rect, 0);
        Grid.SetRow(rect, 0);
        Grid.SetRowSpan(rect, 2);
        Grid.SetColumnSpan(rect, 3);
        Grid.SetRow(tb1, 0);
        Grid.SetColumn(tb1, 1);
        Grid.SetRow(tb2, 1);
        Grid.SetColumn(tb2, 1);
        this.commentGrid.Children.Add(rect);
        this.commentGrid.Children.Add(tb1);
        this.commentGrid.Children.Add(tb2);
    
        Canvas.SetLeft(this.commentGrid, Math.Max(viewRightEdge - this.commentGrid.Width - 20.0, textRightEdge + 20.0));
        Canvas.SetTop(this.commentGrid, textGeometry.GetRenderBounds(solidPen).Top);
    
        this.Children.Add(this.commentGrid);
    }
    
  6. 또한 도구 영역을 그리는 OnRender 이벤트 처리기를 구현합니다.

    protected override void OnRender(DrawingContext dc)
    {
        base.OnRender(dc);
        if (this.textGeometry != null)
        {
            dc.DrawGeometry(brush, solidPen, this.textGeometry);
            Rect textBounds = this.textGeometry.GetRenderBounds(solidPen);
            Point p1 = new Point(textBounds.Right, textBounds.Bottom);
            Point p2 = new Point(Math.Max(Canvas.GetLeft(this.commentGrid) - 20.0, p1.X), p1.Y);
            Point p3 = new Point(Math.Max(Canvas.GetLeft(this.commentGrid), p1.X), (Canvas.GetTop(this.commentGrid) + p1.Y) * 0.5);
            dc.DrawLine(dashPen, p1, p2);
            dc.DrawLine(dashPen, p2, p3);
        }
    }
    

IWpfTextViewCreationListener 추가

IWpfTextViewCreationListener는 이벤트를 보기 위해 수신 대기하는 데 사용할 수 있는 MEF 구성 요소 부분입니다.

  1. CommentAdornmentTest 프로젝트에 클래스 파일을 추가하고 이름을 Connector로 지정합니다.

  2. 다음 using 지시문을 추가합니다.

    using System.ComponentModel.Composition;
    using Microsoft.VisualStudio.Text.Editor;
    using Microsoft.VisualStudio.Utilities;
    
  3. IWpfTextViewCreationListener를 구현하는 클래스를 선언하고 라는 클래스를 만들고, "text"의 ContentTypeAttributeDocumentTextViewRoleAttribute를 사용하여 내보냅니다. 콘텐츠 형식 특성은 구성 요소가 적용되는 콘텐츠의 종류를 지정합니다. 텍스트 형식은 이진이 아닌 모든 파일 형식의 기본 형식입니다. 따라서 만들어진 거의 모든 텍스트 보기는 이 형식입니다. 텍스트 보기 역할 특성은 구성 요소가 적용되는 텍스트 보기의 종류를 지정합니다. 문서 텍스트 보기 역할은 일반적으로 여러 줄로 구성되고 파일에 저장된 텍스트를 표시합니다.

    [Export(typeof(IWpfTextViewCreationListener))]
    [ContentType("text")]
    [TextViewRole(PredefinedTextViewRoles.Document)]
    public sealed class Connector : IWpfTextViewCreationListener
    
  4. CommentAdornmentManager의 정적 Create() 이벤트를 호출할 수 있도록 TextViewCreated 메서드를 구현합니다.

    public void TextViewCreated(IWpfTextView textView)
    {
        CommentAdornmentManager.Create(textView);
    }
    
  5. 명령을 실행하는 데 사용할 수 있는 메서드를 추가합니다.

    static public void Execute(IWpfTextViewHost host)
    {
        IWpfTextView view = host.TextView;
        //Add a comment on the selected text. 
        if (!view.Selection.IsEmpty)
        {
            //Get the provider for the comment adornments in the property bag of the view.
            CommentAdornmentProvider provider = view.Properties.GetProperty<CommentAdornmentProvider>(typeof(CommentAdornmentProvider));
    
            //Add some arbitrary author and comment text. 
            string author = System.Security.Principal.WindowsIdentity.GetCurrent().Name;
            string comment = "Four score....";
    
            //Add the comment adornment using the provider.
            provider.Add(view.Selection.SelectedSpans[0], author, comment);
        }
    }
    

도구 영역 계층 정의

새 도구 영역을 추가하려면 도구 영역 계층을 정의해야 합니다.

도구 영역 계층을 정의하려면

  1. Connector 클래스에서 형식 AdornmentLayerDefinition의 퍼블릭 필드를 선언하고 도구 영역 계층의 고유한 이름을 지정하는 NameAttribute와 이 도구 영역 계층의 Z 순서 관계를 정의하는 OrderAttribute를 사용하여 다른 텍스트 보기 계층(텍스트, 캐럿 및 선택 영역)으로 내보냅니다.

    [Export(typeof(AdornmentLayerDefinition))]
    [Name("CommentAdornmentLayer")]
    [Order(After = PredefinedAdornmentLayers.Selection, Before = PredefinedAdornmentLayers.Text)]
    public AdornmentLayerDefinition commentLayerDefinition;
    
    

주석 도구 영역 제공

도구 영역을 정의할 때 주석 도구 영역 공급자와 주석 도구 영역 관리자도 구현합니다. 주석 도구 영역 공급자는 주석 도구 영역 목록을 유지하고, 기본 텍스트 버퍼에서 Changed 이벤트를 수신 대기하고, 기본 텍스트가 삭제될 때 주석 도구 영역을 삭제합니다.

  1. CommentAdornmentTest 프로젝트에 새 클래스 파일을 추가하고 이름을 CommentAdornmentProvider로 지정합니다.

  2. 다음 using 지시문을 추가합니다.

    using System;
    using System.Collections.Generic;
    using System.Collections.ObjectModel;
    using Microsoft.VisualStudio.Text;
    using Microsoft.VisualStudio.Text.Editor;
    
  3. CommentAdornmentProvider라는 클래스를 추가합니다.

    internal class CommentAdornmentProvider
    {
    }
    
  4. 텍스트 버퍼에 대한 프라이빗 필드와 버퍼와 관련된 주석 도구 영역 목록을 추가합니다.

    private ITextBuffer buffer;
    private IList<CommentAdornment> comments = new List<CommentAdornment>();
    
    
  5. CommentAdornmentProvider에 대한 생성자를 추가합니다. 공급자가 Create() 메서드에 의해 인스턴스화되므로 이 생성자에는 프라이빗 액세스 권한이 있어야 합니다. 생성자는 Changed 이벤트에 OnBufferChanged 이벤트 처리기를 추가합니다.

    private CommentAdornmentProvider(ITextBuffer buffer)
    {
        this.buffer = buffer;
        //listen to the Changed event so we can react to deletions. 
        this.buffer.Changed += OnBufferChanged;
    }
    
    
  6. Create() 메서드를 추가합니다.

    public static CommentAdornmentProvider Create(IWpfTextView view)
    {
        return view.Properties.GetOrCreateSingletonProperty<CommentAdornmentProvider>(delegate { return new CommentAdornmentProvider(view.TextBuffer); });
    }
    
    
  7. Detach() 메서드를 추가합니다.

    public void Detach()
    {
        if (this.buffer != null)
        {
            //remove the Changed listener 
            this.buffer.Changed -= OnBufferChanged;
            this.buffer = null;
        }
    }
    
  8. OnBufferChanged 이벤트 처리기를 추가합니다.

    private void OnBufferChanged(object sender, TextContentChangedEventArgs e)
    {
        //Make a list of all comments that have a span of at least one character after applying the change. There is no need to raise a changed event for the deleted adornments. The adornments are deleted only if a text change would cause the view to reformat the line and discard the adornments.
        IList<CommentAdornment> keptComments = new List<CommentAdornment>(this.comments.Count);
    
        foreach (CommentAdornment comment in this.comments)
        {
            Span span = comment.Span.GetSpan(e.After);
            //if a comment does not span at least one character, its text was deleted.
            if (span.Length != 0)
            {
                keptComments.Add(comment);
            }
        }
    
        this.comments = keptComments;
    }
    
  9. CommentsChanged 이벤트에 대한 선언을 추가합니다.

    public event EventHandler<CommentsChangedEventArgs> CommentsChanged;
    
  10. 도구 영역을 추가하는 Add() 메서드를 만듭니다.

    public void Add(SnapshotSpan span, string author, string text)
    {
        if (span.Length == 0)
            throw new ArgumentOutOfRangeException("span");
        if (author == null)
            throw new ArgumentNullException("author");
        if (text == null)
            throw new ArgumentNullException("text");
    
        //Create a comment adornment given the span, author and text.
        CommentAdornment comment = new CommentAdornment(span, author, text);
    
        //Add it to the list of comments. 
        this.comments.Add(comment);
    
        //Raise the changed event.
        EventHandler<CommentsChangedEventArgs> commentsChanged = this.CommentsChanged;
        if (commentsChanged != null)
            commentsChanged(this, new CommentsChangedEventArgs(comment, null));
    }
    
    
  11. RemoveComments() 메서드를 추가합니다.

    public void RemoveComments(SnapshotSpan span)
    {
        EventHandler<CommentsChangedEventArgs> commentsChanged = this.CommentsChanged;
    
        //Get a list of all the comments that are being kept
        IList<CommentAdornment> keptComments = new List<CommentAdornment>(this.comments.Count);
    
        foreach (CommentAdornment comment in this.comments)
        {
            //find out if the given span overlaps with the comment text span. If two spans are adjacent, they do not overlap. To consider adjacent spans, use IntersectsWith. 
            if (comment.Span.GetSpan(span.Snapshot).OverlapsWith(span))
            {
                //Raise the change event to delete this comment. 
                if (commentsChanged != null)
                    commentsChanged(this, new CommentsChangedEventArgs(null, comment));
            }
            else
                keptComments.Add(comment);
        }
    
        this.comments = keptComments;
    }
    
  12. 지정된 스냅샷 범위의 모든 주석을 반환하는 GetComments() 메서드를 추가합니다.

    public Collection<CommentAdornment> GetComments(SnapshotSpan span)
    {
        IList<CommentAdornment> overlappingComments = new List<CommentAdornment>();
        foreach (CommentAdornment comment in this.comments)
        {
            if (comment.Span.GetSpan(span.Snapshot).OverlapsWith(span))
                overlappingComments.Add(comment);
        }
    
        return new Collection<CommentAdornment>(overlappingComments);
    }
    
  13. 다음과 같이 CommentsChangedEventArgs라는 클래스를 추가합니다.

    internal class CommentsChangedEventArgs : EventArgs
    {
        public readonly CommentAdornment CommentAdded;
    
        public readonly CommentAdornment CommentRemoved;
    
        public CommentsChangedEventArgs(CommentAdornment added, CommentAdornment removed)
        {
            this.CommentAdded = added;
            this.CommentRemoved = removed;
        }
    }
    

주석 도구 영역 관리

주석 도구 영역 관리자는 도구 영역을 만들고 도구 영역 계층에 추가합니다. 도구 영역을 이동하거나 삭제할 수 있도록 LayoutChangedClosed 이벤트를 수신 대기합니다. 주석이 추가되거나 제거될 때 주석 도구 영역 공급자가 발생시킨 CommentsChanged 이벤트를 수신 대기합니다.

  1. CommentAdornmentTest 프로젝트에 클래스 파일을 추가하고 이름을 CommentAdornmentManager로 지정합니다.

  2. 다음 using 지시문을 추가합니다.

    using System;
    using System.Collections.Generic;
    using System.Windows.Media;
    using Microsoft.VisualStudio.Text;
    using Microsoft.VisualStudio.Text.Editor;
    using Microsoft.VisualStudio.Text.Formatting;
    
  3. CommentAdornmentManager라는 클래스를 추가합니다.

    internal class CommentAdornmentManager
        {
        }
    
  4. 일부 프라이빗 필드를 추가합니다.

    private readonly IWpfTextView view;
    private readonly IAdornmentLayer layer;
    private readonly CommentAdornmentProvider provider;
    
  5. 관리자를 LayoutChangedClosed 이벤트에 구독하고 CommentsChanged 이벤트에도 구독하는 생성자를 추가합니다. 관리자가 정적 Create() 메서드에 의해 인스턴스화되므로 생성자는 프라이빗입니다.

    private CommentAdornmentManager(IWpfTextView view)
    {
        this.view = view;
        this.view.LayoutChanged += OnLayoutChanged;
        this.view.Closed += OnClosed;
    
        this.layer = view.GetAdornmentLayer("CommentAdornmentLayer");
    
        this.provider = CommentAdornmentProvider.Create(view);
        this.provider.CommentsChanged += OnCommentsChanged;
    }
    
  6. 필요한 경우 공급자를 가져오거나 만드는 Create() 메서드를 추가합니다.

    public static CommentAdornmentManager Create(IWpfTextView view)
    {
        return view.Properties.GetOrCreateSingletonProperty<CommentAdornmentManager>(delegate { return new CommentAdornmentManager(view); });
    }
    
  7. CommentsChanged 처리기를 추가합니다.

    private void OnCommentsChanged(object sender, CommentsChangedEventArgs e)
    {
        //Remove the comment (when the adornment was added, the comment adornment was used as the tag). 
        if (e.CommentRemoved != null)
            this.layer.RemoveAdornmentsByTag(e.CommentRemoved);
    
        //Draw the newly added comment (this will appear immediately: the view does not need to do a layout). 
        if (e.CommentAdded != null)
            this.DrawComment(e.CommentAdded);
    }
    
  8. Closed 처리기를 추가합니다.

    private void OnClosed(object sender, EventArgs e)
    {
        this.provider.Detach();
        this.view.LayoutChanged -= OnLayoutChanged;
        this.view.Closed -= OnClosed;
    }
    
  9. LayoutChanged 처리기를 추가합니다.

    private void OnLayoutChanged(object sender, TextViewLayoutChangedEventArgs e)
    {
        //Get all of the comments that intersect any of the new or reformatted lines of text.
        List<CommentAdornment> newComments = new List<CommentAdornment>();
    
        //The event args contain a list of modified lines and a NormalizedSpanCollection of the spans of the modified lines.  
        //Use the latter to find the comments that intersect the new or reformatted lines of text. 
        foreach (Span span in e.NewOrReformattedSpans)
        {
            newComments.AddRange(this.provider.GetComments(new SnapshotSpan(this.view.TextSnapshot, span)));
        }
    
        //It is possible to get duplicates in this list if a comment spanned 3 lines, and the first and last lines were modified but the middle line was not. 
        //Sort the list and skip duplicates.
        newComments.Sort(delegate(CommentAdornment a, CommentAdornment b) { return a.GetHashCode().CompareTo(b.GetHashCode()); });
    
        CommentAdornment lastComment = null;
        foreach (CommentAdornment comment in newComments)
        {
            if (comment != lastComment)
            {
                lastComment = comment;
                this.DrawComment(comment);
            }
        }
    }
    
  10. 주석을 그리는 프라이빗 메서드를 추가합니다.

    private void DrawComment(CommentAdornment comment)
    {
        SnapshotSpan span = comment.Span.GetSpan(this.view.TextSnapshot);
        Geometry g = this.view.TextViewLines.GetMarkerGeometry(span);
    
        if (g != null)
        {
            //Find the rightmost coordinate of all the lines that intersect the adornment.
            double maxRight = 0.0;
            foreach (ITextViewLine line in this.view.TextViewLines.GetTextViewLinesIntersectingSpan(span))
                maxRight = Math.Max(maxRight, line.Right);
    
            //Create the visualization.
            CommentBlock block = new CommentBlock(maxRight, this.view.ViewportRight, g, comment.Author, comment.Text);
    
            //Add it to the layer.
            this.layer.AddAdornment(span, comment, block);
        }
    }
    

메뉴 명령을 사용하여 주석 도구 영역 추가

메뉴 명령을 사용하여 VSPackage의 MenuItemCallback 메서드를 구현하여 주석 도구 영역을 만들 수 있습니다.

  1. MenuCommandTest 프로젝트에 다음 참조를 추가합니다.

    • Microsoft.VisualStudio.TextManager.Interop

    • Microsoft.VisualStudio.Editor

    • Microsoft.VisualStudio.Text.UI.Wpf

  2. AddAdornment.cs 파일을 열고 다음 using 지시문을 추가합니다.

    using Microsoft.VisualStudio.TextManager.Interop;
    using Microsoft.VisualStudio.Text.Editor;
    using Microsoft.VisualStudio.Editor;
    using CommentAdornmentTest;
    
  3. Execute() 메서드를 삭제하고 다음 명령 처리기를 추가합니다.

    private async void AddAdornmentHandler(object sender, EventArgs e)
    {
    }
    
  4. 활성 보기를 가져오는 코드를 추가합니다. 활성 IVsTextView를 가져오려면 Visual Studio 셸의 SVsTextManager를 가져와야 합니다.

    private async void AddAdornmentHandler(object sender, EventArgs e)
    {
        IVsTextManager txtMgr = (IVsTextManager) await ServiceProvider.GetServiceAsync(typeof(SVsTextManager));
        IVsTextView vTextView = null;
        int mustHaveFocus = 1;
        txtMgr.GetActiveView(mustHaveFocus, null, out vTextView);
    }
    
  5. 이 텍스트 보기가 편집기 텍스트 보기의 인스턴스인 경우 IVsUserData 인터페이스로 캐스팅한 다음, IWpfTextViewHost 및 연결된 IWpfTextView를 가져올 수 있습니다. IWpfTextViewHost를 사용하여 주석 도구 영역 공급자를 가져오고 도구 영역을 추가하는 Connector.Execute() 메서드를 호출합니다. 이제 명령 처리기는 다음 코드와 같이 표시됩니다.

    private async void AddAdornmentHandler(object sender, EventArgs e)
    {
        IVsTextManager txtMgr = (IVsTextManager) await ServiceProvider.GetServiceAsync(typeof(SVsTextManager));
        IVsTextView vTextView = null;
        int mustHaveFocus = 1;
        txtMgr.GetActiveView(mustHaveFocus, null, out vTextView);
        IVsUserData userData = vTextView as IVsUserData;
         if (userData == null)
        {
            Console.WriteLine("No text view is currently open");
            return;
        }
        IWpfTextViewHost viewHost;
        object holder;
        Guid guidViewHost = DefGuidList.guidIWpfTextViewHost;
        userData.GetData(ref guidViewHost, out holder);
        viewHost = (IWpfTextViewHost)holder;
        Connector.Execute(viewHost);
    }
    
  6. AddAdornmentHandler 메서드를 AddAdornment 생성자에서 AddAdornment 명령의 처리기로 설정합니다.

    private AddAdornment(AsyncPackage package, OleMenuCommandService commandService)
    {
        this.package = package ?? throw new ArgumentNullException(nameof(package));
        commandService = commandService ?? throw new ArgumentNullException(nameof(commandService));
    
        var menuCommandID = new CommandID(CommandSet, CommandId);
        var menuItem = new MenuCommand(this.AddAdornmentHandler, menuCommandID);
        commandService.AddCommand(menuItem);
    }
    

코드 빌드 및 테스트

  1. 솔루션을 빌드하고 디버깅을 시작합니다. 실험적 인스턴스가 나타납니다.

  2. 텍스트 파일을 만듭니다. 텍스트를 입력한 다음, 선택합니다.

  3. 도구 메뉴에서 AddAdornment 호출을 클릭합니다. 풍선이 텍스트 창의 오른쪽에 표시되어야 하며 다음 텍스트와 유사한 텍스트를 포함해야 합니다.

    YourUserName

    Fourscore...