Compartilhar via


Demonstra Passo a passo: Usar um comando shell com uma extensão de editor

Em um VSPackage, você pode adicionar recursos como comandos de menu ao editor. Este passo a passo mostra como adicionar um adorno a um modo de exibição de texto no editor invocando um comando de menu.

Este passo a passo demonstra o uso de um VSPackage junto com uma parte do componente Managed Extensibility Framework (MEF). Você deve usar um VSPackage para registrar o comando de menu com o shell do Visual Studio. E você pode usar o comando para acessar a parte do componente MEF.

Criar uma extensão com um comando de menu

Crie um VSPackage que coloque um comando de menu chamado Adicionar adorno no menu Ferramentas .

  1. Crie um projeto C# VSIX chamado MenuCommandTeste adicione um nome de modelo de item de Comando Personalizado AddAdornment. Para obter mais informações, consulte Criar uma extensão com um comando de menu.

  2. Uma solução chamada MenuCommandTest é aberta. O arquivo MenuCommandTestPackage tem o código que cria o comando de menu e o coloca no menu Ferramentas . Neste ponto, o comando apenas faz com que uma caixa de mensagem apareça. Etapas posteriores mostrarão como alterar isso para exibir o adorno de comentários.

  3. Abra o arquivo source.extension.vsixmanifest no Editor de manifesto VSIX. A Assets guia deve ter uma linha para um Microsoft.VisualStudio.VsPackage chamado MenuCommandTest.

  4. Salve e feche o arquivo source.extension.vsixmanifest .

Adicionar uma extensão MEF à extensão de comando

  1. No Gerenciador de Soluções, clique com o botão direito do mouse no nó da solução, clique em Adicionar e em Novo Projeto. Na caixa de diálogo Adicionar Novo Projeto, clique em Extensibilidade em Visual C# e, em seguida, em Projeto VSIX. Dê ao projeto o nome de CommentAdornmentTest.

  2. Como esse projeto interagirá com o assembly VSPackage de nome forte, você deve assinar o assembly. Você pode reutilizar o arquivo de chave já criado para o assembly VSPackage.

    1. Abra as propriedades do projeto e selecione a guia Assinatura .

    2. Selecione Assinar o assembly.

    3. Em Escolher um arquivo de chave de nome forte, selecione o arquivo Key.snk que foi gerado para o assembly MenuCommandTest.

Consulte a extensão MEF no projeto VSPackage

Como você está adicionando um componente MEF ao VSPackage, você deve especificar os dois tipos de ativos no manifesto.

Observação

Para saber mais informações sobre o MEF, consulte Managed Extensibility Framework (MEF).

Para fazer referência ao componente MEF no projeto VSPackage

  1. No projeto MenuCommandTest, abra o arquivo source.extension.vsixmanifest no Editor de manifesto VSIX.

  2. Na guia Ativos, clique em Novo.

  3. Na lista Tipo, escolha Microsoft.VisualStudio.MefComponent.

  4. Na lista Origem, escolha Um projeto na solução atual.

  5. Na lista Projeto, escolha CommentAdornmentTest.

  6. Salve e feche o arquivo source.extension.vsixmanifest .

  7. Certifique-se de que o projeto MenuCommandTest tem uma referência ao projeto CommentAdornmentTest.

  8. No projeto CommentAdornmentTest, defina o projeto para produzir uma montagem. No Gerenciador de Soluções, selecione o projeto e procure na janela Propriedades a propriedade Copy Build Output to OutputDirectory e defina-a como true.

Definir um adorno de comentário

O próprio adorno de comentários consiste em um ITrackingSpan que rastreia o texto selecionado e algumas cadeias de caracteres que representam o autor e a descrição do texto.

Para definir um adorno de comentário

  1. No projeto CommentAdornmentTest, adicione um novo arquivo de classe e nomeie-o CommentAdornment.

  2. Adicione as seguintes referências:

    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. Adicione a seguinte using diretiva.

    using Microsoft.VisualStudio.Text;
    
  4. O arquivo deve conter uma classe chamada CommentAdornment.

    internal class CommentAdornment
    
  5. Adicione três campos à CommentAdornment classe para o , o ITrackingSpanautor e a descrição.

    public readonly ITrackingSpan Span;
    public readonly string Author;
    public readonly string Text;
    
  6. Adicione um construtor que inicialize os campos.

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

Criar um elemento visual para o adorno

