Compartilhar via


Passo a passo: Usando um comando Shell com uma extensão de Editor

A partir de um VSPackage, você pode adicionar recursos como, por exemplo, comandos de menu para o editor. Esta explicação passo a passo mostra como adicionar um adorno para um modo de exibição de texto no editor, invocando um comando de menu.

Esta explicação passo a passo demonstra o uso de um VSPackage em conjunto com uma parte de componente Managed Extensibility Framework (MEF). Você deve usar um VSPackage para registrar o comando de menu com o shell de Visual Studio, e você pode usar o comando para acessar a parte do componente MEF.

Pré-requisitos

Para concluir este passo a passo, você deve instalar o SDL do Visual Studio 2010.

Dica

Para obter mais informações sobre o SDK de Visual Studio, consulte Ampliando a visão geral de Visual Studio.Para descobrir como fazer o download do SDK do Visual Studio, consulte Visual Studio extensibilidade Developer Center no site do MSDN.

Locais para o modelo de projeto de pacote de Visual Studio

O modelo de projeto do pacote de Visual Studio pode ser encontrado em três locais diferentes de Novo projeto caixa de diálogo:

  1. Em Visual Basic extensibilidade. O idioma padrão do projeto é Visual Basic.

  2. Em C# extensibilidade. O idioma padrão do projeto é C#.

  3. Em outra extensibilidade de tipos de projeto. O idioma padrão do projeto é C++

Criando um VSPackage do comando de Menu

Criar um VSPackage que coloca um comando de menu chamado Adicionar adorno sobre o Ferramentas menu.

Para criar um comando de menu VSPackage

  1. Crie um pacote de Visual Studio e o nome MenuCommandTest. Clique em OK.

  2. Na página Bem-vindo, clique em próximo.

  3. No Select a Programming Language , selecione Visual Basic ou Visual C#, certifique-se de que gerar um novo arquivo de chave para assinar o conjunto está selecionada e clique em próximo.

  4. No Informações básicas de VSPackage página, o nome VSPackage caixa Digite MenuCommande, em seguida, clique em próximo.

  5. Sobre o Selecionar opções de VSPackage , selecione Comando de Menu e, em seguida, clique em próximo.

  6. Sobre o Opções de comando página, o nome do comando caixa Digite Adorno adicionar. No ID do comando , digite cmdidAddAdornment. Clique em Next.

  7. Sobre o Selecionar opções de teste , desmarque ambas as opções e, em seguida, em Concluir.

  8. Uma solução chamada MenuCommandTest é aberta. O arquivo de MenuCommandTestPackage tem o código que cria o comando de menu e coloca-o Ferramentas menu. Neste ponto, o comando apenas faz com que uma caixa de mensagem a ser exibida. Etapas posteriores mostrará como alterar essa opção para exibir o adorno de comentário.

  9. Abra o arquivo de source.extension.vsixmanifest no Editor de VSIX de manifesto. Ele deve ter um Content de linha para um VSPackage chamado MenuCommand.

  10. Salve e feche o arquivo Source.extension.vsixmanifest.

A adição de uma extensão MEF à solução VSPackage

Para adicionar a extensão MEF à solução VSPackage

  1. Em Solution Explorer, o botão direito do mouse no nó da solução, clique em Adde, em seguida, clique em Novo projeto. No Add New Project caixa de diálogo, clique em extensibilidade em Visual Basic ou Visual C#, em seguida, EditorClassifier. Nomeie o projeto CommentAdornmentTest.

  2. Porque este projeto irá interagir com o assembly de nome forte de VSPackage, 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 o Signing página.

    2. Selecione Sign the assembly.

    3. Em Choose a strong name key file, selecione o arquivo Key.snk que foi gerado para o assembly MenuCommandTest.

    4. Salve o projeto.

Referindo-se à extensão MEF no projeto VSPackage

Como você está adicionando um componente MEF para o VSPackage, você deve especificar os dois tipos de conteúdo no manifesto.

Dica

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

Para referir-se ao componente MEF no projeto VSPackage

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

  2. Sob o conteúdo do título, clique em Adicionar conteúdo. Sobre o Selecionar o tipo de conteúdo , selecione MEF componente. Em Selecionar uma fonte de, selecione CommentAdornmentTest.

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

  4. Adicione uma referência ao projeto CommentAdornmentTest.

