Compartir a través de


tutorial: Mediante un comando de shell con una extensión del editor

De un Paquete, puede agregar características como comandos de menú al editor. Este tutorial muestra cómo agregar un elemento gráfico a una vista de texto en el editor invocar un comando de menú.

Este tutorial muestra el uso de un VSPackage así como una parte administrada del componente del marco (MEF) de extensibilidad de. Debe utilizar un VSPackage para registrar el comando de menú con el shell de Visual Studio, y puede usar el comando para tener acceso a la parte componente MEF.

Requisitos previos

Para completar este tutorial, debe instalar Visual Studio 2010 SDK.

NotaNota

Para obtener más información sobre el SDK de Visual Studio, vea Información general de Visual Studio que extiende.Para averiguar cómo descargar el SDK de Visual Studio, vea Centro para desarrolladores de extensibilidad de Visual Studio en el sitio web de MSDN.

Ubicaciones de la plantilla de proyecto paquete de Visual Studio

La plantilla de proyecto paquete de Visual Studio se puede encontrar en tres ubicaciones diferentes en el diálogo de Nuevo proyecto :

  1. bajo extensibilidad de Visual Basic. El idioma predeterminado del proyecto es Visual Basic.

  2. bajo extensibilidad de C#. El lenguaje predeterminado del proyecto es C#.

  3. En otra extensibilidad de los tipos de proyecto. El idioma predeterminado del proyecto es C++

crear un comando de menú VSPackage

Cree un Paquete que coloque un comando de menú denominado Agregue el elemento gráfico en el menú de Herramientas .

para crear un comando de menú VSPackage

  1. Crear un paquete de Visual Studio y denomínelo MenuCommandTest. Haga clic en Aceptar.

  2. En la página del asistente, haga clic en Siguiente.

  3. En la página de Seleccione un lenguaje de programación , Visual Basic seleccione o Visual c#, asegúrese de que Genera un nuevo archivo de clave para firmar el ensamblado está seleccionado, y haga clic en Siguiente.

  4. En la página de información básica de VSPackage , en nombre de VSPackageMenuCommandescriba, y haga clic en Siguiente.

  5. En la página de Opciones de selección de VSPackage , Comando de menú seleccione y haga clic en Siguiente.

  6. En la página de opciones de comando , en nombre de comandoAgregue el elemento gráficoconversión boxing. en el cuadro de Identificador de comando , cmdidAddAdornmentescrito. Haga clic en Siguiente.

  7. En la página de Opciones location de la prueba , desactive ambas opciones y haga clic en Finalizar.

  8. una solución denominada MenuCommandTest se abre. El archivo de MenuCommandTestPackage tiene el código que crea el comando de menú y lo pondrá en el menú de Herramientas . En este punto, el comando simplemente produce un cuadro de mensaje que se mostrará. Pasos posteriores muestran cómo cambiar esto para mostrar el elemento gráfico de comentario.

  9. Abra el archivo source.extension.vsixmanifest del editor de Manifiesto VSIX. Debe tener una fila de Content para un Paquete denominado MenuCommand.

  10. Guarde y cierre el archivo Source.extension.vsixmanifest.

Agregar una extensión MEF a la solución de VSPackage

Para agregar la extensión MEF a la solución de VSPackage

  1. En el Explorador de soluciones, haga clic con el botón secundario en el nodo de la solución, después haga clic en Agregar y, a continuación, en Nuevo proyecto. En el cuadro de diálogo de Agregar nuevo proyecto , extensibilidad de clic bajo Visual Basic o Visual c#, entonces EditorClassifier. Asigne al proyecto CommentAdornmentTest.

  2. Dado que este proyecto interactuará con el ensamblado con nombre seguro de VSPackage, debe firmar el ensamblado. Se puede reutilizar el archivo de clave creado para el ensamblado de VSPackage.

    1. Abra las propiedades del proyecto y seleccione la página de Firma .

    2. Seleccione Firmar el ensamblado.

    3. En Elija un archivo de clave de nombre seguro, seleccione el archivo Key.snk generado para el ensamblado de MenuCommandTest.

    4. Guarde el proyecto.

Consultar la extensión MEF en el proyecto de VSPackage

Como está agregando un componente MEF al Paquete, debe especificar ambos tipos de contenido en el manifiesto.

NotaNota

Para obtener más información sobre MEF, vea Managed Extensibility Framework (MEF).