Defina um elemento visual para o seu adorno. Para esta explicação passo a passo, defina um controle que herda da classe CanvasWindows Presentation Foundation (WPF).

  1. Crie uma classe no projeto CommentAdornmentTest e nomeie-a CommentBlock.

  2. Adicione as seguintes using diretivas.

    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. Faça a CommentBlock classe herdar de Canvas.

    internal class CommentBlock : Canvas
    { }
    
  4. Adicione alguns campos privados para definir os aspectos visuais do adorno.

    private Geometry textGeometry;
    private Grid commentGrid;
    private static Brush brush;
    private static Pen solidPen;
    private static Pen dashPen;
    
  5. Adicione um construtor que defina o adorno de comentário e adicione o texto relevante.

    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. Também implemente um OnRender manipulador de eventos que desenhe o adorno.

    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);
        }
    }
    

Adicionar um IWpfTextViewCreationListener

O IWpfTextViewCreationListener é uma parte do componente MEF que você pode usar para ouvir eventos de criação de exibição.

  1. Adicione um arquivo de classe ao projeto CommentAdornmentTest e nomeie-o Connector.

  2. Adicione as seguintes using diretivas.

    using System.ComponentModel.Composition;
    using Microsoft.VisualStudio.Text.Editor;
    using Microsoft.VisualStudio.Utilities;
    
  3. Declare uma classe que implementa IWpfTextViewCreationListenero e exporte-a com um de "texto" e um ContentTypeAttributeTextViewRoleAttribute de Document. O atributo de tipo de conteúdo especifica o tipo de conteúdo ao qual o componente se aplica. O tipo de texto é o tipo base para todos os tipos de arquivo não-binário. Portanto, quase todos os modos de exibição de texto criados serão desse tipo. O atributo de função de exibição de texto especifica o tipo de modo de exibição de texto ao qual o componente se aplica. As funções de exibição de texto do documento geralmente mostram texto composto de linhas e armazenado em um arquivo.

    [Export(typeof(IWpfTextViewCreationListener))]
    [ContentType("text")]
    [TextViewRole(PredefinedTextViewRoles.Document)]
    public sealed class Connector : IWpfTextViewCreationListener
    
  4. Implemente o método para que ele chame o TextViewCreated evento estático Create() do CommentAdornmentManager.

    public void TextViewCreated(IWpfTextView textView)
    {
        CommentAdornmentManager.Create(textView);
    }
    
  5. Adicione um método que você pode usar para executar o comando.

    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);
        }
    }
    

Definir uma camada de adorno

Para adicionar um novo adorno, você deve definir uma camada de adorno.

Para definir uma camada de adorno

  1. Connector Na classe, declare um campo público do tipo AdornmentLayerDefinitione exporte-o com um que especifique um nome exclusivo para a camada de adorno e um OrderAttributeNameAttribute que defina a relação de ordem Z dessa camada de adorno com as outras camadas de exibição de texto (texto, acento circunflexo e seleção).

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

Fornecer adornos de comentários

Ao definir um adorno, implemente também um provedor de adorno de comentários e um gerenciador de adornos de comentários. O provedor de adornos de comentários mantém uma lista de adornos de comentários, escuta eventos no buffer de Changed texto subjacente e exclui adornos de comentários quando o texto subjacente é excluído.

  1. Adicione um novo arquivo de classe ao projeto CommentAdornmentTest e nomeie-o CommentAdornmentProvider.

  2. Adicione as seguintes using diretivas.

    using System;
    using System.Collections.Generic;
    using System.Collections.ObjectModel;
    using Microsoft.VisualStudio.Text;
    using Microsoft.VisualStudio.Text.Editor;
    
  3. Adicione uma classe chamada CommentAdornmentProvider.

    internal class CommentAdornmentProvider
    {
    }
    
  4. Adicione campos privados para o buffer de texto e a lista de adornos de comentários relacionados ao buffer.

    private ITextBuffer buffer;
    private IList<CommentAdornment> comments = new List<CommentAdornment>();
    
    
  5. Adicione um construtor para CommentAdornmentProvider. Esse construtor deve ter acesso privado porque o provedor é instanciado pelo Create() método. O construtor adiciona o OnBufferChanged manipulador de eventos ao Changed evento.

    private CommentAdornmentProvider(ITextBuffer buffer)
    {
        this.buffer = buffer;
        //listen to the Changed event so we can react to deletions. 
        this.buffer.Changed += OnBufferChanged;
    }
    
    
  6. Adicione o método Create().

    public static CommentAdornmentProvider Create(IWpfTextView view)
    {
        return view.Properties.GetOrCreateSingletonProperty<CommentAdornmentProvider>(delegate { return new CommentAdornmentProvider(view.TextBuffer); });
    }
    
    
  7. Adicione o método Detach().

    public void Detach()
    {
        if (this.buffer != null)
        {
            //remove the Changed listener 
            this.buffer.Changed -= OnBufferChanged;
            this.buffer = null;
        }
    }
    
  8. Adicione o manipulador de OnBufferChanged eventos.

    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. Adicione uma declaração para um CommentsChanged evento.

    public event EventHandler<CommentsChangedEventArgs> CommentsChanged;
    
  10. Crie um Add() método para adicionar o adorno.

    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. Adicione um RemoveComments() método.

    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. Adicione um GetComments() método que retorne todos os comentários em uma determinada extensão de instantâneo.

    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. Adicione uma classe chamada CommentsChangedEventArgs, da seguinte maneira.

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