Definindo um adorno de comentário

O adorno comentário próprio 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, exclua os arquivos de classe existentes.

  2. Adicione um novo arquivo de classe e denomine- CommentAdornment.

  3. Adicione o seguinte using instrução.

    Imports Microsoft.VisualStudio.Text
    
    using Microsoft.VisualStudio.Text;
    
  4. Adicionar uma classe chamada CommentAdornment.

    Friend Class CommentAdornment
    
    internal class CommentAdornment
    
  5. Adicionar três campos para o CommentAdornment de classe para o ITrackingSpan, o autor e a descrição.

    Public ReadOnly Span As ITrackingSpan
    Public ReadOnly Author As String 
    Public ReadOnly Text As String
    
    public readonly ITrackingSpan Span;
    public readonly string Author;
    public readonly string Text;
    
  6. Adicione um construtor que inicializa os campos.

    Public Sub New(ByVal span As SnapshotSpan, ByVal author As String, ByVal text As String)
        Me.Span = span.Snapshot.CreateTrackingSpan(span, SpanTrackingMode.EdgeExclusive)
        Me.Author = author
        Me.Text = text
    End Sub
    
    public CommentAdornment(SnapshotSpan span, string author, string text)
    {
        this.Span = span.Snapshot.CreateTrackingSpan(span, SpanTrackingMode.EdgeExclusive);
        this.Author = author;
        this.Text = text;
    }
    

Criando um elemento Visual para o adorno.

Você também deve definir um elemento visual para sua adorno. Para esta explicação, definir um controle que herda da classe Windows Presentation Foundation (WPF) Canvas.

Para criar um elemento visual para o adorno.

  1. Criar uma classe no projeto CommentAdornmentTest e o nome de CommentBlock.

  2. Adicione o seguinte using instruções.

    Imports System
    Imports System.Windows
    Imports System.Windows.Controls
    Imports System.Windows.Media
    Imports System.Windows.Shapes
    
    using System;
    using System.Windows;
    using System.Windows.Controls;
    using System.Windows.Media;
    using System.Windows.Shapes;
    
  3. Verifique o CommentBlock classe herdar de Canvas.

    Friend Class CommentBlock
        Inherits Canvas
    
    internal class CommentBlock : Canvas
    
  4. Adicione alguns campos privados para definir os aspectos visuais do adorno.

    Private textGeometry As Geometry
    Private commentGrid As Grid
    Private Shared brush As Brush
    Private Shared solidPen As Pen
    Private Shared dashPen As Pen
    
    private Geometry textGeometry;
    private Grid commentGrid;
    private static Brush brush;
    private static Pen solidPen;
    private static Pen dashPen;
    
  5. Adicione um construtor que define o adorno de comentário e adiciona o texto relevante.

    Public Sub New(ByVal textRightEdge As Double, ByVal viewRightEdge As Double, ByVal newTextGeometry As Geometry, ByVal author As String, ByVal body As String)
        If brush Is Nothing Then
            brush = New SolidColorBrush(Color.FromArgb(&H20, &H0, &HFF, &H0))
            brush.Freeze()
            Dim penBrush As Brush = 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()
        End If 
    
        Me.textGeometry = newTextGeometry
    
        Dim tb1 As New TextBlock()
        tb1.Text = author
        Dim tb2 As New TextBlock()
        tb2.Text = body
    
        Const MarginWidth As Integer = 8
        Me.commentGrid = New Grid()
        Me.commentGrid.RowDefinitions.Add(New RowDefinition())
        Me.commentGrid.RowDefinitions.Add(New RowDefinition())
        Dim cEdge As New ColumnDefinition()
        cEdge.Width = New GridLength(MarginWidth)
        Dim cEdge2 As New ColumnDefinition()
        cEdge2.Width = New GridLength(MarginWidth)
        Me.commentGrid.ColumnDefinitions.Add(cEdge)
        Me.commentGrid.ColumnDefinitions.Add(New ColumnDefinition())
        Me.commentGrid.ColumnDefinitions.Add(cEdge2)
    
        Dim rect As New System.Windows.Shapes.Rectangle()
        rect.RadiusX = 6
        rect.RadiusY = 3
        rect.Fill = brush
        rect.Stroke = Brushes.Green
    
        Dim inf As New Size(Double.PositiveInfinity, Double.PositiveInfinity)
        tb1.Measure(inf)
        tb2.Measure(inf)
        Dim middleWidth As Double = Math.Max(tb1.DesiredSize.Width, tb2.DesiredSize.Width)
        Me.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)
        Me.commentGrid.Children.Add(rect)
        Me.commentGrid.Children.Add(tb1)
        Me.commentGrid.Children.Add(tb2)
    
        Canvas.SetLeft(Me.commentGrid, Math.Max(viewRightEdge - Me.commentGrid.Width - 20.0, textRightEdge + 20.0))
        Canvas.SetTop(Me.commentGrid, textGeometry.GetRenderBounds(solidPen).Top)
    
        Me.Children.Add(Me.commentGrid)
    End Sub
    
    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 implementar um OnRender o manipulador de eventos que desenha o adorno.

    Protected Overrides Sub OnRender(ByVal dc As DrawingContext)
        MyBase.OnRender(dc)
        If Me.textGeometry IsNot Nothing Then
            dc.DrawGeometry(brush, solidPen, Me.textGeometry)
            Dim textBounds As Rect = Me.textGeometry.GetRenderBounds(solidPen)
            Dim p1 As New Point(textBounds.Right, textBounds.Bottom)
            Dim p2 As New Point(Math.Max(Canvas.GetLeft(Me.commentGrid) - 20.0, p1.X), p1.Y)
            Dim p3 As New Point(Math.Max(Canvas.GetLeft(Me.commentGrid), p1.X), (Canvas.GetTop(Me.commentGrid) + p1.Y) * 0.5)
            dc.DrawLine(dashPen, p1, p2)
            dc.DrawLine(dashPen, p2, p3)
        End If 
    End Sub
    
    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);
        }
    }
    

