Freigeben über


Exemplarische Vorgehensweise: Verwenden eines Shellbefehls mit einer Editor-Erweiterung

Von einem VSPackage können Sie Funktionen, z. B. Menübefehle dem Editor hinzufügen. In dieser exemplarischen Vorgehensweise wird erläutert, wie ein Zusatzelement einer Textansicht in den Editor hinzugefügt werden, indem sie einen Menübefehl aufgerufen wird.

In dieser exemplarischen Vorgehensweise wird die Verwendung von VSPackages zusammen mit einer Komponente des Managed Extensibility Framework (MEF). Sie müssen ein VSPackage verwenden, um auf den Menübefehl mit der Visual Studio-Shell zu registrieren, und Sie können den Befehl verwenden, um den MEF-Komponenten-Teil zuzugreifen.

Vorbereitungsmaßnahmen

Zum Abschließen dieser exemplarischen Vorgehensweise müssen Sie Visual Studio 2010 SDKinstallieren.

Hinweis

Weitere Informationen über das Visual Studio-SDK finden Sie unter Erweitern von Visual Studio Overview.Um herauszufinden finden Sie unter wie das Visual Studio-SDK, auf Visual Studio Extensibility Developer Center der MSDN-Website herunterlädt.

Speicherorte für die Visual Studio-Paket-Projektvorlage

Die Visual Studio-Paket importieren kann in drei verschiedenen Stellen im Dialogfeld Neues Projekt gefunden werden:

  1. Klicken Sie unter Von Visual Basic-Erweiterbarkeit. Die Standardsprache des Projekts ist Visual Basic.

  2. Die C#-Erweiterbarkeit. Die Standardsprache ist C# des Projekts.

  3. Verwenden anderer Projekttyp-Erweiterbarkeit. Die Standardsprache des Projekts ist C++

Erstellen eines Menübefehls VSPackage

Erstellen Sie ein VSPackage, das einen Menübefehl, setzt der Zusatzelement hinzufügen auf das Menü Extras .

So erstellen Sie einen Menübefehl VSPackage

  1. Erstellen Sie ein Visual Studio-Paket, und nennen Sie es MenuCommandTest. Klicken Sie auf OK.

  2. Wählen Sie auf der Seite Willkommen auf Weiter.

  3. Wählen Sie auf der Seite Wählen Sie eine Programmiersprache aus. überprüfen oder Visual BasicVisual C#, ob Neue Schlüsseldatei für die Assemblysignierung generieren ausgewählt ist, und klicken Sie dann auf Weiter.

  4. Auf der Seite Grundlegende Informationen zum VSPackage in VSPackage-Name kastenähnliches MenuCommandund klicken Sie dann auf Weiter.

  5. Wählen Sie auf der Seite Optionen für das VSPackage auswählenMenübefehl , und klicken Sie dann auf Weiter.

  6. Auf der Seite Befehlsoptionen in Befehlsname kastenähnliches Fügen Sie ein Zusatzelement. Im Befehls-ID Feld Typ cmdidAddAdornment. Klicken Sie auf Next.

  7. Deaktivieren Sie auf der Seite Testoptionen aktivieren beide Optionen, und klicken Sie dann auf Fertig stellen.

  8. Eine Projektmappe mit dem Namen MenuCommandTest wird geöffnet. Die MenuCommandTestPackage-Datei enthält den Code, der den Menübefehl erstellen und auf das Menü Extras wird. An diesem Punkt wird der Befehl gegenwärtig ein Meldungsfeld angezeigt werden soll. In späteren Schritten wird erläutert, wie Sie dies ändern, um den Kommentar zusatzelement anzuzeigen.

  9. Öffnen Sie die Datei source.extension.vsixmanifest im VSIX-Manifest-Editor. Er sollte eine Content Zeile für MenuCommand VSPackages mit dem Namen aufweist.

  10. Speichern und schließen Sie die Source.extension.vsixmanifest-Datei.

Eine MEF-Erweiterung zur VSPackage-Projektmappe hinzu