Gerenciar adornos de comentários

O gerenciador de adornos de comentários cria o adorno e o adiciona à camada de adorno. Ele ouve os LayoutChanged eventos para Closed que possa mover ou excluir o adorno. Ele também escuta o CommentsChanged evento que é disparado pelo provedor de adorno de comentários quando os comentários são adicionados ou removidos.

  1. Adicione um arquivo de classe ao projeto CommentAdornmentTest e nomeie-o CommentAdornmentManager.

  2. Adicione as seguintes using diretivas.

    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. Adicione uma classe chamada CommentAdornmentManager.

    internal class CommentAdornmentManager
        {
        }
    
  4. Adicione alguns campos privados.

    private readonly IWpfTextView view;
    private readonly IAdornmentLayer layer;
    private readonly CommentAdornmentProvider provider;
    
  5. Adicione um construtor que inscreva o gerente para os LayoutChanged eventos e Closed também para o CommentsChanged evento. O construtor é privado porque o gerenciador é instanciado pelo método estático 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. Adicione o Create() método que obtém um provedor ou cria um, se necessário.

    public static CommentAdornmentManager Create(IWpfTextView view)
    {
        return view.Properties.GetOrCreateSingletonProperty<CommentAdornmentManager>(delegate { return new CommentAdornmentManager(view); });
    }
    
  7. Adicione o CommentsChanged manipulador.

    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. Adicione o Closed manipulador.

    private void OnClosed(object sender, EventArgs e)
    {
        this.provider.Detach();
        this.view.LayoutChanged -= OnLayoutChanged;
        this.view.Closed -= OnClosed;
    }
    
  9. Adicione o LayoutChanged manipulador.

    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. Adicione o método private que desenha o comentário.

    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);
        }
    }
    

Use o comando de menu para adicionar o adorno de comentário

Você pode usar o comando de menu para criar um adorno de comentário implementando o MenuItemCallback método do VSPackage.

  1. Adicione as seguintes referências ao projeto MenuCommandTest:

    • Microsoft.VisualStudio.TextManager.Interop

    • Microsoft.VisualStudio.Editor

    • Microsoft.VisualStudio.Text.UI.Wpf

  2. Abra o arquivo AddAdornment.cs e adicione as seguintes using diretivas.

    using Microsoft.VisualStudio.TextManager.Interop;
    using Microsoft.VisualStudio.Text.Editor;
    using Microsoft.VisualStudio.Editor;
    using CommentAdornmentTest;
    
  3. Exclua o método e adicione o Execute() seguinte manipulador de comandos.

    private async void AddAdornmentHandler(object sender, EventArgs e)
    {
    }
    
  4. Adicione código para obter a exibição ativa. Você deve obter o SVsTextManager do shell do Visual Studio para obter o ativo IVsTextView.

    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. Se esse modo de exibição de texto for uma instância de um modo de exibição de texto do editor, você poderá convertê-lo na IVsUserData interface e, em seguida, obter o IWpfTextViewHost e seu associado IWpfTextView. Use o para chamar o método, que obtém o provedor de adorno de comentário e adiciona o IWpfTextViewHostConnector.Execute() adorno. O manipulador de comandos agora deve se parecer com este código:

    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. Defina o método AddAdornmentHandler como o manipulador para o comando AddAdornment no construtor 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);
    }
    

Compilar e testar o código

  1. Compile a solução e inicie a depuração. A instância experimental deve aparecer.

  2. Crie um arquivo de texto. Digite algum texto e selecione-o.

  3. No menu Ferramentas, clique em Invocar Adicionar adorno. Um balão deve ser exibido no lado direito da janela de texto e deve conter texto semelhante ao texto a seguir.

    SeuUsuárioNome

    Quatro...