Adicionando um IWpfTextViewCreationListener

O IWpfTextViewCreationListener é uma parte de componente MEF que pode ser usado para escutar para exibir eventos de criação.

Para adicionar um IWpfTextViewCreationListener

  1. Adicionar um arquivo de classe ao projeto CommentAdornmentTest e o nome de conector.

  2. Adicione o seguinte using instruções.

    Imports System.ComponentModel.Composition
    Imports Microsoft.VisualStudio.Text.Editor
    Imports Microsoft.VisualStudio.Utilities
    
    using System.ComponentModel.Composition;
    using Microsoft.VisualStudio.Text.Editor;
    using Microsoft.VisualStudio.Utilities;
    
  3. Declara uma classe que implementa IWpfTextViewCreationListener e exportá-lo com um ContentTypeAttribute de "texto" e um TextViewRoleAttribute 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ários. Portanto, quase todas as modo de exibição de texto que é criado será desse tipo. O atributo de função do modo de exibição de texto Especifica o tipo de modo de exibição de texto ao qual o componente se aplica. Geralmente, as funções de modo de exibição de texto do documento mostram o texto que é composto de linhas e é armazenado em um arquivo.

    <Export(GetType(IWpfTextViewCreationListener)), ContentType("text"), TextViewRole(PredefinedTextViewRoles.Document)>
    Public NotInheritable Class Connector
        Implements IWpfTextViewCreationListener
    
    [Export(typeof(IWpfTextViewCreationListener))]
    [ContentType("text")]
    [TextViewRole(PredefinedTextViewRoles.Document)]
    public sealed class Connector : IWpfTextViewCreationListener
    
  4. Implementar a TextViewCreated , de modo que ele chama estática do método Create() evento da CommentAdornmentManager.

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

    Public Shared Sub Execute(ByVal host As IWpfTextViewHost)
        Dim view As IWpfTextView = host.TextView
        'Add a comment on the selected text. 
        If Not view.Selection.IsEmpty Then 
            'Get the provider for the comment adornments in the property bag of the view. 
            Dim provider As CommentAdornmentProvider = view.Properties.GetProperty(Of CommentAdornmentProvider)(GetType(CommentAdornmentProvider))
    
            'Add some arbitrary author and comment text. 
            Dim author As String = System.Security.Principal.WindowsIdentity.GetCurrent().Name
            Dim comment As String = "Four score...." 
    
            'Add the comment adornment using the provider.
            provider.Add(view.Selection.SelectedSpans(0), author, comment)
        End If 
    End Sub
    
    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);
        }
    }
    

