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.
![]() |
---|
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 :
bajo extensibilidad de Visual Basic. El idioma predeterminado del proyecto es Visual Basic.
bajo extensibilidad de C#. El lenguaje predeterminado del proyecto es C#.
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
Crear un paquete de Visual Studio y denomínelo MenuCommandTest. Haga clic en Aceptar.
En la página del asistente, haga clic en Siguiente.
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.
En la página de información básica de VSPackage , en nombre de VSPackageMenuCommandescriba, y haga clic en Siguiente.
En la página de Opciones de selección de VSPackage , Comando de menú seleccione y haga clic en Siguiente.
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.
En la página de Opciones location de la prueba , desactive ambas opciones y haga clic en Finalizar.
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.
Abra el archivo source.extension.vsixmanifest del editor de Manifiesto VSIX. Debe tener una fila de Content para un Paquete denominado MenuCommand.
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
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.
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.
Abra las propiedades del proyecto y seleccione la página de Firma .
Seleccione Firmar el ensamblado.
En Elija un archivo de clave de nombre seguro, seleccione el archivo Key.snk generado para el ensamblado de MenuCommandTest.
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.
![]() |
---|
Para obtener más información sobre MEF, vea Managed Extensibility Framework (MEF). |
Para hacer referencia al componente MEF en el proyecto de VSPackage
En el proyecto de MenuCommandTest, abra el archivo source.extension.vsixmanifest del editor de Manifiesto VSIX.
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.
Guarde y cierre el archivo source.extension.vsixmanifest.
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
en el proyecto de CommentAdornmentTest, elimine los archivos existentes de la clase.
Agregue un nuevo archivo de clase y denomínelo CommentAdornment.
Agregue la siguiente instrucción de using .
Imports Microsoft.VisualStudio.Text
using Microsoft.VisualStudio.Text;
agregue una clase denominada CommentAdornment.
Friend Class CommentAdornment
internal class CommentAdornment
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;
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
Cree una clase del proyecto de CommentAdornmentTest y denomínela CommentBlock.
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;
Haga que la clase de CommentBlock heredar de Canvas.
Friend Class CommentBlock Inherits Canvas
internal class CommentBlock : Canvas
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;
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); }
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
Agregue un archivo de clase al proyecto de CommentAdornmentTest y denomínelo conector.
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;
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
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); }
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
Agregue un nuevo archivo de clase al proyecto de CommentAdornmentTest y denomínelo CommentAdornmentProvider.
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;
agregue una clase denominada CommentAdornmentProvider.
Friend Class CommentAdornmentProvider
internal class CommentAdornmentProvider
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>();
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; }
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); }); }
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; } }
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; }
Agregue una declaración de un evento de CommentsChanged .
Public Event CommentsChanged As EventHandler(Of CommentsChangedEventArgs)
public event EventHandler<CommentsChangedEventArgs> CommentsChanged;
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)); }
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; }
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); }
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
Agregue un archivo de clase al proyecto de CommentAdornmentTest y denomínelo CommentAdornmentManager.
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;
agregue una clase denominada CommentAdornmentManager.
Friend Class CommentAdornmentManager
internal class CommentAdornmentManager
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;
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; }
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); }); }
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); }
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; }
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); } } }
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
Agregue las referencias siguientes al proyecto de MenuCommandTest:
Microsoft.VisualStudio.TextManager.Interop
Microsoft.VisualStudio.Editor
f de Microsoft.VisualStudio.Text.UI.Wp
Agregue una referencia al proyecto de CommentAdornmentTest.
Abra el archivo de MenuCommandTestPackage.
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;
en el método de MenuItemCallback , quite el código existente.
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);
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;
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
Compile la solución. Al ejecutar este proyecto en el depurador, una segunda instancia de Visual Studio se crea instancias.
Crear un archivo de texto Escriba texto a continuación seleccione.
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