Para hacer referencia al componente MEF en el proyecto de VSPackage

  1. En el proyecto de MenuCommandTest, abra el archivo source.extension.vsixmanifest del editor de Manifiesto VSIX.

  2. En Contenido que trata, haga clic en Agregue contenido. En la lista de Tipo de contenido seleccione , seleccione Componente MEF. En Seleccione un origen, seleccione CommentAdornmentTest.

  3. Guarde y cierre el archivo source.extension.vsixmanifest.

  4. Agregue una referencia al proyecto de CommentAdornmentTest.

Definición de un elemento gráfico de comentario

El elemento gráfico propio de comentario consta de ITrackingSpan que sigue al texto seleccionado, y algunas cadenas que representan el autor y la descripción de texto.

Para definir un elemento gráfico de comentario

  1. en el proyecto de CommentAdornmentTest, elimine los archivos existentes de la clase.

  2. Agregue un nuevo archivo de clase y denomínelo CommentAdornment.

  3. Agregue la siguiente instrucción de using .

    Imports Microsoft.VisualStudio.Text
    
    using Microsoft.VisualStudio.Text;
    
  4. agregue una clase denominada CommentAdornment.

    Friend Class CommentAdornment
    
    internal class CommentAdornment
    
  5. agregue tres campos a la clase de CommentAdornment para ITrackingSpan, el autor, y la descripción.

    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. Agregue un constructor que inicializa los 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;
    }
    

Crear un elemento de Visual para el elemento gráfico

También debe definir un elemento visual para el elemento gráfico. Para este tutorial, defina un control que hereda de la clase (WPF) Canvasde Windows Presentation Foundation.

Para crear un elemento visual para el elemento gráfico

  1. Cree una clase del proyecto de CommentAdornmentTest y denomínela CommentBlock.

  2. Agregue las siguientes instrucciones de using .

    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. Haga que la clase de CommentBlock heredar de Canvas.

    Friend Class CommentBlock
        Inherits Canvas
    
    internal class CommentBlock : Canvas
    
  4. Agregue algunos campos privados para definir los aspectos visuales del elemento gráfico.

    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. Agregue un constructor que defina el elemento gráfico de comentario y agregue texto pertinente.

    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. También implementa un controlador de eventos de OnRender que dibuja el elemento gráfico.

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

agregar un IWpfTextViewCreationListener

IWpfTextViewCreationListener es una parte MEF que puede utilizar para realizar escuchas para ver los eventos de creación.

para agregar un IWpfTextViewCreationListener

  1. Agregue un archivo de clase al proyecto de CommentAdornmentTest y denomínelo conector.

  2. Agregue las siguientes instrucciones de using .

    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. Declare una clase que implemente IWpfTextViewCreationListener y exportela con ContentTypeAttribute “text” y TextViewRoleAttribute de Document. El atributo de tipo de contenido especifica la clase de contenido a la que el componente se aplica. El tipo de texto es el tipo base para todos los tipos de archivos de no-binario. Por consiguiente, casi cada vista de texto que se crea se de este tipo. De la vista de texto el rol especifica la clase de vista de texto a la que el componente se aplica. Los roles de la vista de texto del documento muestra normalmente el texto que se compone de líneas y se almacena en un archivo.

    <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. Implemente el método de TextViewCreated de para que llame al evento estático de Create() de 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. Agregue un método que se puede utilizar para ejecutar el 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);
        }
    }
    

Definición de un nivel del elemento gráfico

Para agregar una nueva opción gráfica, debe definir un nivel del elemento gráfico.

Para definir un nivel del elemento gráfico

  • En la clase de Connector declare un campo público de AdornmentLayerDefinition escrito y exportelo con NameAttribute que especifique un nombre único para el nivel del elemento gráfico y OrderAttribute que define la relación del orden Z de este nivel del elemento gráfico a los demás niveles de vista de texto (texto, símbolo de intercalación, y selección).

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

Proporcionar a los elementos gráficos de comentario

Cuando se define un elemento gráfico, también implementa un proveedor del elemento gráfico de comentario y un administrador del elemento gráfico de comentario. El proveedor del elemento gráfico de comentario mantiene una lista de elementos gráficos comment, escucha a Changed eventos en el búfer de texto subyacente, y elimina elementos gráficos de comentario cuando se elimine el texto subyacente.