A definição de uma camada de adorno

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

Para definir uma camada de adorno

  • No Connector classe declarar um campo público do tipo AdornmentLayerDefinition e exportá-lo com um NameAttribute que especifica um nome exclusivo para a camada de adorno e um OrderAttribute que define a relação de ordem z dessa camada de adorno para as outras camadas de modo de exibição de texto (texto, cursor e seleção).

    <Export(GetType(AdornmentLayerDefinition)), Name("CommentAdornmentLayer"), Order(After:=PredefinedAdornmentLayers.Selection, Before:=PredefinedAdornmentLayers.Text)>
    Public commentLayerDefinition As AdornmentLayerDefinition
    
    [Export(typeof(AdornmentLayerDefinition))]
    [Name("CommentAdornmentLayer")]
    [Order(After = PredefinedAdornmentLayers.Selection, Before = PredefinedAdornmentLayers.Text)]
    public AdornmentLayerDefinition commentLayerDefinition;
    

Fornecendo o comentário ornamentos

Quando você define um adorno, também implemente um provedor de adorno do comentário e um gerente de adorno do comentário. O provedor de adorno comentário mantém uma lista dos ornamentos do comentário, ouve Changed eventos no buffer de texto subjacente e exclusões comentário ornamentos quando o texto de base é excluído.