Um die MEF-Erweiterung der VSPackage-Projektmappe hinzu

  1. Klicken Sie im Projektmappen-Explorer mit der rechten Maustaste auf den Projektmappenknoten, klicken Sie auf Hinzufügen und dann auf Neues Projekt. Im Neues Projekt hinzufügen Dialogfeld, in Visual Basic oder klicken Sie auf Erweiterungen mit **Visual C#**dann EditorClassifier. Nennen Sie das Projekt CommentAdornmentTest.

  2. Da dieses Projekt mit der stark-benannten VSPackage-Assembly interagiert, müssen Sie die Assembly signieren. Sie können die Schlüsseldatei erneut verwenden, die bereits für die VSPackage-Assembly erstellt wird.

    1. Öffnen Sie die Projekteigenschaften, und wählen Sie die Signierung Seite aus.

    2. Wählen Sie Assembly signierenaus.

    3. Die Schlüsseldatei mit starkem Namen auswählenwählen Sie die Key.snk-Datei aus, die für die MenuCommandTest-Assembly generiert wurde.

    4. Speichern Sie das Projekt.

Klicken Sie auf die MEF-Erweiterung im VSPackage-Projekt bezugnehmen

Da Sie eine MEF-Komponente in einem VSPackage hinzufügen, müssen Sie beide Arten von Inhalt im Manifest angeben.

Hinweis

Weitere Informationen über MEF finden Sie unter Managed Extensibility Framework (MEF).

So verweisen VSPackage-Projekt im MEF-Komponente

  1. Im MenuCommandTest-Projekt öffnen Sie die Datei source.extension.vsixmanifest im VSIX-Manifest-Editor.

  2. Die vorangehenden Inhalt , klicken Sie auf Inhalt hinzufügen. Auf der Inhaltstyp auswählen Liste ausgewähltes MEF-Komponente. Durch die Option Quelle auswählenCommentAdornmentTest.

  3. Speichern und schließen Sie die Datei source.extension.vsixmanifest.

  4. Fügen Sie einen Verweis auf das CommentAdornmentTest-Projekt hinzu.

Ein Kommentar-Zusatzelement definieren

Der Kommentar zusatzelement selbst besteht ITrackingSpan , das den markierten Text nachverfolgt, und einige von Zeichenfolgen, die den Autor und die Beschreibung des Texts darstellt.

So definieren zusatzelement ein Kommentar

  1. Im CommentAdornmentTest-Projekt löschen Sie die vorhandene Klassendateien.

  2. Fügen Sie eine neue Klassendatei hinzu, und nennen Sie sie CommentAdornment.

  3. Fügen Sie die folgende using-Anweisung hinzu.

    Imports Microsoft.VisualStudio.Text
    
    using Microsoft.VisualStudio.Text;
    
  4. Fügen Sie eine Klasse hinzu, die CommentAdornmentbenannt ist.

    Friend Class CommentAdornment
    
    internal class CommentAdornment
    
  5. Fügen Sie drei Felder der CommentAdornment-Klasse für ITrackingSpan, den Autor und die Beschreibung hinzu.

    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. Fügen Sie einen Konstruktor hinzu, der die Felder initialisiert.

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

Ein visuelles Element für das Zusatzelement erstellen

Sie müssen ein visuelles Element für das Zusatzelement definieren. In dieser exemplarischen Vorgehensweise definieren Sie ein Steuerelement, das von der Klasse CanvasWindows Presentation Foundation (WPF) erbt.

So erstellen Sie ein visuelles Element für das Zusatzelement erstellen

  1. Erstellen Sie eine Klasse im CommentAdornmentTest-Projekt, und nennen Sie diese CommentBlock.

  2. Fügen Sie die folgenden using-Anweisungen hinzu.

    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. Lassen Sie die CommentBlock-Klasse erben von Canvas.

    Friend Class CommentBlock
        Inherits Canvas
    
    internal class CommentBlock : Canvas
    
  4. Fügen Sie einige private Felder hinzu, um die visuellen Aspekte des Zusatzelements zu definieren.

    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. Fügen Sie einen Konstruktor hinzu, der dem Kommentar zusatzelement definiert und den entsprechenden Text hinzugefügt wird.

    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. Implementieren Sie außerdem einen OnRender-Ereignishandler, der das Zusatzelement zeichnet.

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

Ein IWpfTextViewCreationListener hinzu

IWpfTextViewCreationListener ist ein MEF-Komponenten-Teil, den Sie verwenden können, um zum Überwachen von Builds anzeigen.