Para agregar el proveedor del elemento gráfico de comentario

  1. Agregue un nuevo archivo de clase al proyecto de CommentAdornmentTest y denomínelo CommentAdornmentProvider.

  2. Agregue las siguientes instrucciones de using .

    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. agregue una clase denominada CommentAdornmentProvider.

    Friend Class CommentAdornmentProvider
    
    internal class CommentAdornmentProvider
    
  4. Agregue campos privados para el búfer de texto y la lista de elementos gráficos de comentario relacionadas con el búfer.

    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. Agregue un constructor para CommentAdornmentProvider. Este constructor debe tener acceso privado porque el proveedor crea instancias por el método de Create() . El constructor agrega el controlador de eventos de OnBufferChanged al evento de Changed .

    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. agregue el método de Create() .

    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. agregue el método de Detach() .

    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. Agregue el controlador de eventos de OnBufferChanged .

    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. Agregue una declaración de un evento de CommentsChanged .

    Public Event CommentsChanged As EventHandler(Of CommentsChangedEventArgs)
    
    public event EventHandler<CommentsChangedEventArgs> CommentsChanged;
    
  10. Cree un método de Add() para agregar el elemento gráfico.

    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. Agregue un método RemoveComments().

    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. Agregue un método de GetComments() que devuelve todos los comentarios de un intervalo determinado de la instantánea.

    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. agregue una clase denominada CommentsChangedEventArgs, como sigue.

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

Administrar elementos gráficos de comentario

El administrador del elemento gráfico de comentario crea la opción gráfica y la agrega al nivel del elemento gráfico. Escucha los eventos de LayoutChanged y de Closed para poder mover o eliminar el elemento gráfico. También escucha a CommentsChanged el evento iniciado por el proveedor del elemento gráfico de comentario cuando se agregan o se quitan los comentarios.

Para administrar los elementos gráficos de comentario

  1. Agregue un archivo de clase al proyecto de CommentAdornmentTest y denomínelo CommentAdornmentManager.

  2. Agregue las siguientes instrucciones de using .

    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. agregue una clase denominada CommentAdornmentManager.

    Friend Class CommentAdornmentManager
    
    internal class CommentAdornmentManager
    
  4. agregue algunos 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. Agregue un constructor que se suscribe al administrador a los eventos de LayoutChanged y de Closed , y también al evento de CommentsChanged . El constructor es privado porque el método estático de Create() crea una instancia del administrador.

    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. Agregue el método de Create() que obtiene un proveedor o crea uno si es necesario.

    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. Agregue el controlador de CommentsChanged .

    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. Agregue el controlador de Closed .

    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. Agregue el controlador de LayoutChanged .

    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. Agregue el método privado que dibuja el comentario.

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

Mediante el comando de menú para agregar el elemento gráfico de comentario

Puede utilizar el comando de menú para crear un elemento gráfico de comentario implementando el método de MenuItemCallback de VSPackage.

Para utilizar el comando de menú para agregar el elemento gráfico de comentario

  1. Agregue las referencias siguientes al proyecto de MenuCommandTest:

    • Microsoft.VisualStudio.TextManager.Interop

    • Microsoft.VisualStudio.Editor

    • f de Microsoft.VisualStudio.Text.UI.Wp

  2. Agregue una referencia al proyecto de CommentAdornmentTest.

  3. Abra el archivo de MenuCommandTestPackage.

  4. Agregue las siguientes instrucciones de using .

    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. en el método de MenuItemCallback , quite el código existente.

  6. Agregue código para obtener la vista activa. Debe obtener SVsTextManager del shell de Visual Studio para obtener IVsTextViewactivo.

    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. Si esta vista de texto es una instancia de una vista de texto del editor, puede convertirla a la interfaz de IVsUserData y después obtener IWpfTextViewHost y su IWpfTextViewasociado.

    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. Utilice IWpfTextViewHost para llamar al método de Connector.Execute() , que obtiene el proveedor del elemento gráfico de comentario y agrega el elemento gráfico.

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

Compilar y probar el código

Para probar este código, compile la solución de MenuCommand y ejecútela en la instancia experimental.

para compilar y probar la solución de MenuCommand

  1. Compile la solución. Al ejecutar este proyecto en el depurador, una segunda instancia de Visual Studio se crea instancias.

  2. Crear un archivo de texto Escriba texto a continuación seleccione.

  3. En el menú de Herramientas , haga clic en Agregue el elemento gráfico. Un globo se debería mostrar a la derecha de la ventana de texto, y debe contener el texto que se parece al siguiente texto.

    TheUserName

    ochenta…

Vea también

Tareas

tutorial: vincular un tipo de contenido a una extensión de nombre de archivo