Para adicionar o provedor de adorno do comentário

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

  2. Adicione o seguinte using instruções.

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

    Friend Class CommentAdornmentProvider
    
    internal class CommentAdornmentProvider
    
  4. Adicione campos privados para o buffer de texto e a lista de ornamentos de comentário relacionadas ao buffer.

    Private buffer As ITextBuffer
    Private comments As IList(Of CommentAdornment) = New List(Of CommentAdornment)()
    
    private ITextBuffer buffer;
    private IList<CommentAdornment> comments = new List<CommentAdornment>();
    
  5. Adicionar um construtor para CommentAdornmentProvider. Este construtor deve ter acesso privado porque o provedor é instanciado com o Create() método. O construtor adiciona o OnBufferChanged o manipulador de eventos para o Changed evento.

    Private Sub New(ByVal buffer As ITextBuffer)
        Me.buffer = buffer
        'listen to the Changed event so we can react to deletions. 
        AddHandler Me.buffer.Changed, AddressOf OnBufferChanged
    End Sub
    
    private CommentAdornmentProvider(ITextBuffer buffer)
    {
        this.buffer = buffer;
        //listen to the Changed event so we can react to deletions. 
        this.buffer.Changed += OnBufferChanged;
    }
    
  6. Adicionar o Create() método.

    Public Shared Function Create(ByVal view As IWpfTextView) As CommentAdornmentProvider
        Return view.Properties.GetOrCreateSingletonProperty(Of CommentAdornmentProvider)(Function() New CommentAdornmentProvider(view.TextBuffer))
    End Function
    
    public static CommentAdornmentProvider Create(IWpfTextView view)
    {
        return view.Properties.GetOrCreateSingletonProperty<CommentAdornmentProvider>(delegate { return new CommentAdornmentProvider(view.TextBuffer); });
    }
    
  7. Adicionar o Detach() método.

    Public Sub Detach()
        If Me.buffer IsNot Nothing Then 
            'remove the Changed listener 
            RemoveHandler Me.buffer.Changed, AddressOf OnBufferChanged
            Me.buffer = Nothing 
        End If 
    End Sub
    
    public void Detach()
    {
        if (this.buffer != null)
        {
            //remove the Changed listener 
            this.buffer.Changed -= OnBufferChanged;
            this.buffer = null;
        }
    }
    
  8. Adicionar o OnBufferChanged manipulador de eventos.

    Private Sub OnBufferChanged(ByVal sender As Object, ByVal e As TextContentChangedEventArgs)
        '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. 
        Dim keptComments As IList(Of CommentAdornment) = New List(Of CommentAdornment)(Me.comments.Count)
    
        For Each comment As CommentAdornment In Me.comments
            Dim span As Span = comment.Span.GetSpan(e.After)
            'if a comment does not span at least one character, its text was deleted. 
            If span.Length <> 0 Then
                keptComments.Add(comment)
            End If 
        Next comment
    
        Me.comments = keptComments
    End Sub
    
    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. Adicionar uma declaração para um CommentsChanged evento.

    Public Event CommentsChanged As EventHandler(Of CommentsChangedEventArgs)
    
    public event EventHandler<CommentsChangedEventArgs> CommentsChanged;
    
  10. Criar um Add() método para adicionar o adorno.

    Public Sub Add(ByVal span As SnapshotSpan, ByVal author As String, ByVal text As String)
        If span.Length = 0 Then 
            Throw New ArgumentOutOfRangeException("span")
        End If 
        If author Is Nothing Then 
            Throw New ArgumentNullException("author")
        End If 
        If text Is Nothing Then 
            Throw New ArgumentNullException("text")
        End If 
    
        'Create a comment adornment given the span, author and text. 
        Dim comment As New CommentAdornment(span, author, text)
    
        'Add it to the list of comments. 
        Me.comments.Add(comment)
    
        'Raise the changed event. 
        Dim commentsChanged As EventHandler(Of CommentsChangedEventArgs) = Me.CommentsChangedEvent
        If CommentsChangedEvent IsNot Nothing Then
            CommentsChangedEvent(Me, New CommentsChangedEventArgs(comment, Nothing))
        End If 
    End Sub
    
    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. Adicionar um RemoveComments() método.

    Public Sub RemoveComments(ByVal span As SnapshotSpan)
        Dim commentsChanged As EventHandler(Of CommentsChangedEventArgs) = Me.CommentsChangedEvent
    
        'Get a list of all the comments that are being kept  
        Dim keptComments As IList(Of CommentAdornment) = New List(Of CommentAdornment)(Me.comments.Count)
    
        For Each comment As CommentAdornment In Me.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) Then 
                'Raise the change event to delete this comment. 
                If CommentsChangedEvent IsNot Nothing Then
                    CommentsChangedEvent(Me, New CommentsChangedEventArgs(Nothing, comment))
                End If 
            Else
                keptComments.Add(comment)
            End If 
        Next comment
    
        Me.comments = keptComments
    End Sub
    
    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. Adicionar um GetComments() método que retorna todos os comentários em um intervalo determinado snapshot.

    Public Function GetComments(ByVal span As SnapshotSpan) As Collection(Of CommentAdornment)
        Dim overlappingComments As IList(Of CommentAdornment) = New List(Of CommentAdornment)()
        For Each comment As CommentAdornment In Me.comments
            If comment.Span.GetSpan(span.Snapshot).OverlapsWith(span) Then
                overlappingComments.Add(comment)
            End If 
        Next comment
    
        Return New Collection(Of CommentAdornment)(overlappingComments)
    End Function
    
    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. Adicionar uma classe chamada CommentsChangedEventArgs, da seguinte forma.

    Friend Class CommentsChangedEventArgs
        Inherits EventArgs
        Public ReadOnly CommentAdded As CommentAdornment
        Public ReadOnly CommentRemoved As CommentAdornment
    
        Public Sub New(ByVal added As CommentAdornment, ByVal removed As CommentAdornment)
            Me.CommentAdded = added
            Me.CommentRemoved = removed
        End Sub 
    End Class
    
    internal class CommentsChangedEventArgs : EventArgs
    {
        public readonly CommentAdornment CommentAdded;
    
        public readonly CommentAdornment CommentRemoved;
    
        public CommentsChangedEventArgs(CommentAdornment added, CommentAdornment removed)
        {
            this.CommentAdded = added;
            this.CommentRemoved = removed;
        }
    }
    

Gerenciando o comentário ornamentos

O Gerenciador de adornos comentário cria o adorno e o adiciona à camada de adorno. Ele ouve o LayoutChanged e Closed , de modo que ele pode mover ou excluir o adorno de eventos. Ele também ouve a CommentsChanged evento que é acionado pelo provedor de adorno de comentário quando comentários forem adicionados ou removidos.