So fügen Sie ein IWpfTextViewCreationListener hinzu

  1. Fügen Sie dem CommentAdornmentTest-Projekt eine Klassendatei hinzu, und nennen Sie sie Konnektor.

  2. Fügen Sie die folgenden using-Anweisungen hinzu.

    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. Deklarieren Sie eine Klasse, die IWpfTextViewCreationListener implementiert und exportieren Sie sie mit ContentTypeAttribute „des Texts“ und TextViewRoleAttribute von Document. Der Inhaltstyp Attribut gibt die Art des Inhalts an, für die die Komponente gilt. Die Textart ist der Basistyp für alle nicht binär Dateitypen. Daher ist fast jede erstellte Textansicht, für diesen Typ. Die Rolle der Text Attribut gibt die Art der Textansicht an, für die die Komponente gilt. Show-Text der Dokumenttext Bildlauf im Allgemeinen von den Zeilen besteht und in einer Datei gespeichert wird.

    <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. Implementieren Sie die TextViewCreated-Methode, damit das statische Create()-Ereignis CommentAdornmentManageraufruft.

    Public Sub TextViewCreated(ByVal textView As IWpfTextView) Implements IWpfTextViewCreationListener.TextViewCreated
        CommentAdornmentManager.Create(textView)
    End Sub
    
    public void TextViewCreated(IWpfTextView textView)
    {
        CommentAdornmentManager.Create(textView);
    }
    
  5. Fügen Sie eine Methode hinzu, die Sie verwenden können, um den Befehl auszuführen.

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

Eine Zusatzelement-Ebene definieren

Um ein neues Zusatzelement hinzuzufügen, müssen Sie eine Zusatzelementebene definieren.

So definieren Sie eine Zusatzelementebene

  • In der Connector-Klasse deklarieren Sie ein öffentliches Feld vom Typ AdornmentLayerDefinition und exportieren Sie es mit NameAttribute , das einen eindeutigen Namen für die Zusatzelementebene und OrderAttribute angibt, die das z-Reihenfolgen-Verhältnis der Zusatzelementebene der Text auf anderen Ebenen definiert (Text). Auswahl und der Einfügemarke

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

Bereitstellen von Kommentar-Zusatzelementen

Wenn Sie ein Zusatzelement definieren, implementieren Sie auch einen Kommentar einen Kommentar zusatzelement und zusatzelement Manager. Der Kommentar zusatzelement Anbieter verwaltet eine Liste von Kommentar zusatzelementen, lauscht auf Changed-Ereignissen im zugrunde liegenden Textpuffer und Löschvorgänge Kommentar zusatzelemente, wenn der zugrunde liegende Text gelöscht wird.

Um den Hersteller der zusatzelement Kommentar hinzu

  1. Fügen Sie dem CommentAdornmentTest-Projekt eine neue Klassendatei hinzu, und nennen Sie sie CommentAdornmentProvider.

  2. Fügen Sie die folgenden using-Anweisungen hinzu.

    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. Fügen Sie eine Klasse hinzu, die CommentAdornmentProviderbenannt ist.

    Friend Class CommentAdornmentProvider
    
    internal class CommentAdornmentProvider
    
  4. Fügen Sie private Felder für den Textpuffer und die Liste der zusatzelementen Kommentar hinzu, die dem Puffer verknüpft sind.

    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. Fügen Sie einen Konstruktor für CommentAdornmentProviderhinzu. Dieser Konstruktor sollte privaten Zugriff haben, da der Anbieter durch die Create()-Methode instanziiert wird. Der Konstruktor fügt den OnBufferChanged-Ereignishandler dem Changed-Ereignis hinzu.

    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. Fügen Sie die Create()-Methode hinzu.

    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. Fügen Sie die Detach()-Methode hinzu.

    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. Fügen Sie den OnBufferChanged-Ereignishandler hinzu.

    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. Fügen Sie eine Deklaration für ein CommentsChanged-Ereignis hinzu.

    Public Event CommentsChanged As EventHandler(Of CommentsChangedEventArgs)
    
    public event EventHandler<CommentsChangedEventArgs> CommentsChanged;
    
  10. Erstellen Sie eine Add()-Methode, um das Zusatzelement hinzuzufügen.

    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. Fügen Sie eine RemoveComments()-Methode hinzu.

    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. Fügen Sie eine GetComments()-Methode hinzu, die alle Kommentare in einer angegebenen Momentaufnahmespanne zurückgibt.

    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. Fügen Sie eine Klasse hinzu, die CommentsChangedEventArgswie folgt benannt ist.

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