Para gerenciar os ornamentos de comentário

  1. Adicionar um arquivo de classe ao projeto CommentAdornmentTest e o nome de CommentAdornmentManager.

  2. Adicione o seguinte using instruções.

    Imports System
    Imports System.Collections.Generic
    Imports System.Windows.Media
    Imports Microsoft.VisualStudio.Text
    Imports Microsoft.VisualStudio.Text.Editor
    Imports Microsoft.VisualStudio.Text.Formatting
    
    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. Adicionar uma classe chamada CommentAdornmentManager.

    Friend Class CommentAdornmentManager
    
    internal class CommentAdornmentManager
    
  4. Adicione alguns campos privados.

    Private ReadOnly view As IWpfTextView
    Private ReadOnly layer As IAdornmentLayer
    Private ReadOnly provider As CommentAdornmentProvider
    
    private readonly IWpfTextView view;
    private readonly IAdornmentLayer layer;
    private readonly CommentAdornmentProvider provider;
    
  5. Adicionar um construtor que assina o Gerenciador para a LayoutChanged e Closed eventos e também para o CommentsChanged evento. O construtor é particular, porque o Gerenciador é instanciado com estática Create() método.

    Private Sub New(ByVal view As IWpfTextView)
        Me.view = view
        AddHandler Me.view.LayoutChanged, AddressOf OnLayoutChanged
        AddHandler Me.view.Closed, AddressOf OnClosed
    
        Me.layer = view.GetAdornmentLayer("CommentAdornmentLayer")
    
        Me.provider = CommentAdornmentProvider.Create(view)
        AddHandler Me.provider.CommentsChanged, AddressOf OnCommentsChanged
    End Sub
    
    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. Adicionar o Create() método que obtém um provedor ou cria um se necessário.

    Public Shared Function Create(ByVal view As IWpfTextView) As CommentAdornmentManager
        Return view.Properties.GetOrCreateSingletonProperty(Of CommentAdornmentManager)(Function() New CommentAdornmentManager(view))
    End Function
    
    public static CommentAdornmentManager Create(IWpfTextView view)
    {
        return view.Properties.GetOrCreateSingletonProperty<CommentAdornmentManager>(delegate { return new CommentAdornmentManager(view); });
    }
    
  7. Adicionar o CommentsChanged manipulador.

    Private Sub OnCommentsChanged(ByVal sender As Object, ByVal e As CommentsChangedEventArgs)
        'Remove the comment (when the adornment was added, the comment adornment was used as the tag). 
        If e.CommentRemoved IsNot Nothing Then 
            Me.layer.RemoveAdornmentsByTag(e.CommentRemoved)
        End If 
    
        'Draw the newly added comment (this will appear immediately: the view does not need to do a layout). 
        If e.CommentAdded IsNot Nothing Then 
            Me.DrawComment(e.CommentAdded)
        End If 
    End Sub
    
    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. Adicionar o Closed manipulador.

    Private Sub OnClosed(ByVal sender As Object, ByVal e As EventArgs)
        Me.provider.Detach()
        RemoveHandler Me.view.LayoutChanged, AddressOf OnLayoutChanged
        RemoveHandler Me.view.Closed, AddressOf OnClosed
    End Sub
    
    private void OnClosed(object sender, EventArgs e)
    {
        this.provider.Detach();
        this.view.LayoutChanged -= OnLayoutChanged;
        this.view.Closed -= OnClosed;
    }
    
  9. Adicionar o LayoutChanged manipulador.

    Private Sub OnLayoutChanged(ByVal sender As Object, ByVal e As TextViewLayoutChangedEventArgs)
        'Get all of the comments that intersect any of the new or reformatted lines of text. 
        Dim newComments As New List(Of 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. 
        For Each span As Span In e.NewOrReformattedSpans
            newComments.AddRange(Me.provider.GetComments(New SnapshotSpan(Me.view.TextSnapshot, span)))
        Next 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(Function(a As CommentAdornment, b As CommentAdornment) a.GetHashCode().CompareTo(b.GetHashCode()))
    
        Dim lastComment As CommentAdornment = Nothing 
        For Each comment As CommentAdornment In newComments
            If comment IsNot lastComment Then
                lastComment = comment
                Me.DrawComment(comment)
            End If 
        Next comment
    End Sub
    
    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 particular que desenha o comentário.

    Private Sub DrawComment(ByVal comment As CommentAdornment)
        Dim span As SnapshotSpan = comment.Span.GetSpan(Me.view.TextSnapshot)
        Dim g As Geometry = Me.view.TextViewLines.GetMarkerGeometry(span)
    
        If g IsNot Nothing Then 
            'Find the rightmost coordinate of all the lines that intersect the adornment. 
            Dim maxRight As Double = 0.0
            For Each line As ITextViewLine In Me.view.TextViewLines.GetTextViewLinesIntersectingSpan(span)
                maxRight = Math.Max(maxRight, line.Right)
            Next line
    
            'Create the visualization. 
            Dim block As New CommentBlock(maxRight, Me.view.ViewportRight, g, comment.Author, comment.Text)
    
            'Add it to the layer. 
            Me.layer.AddAdornment(span, comment, block)
        End If 
    End Sub
    
    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);
        }
    }
    