Verwalten Kommentar-Zusatzelemente

Der Kommentar zusatzelement Manager stellt das Zusatzelement und fügt es der Zusatzelementebene hinzu. Sie Closed lauscht auf LayoutChanged und Ereignissen, damit sie das Zusatzelement verschieben oder löschen kann. Sie CommentsChanged außerdem überwacht, das von dem Ereignis zusatzelement den Kommentar Anbieter ausgelöst wird, wenn Kommentare hinzugefügt oder entfernt werden.

So verwalten zusatzelemente Kommentar

  1. Fügen Sie dem CommentAdornmentTest-Projekt eine Klassendatei hinzu, und nennen Sie sie CommentAdornmentManager.

  2. Fügen Sie die folgenden using-Anweisungen hinzu.

    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. Fügen Sie eine Klasse hinzu, die CommentAdornmentManagerbenannt ist.

    Friend Class CommentAdornmentManager
    
    internal class CommentAdornmentManager
    
  4. Fügen Sie einige private Felder hinzu.

    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. Fügen Sie einen Konstruktor, der den Manager für LayoutChanged und Closed-Ereignisse abonniert, sowie die dem CommentsChanged-Ereignis hinzu. Der Konstruktor ist privat, da der Manager von der statischen Create()-Methode instanziiert wird.

    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. Fügen Sie die Create()-Methode hinzu, die einen Anbieter abruft oder nach Bedarf erstellt.

    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. Fügen Sie den CommentsChanged-Handler hinzu.

    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. Fügen Sie den Closed-Handler hinzu.

    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. Fügen Sie den LayoutChanged-Handler hinzu.

    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. Fügen Sie die private Methode hinzu, die den Kommentar zeichnet.

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

Verwenden des Menübefehls, das hinzugefügt werden soll Kommentar-Zusatzelement

Sie können den Menübefehl verwenden, ein Kommentar zusatzelement erstellen, indem Sie die MenuItemCallback VSPackages Methode implementieren.

Um den Menübefehl verwenden, das zusatzelement Kommentar hinzu

  1. Fügen Sie dem MenuCommandTest-Projekt die folgenden Verweise hinzu:

    • Microsoft.VisualStudio.TextManager.Interop

    • Microsoft.VisualStudio.Editor

    • Microsoft.VisualStudio.Text.UI.Wp f

  2. Fügen Sie einen Verweis auf das CommentAdornmentTest-Projekt hinzu.

  3. Öffnen Sie die MenuCommandTestPackage-Datei.

  4. Fügen Sie die folgenden using-Anweisungen hinzu.

    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. In der MenuItemCallback-Methode entfernen Sie den vorhandenen Code.

  6. Fügen Sie Code hinzu, um die aktuelle Ansicht zu erhalten. Sie müssen SVsTextManager der Visual Studio-Shells abrufen, um aktive IVsTextViewabzurufen.

    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. Wenn diese Textansicht Text Editor eine Instanz einer Ansicht ist, können Sie sie mit der IVsUserData-Schnittstelle umwandeln und dann IWpfTextViewHost und sein zugeordnetes IWpfTextViewabrufen.

    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. Verwenden Sie IWpfTextViewHost , um die Connector.Execute()-Methode aufzurufen, die den Kommentar zusatzelement Textanbieter abruft und das Zusatzelement hinzugefügt wird.

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

Erstellen und Testen von Code

Um diesen Code zu testen, erstellen Sie die MenuCommand-Projektmappe und führen Sie sie in der experimentellen Instanz aus.

So erstellen und testen die MenuCommand-Projektmappe

  1. Erstellen Sie die Projektmappe. Wenn Sie dieses Projekt im Debugger ausführen, wird eine zweite Instanz von Visual Studio instanziiert.

  2. Erstellen einer Textdatei Geben Sie Text ein, und wählen Sie ihn dann aus.

  3. Zeigen Sie im Menü Extras Klicken Sie auf Zusatzelement hinzufügen. Eine Sprechblase sollte auf der rechten Seite des Textfensters angezeigt werden, und es sollte Text enthalten, der dem folgenden Text entspricht.

    TheUserName

    Achtzig…

Siehe auch

Aufgaben

Exemplarische Vorgehensweise: Verknüpfen einer Dateinamenerweiterung eines Inhaltstyps