Usando 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 ao implementar a MenuItemCallback o VSPackage método.

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

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

    • Microsoft.VisualStudio.TextManager.Interop

    • Microsoft.VisualStudio.Editor

    • Microsoft.VisualStudio.Text.UI.WPF

  2. Adicione uma referência ao projeto CommentAdornmentTest.

  3. Abra o arquivo MenuCommandTestPackage.

  4. Adicione o seguinte using instruções.

    Imports Microsoft.VisualStudio.TextManager.Interop
    Imports Microsoft.VisualStudio.Text.Editor
    Imports Microsoft.VisualStudio.Editor
    Imports CommentAdornmentTest
    
    using Microsoft.VisualStudio.TextManager.Interop;
    using Microsoft.VisualStudio.Text.Editor;
    using Microsoft.VisualStudio.Editor;
    using CommentAdornmentTest;
    
  5. No MenuItemCallback método, remover o código existente.

  6. Adicione código para obter o modo de exibição ativo. Você deve obter o SVsTextManager do shell para obter a ativa Visual Studio IVsTextView.

    Dim txtMgr As IVsTextManager = CType(GetService(GetType(SVsTextManager)), IVsTextManager)
    Dim vTextView As IVsTextView = Nothing 
    Dim mustHaveFocus As Integer = 1
    txtMgr.GetActiveView(mustHaveFocus, Nothing, vTextView)
    
    IVsTextManager txtMgr = (IVsTextManager)GetService(typeof(SVsTextManager));
    IVsTextView vTextView = null;
    int mustHaveFocus = 1;
    txtMgr.GetActiveView(mustHaveFocus, null, out vTextView);
    
  7. Se este modo de exibição de texto é uma instância de um modo de exibição de texto do editor, você poderá lançá-la para o IVsUserData interface e, em seguida, obter o IWpfTextViewHost e seu associado IWpfTextView.

    Dim userData As IVsUserData = TryCast(vTextView, IVsUserData)
    If userData Is Nothing Then
        Console.WriteLine("No text view is currently open")
        Return 
    End If 
    Dim viewHost As IWpfTextViewHost
    Dim holder As Object 
    Dim guidViewHost As Guid = DefGuidList.guidIWpfTextViewHost
    userData.GetData(guidViewHost, holder)
    viewHost = CType(holder, IWpfTextViewHost)
    
    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;
    
  8. Use o IWpfTextViewHost para chamar o Connector.Execute() método, que obtém o provedor de adorno do comentário e adiciona o adorno.

    Connector.Execute(viewHost)
    
    Connector.Execute(viewHost);
    

Criar e testar o código

Para testar esse código, crie a solução de MenuCommand e execute-o na instância experimental.

Para criar e testar a solução MenuCommand

  1. Crie a solução. Ao executar este projeto no depurador, uma segunda instância do Visual Studio é instanciada.

  2. Criar um arquivo de texto. Digite algum texto e, em seguida, selecione-o.

  3. Sobre o Ferramentas menu, clique em Adicionar adorno. Um balão deve ser exibido no lado direito da janela de texto e deve conter o texto que se parece com o texto a seguir.

    Seu nome de usuário

    Fourscore…

Consulte também

Tarefas

Passo a passo: Vinculação a um tipo de conteúdo a uma extensão de nome de arquivo