从 VSPackage,可以添加功能 (如菜单命令到编辑器中。 本演练演示如何将修饰到编辑器的文本视图通过调用菜单命令。
本演练使用 managed extensibility framework 组成部分共同表明使用 (MEF) VSPackage。 必须使用 VSPackage 注册与 Visual Studio shell 的菜单命令,因此,您可以使用命令来访问 MEF 组件。
系统必备
若要完成本演练,您必须安装 Visual Studio 2010 SDK。
备注
有关 Visual Studio SDK 的更多信息,请参见 扩展 Visual Studio 概述。若要查找有关中所列如何下载 Visual Studio SDK,请 Visual Studio Extensibility Developer Center 参见 MSDN 网站上。
Visual Studio 包 " 项目模板的位置
Visual Studio 包 " 项目模板可以在 新项目 对话框的三个不同位置找到:
在 Visual Basic 扩展性下。 该项的默认语言是 Visual Basic。
在 C# 扩展性下。 该项目的默认语言是 C#。
在其他项下键入扩展性。 该项的默认语言是 C++
创建菜单命令、 VSPackage
创建在 工具 菜单将名为 添加修饰 的菜单命令的 VSPackage。
创建菜单命令、 VSPackage
创建 Visual Studio 包并将其命名为 MenuCommandTest。 单击**“确定”**。
在 " 欢迎 " 页上,单击 接下来。
在 选择一种编程语言 页上,选择的 Visual Basic 或 Visual C#,确保, 生成一个新密钥文件对程序集进行签名 选中,然后单击 接下来。
在 基本的 VSPackage 信息 页上,在 VSPackage 名称 框 MenuCommand,然后单击 接下来。
在 选择 VSPackage 选项 页上,选择的 菜单命令 然后单击 接下来。
在 命令选项 页上,在 命令名 框 添加修饰。 在 命令 ID 框中,键入 cmdidAddAdornment。 单击**“下一步”**。
在 选择测试选项 页中,清除两个选项并单击 完成。
打开名为 MenuCommandTest 的解决方案。 MenuCommandTestPackage 文件在 工具 菜单创建菜单命令并将它的代码。 此时,该命令使一个消息框中显示。 后续步骤演示如何更改此显示注释修饰。
打开在 VSIX 清单编辑器中的 source.extension.vsixmanifest 文件。 它应有一个名为 MenuCommand 的 VSPackage 的一 Content 行。
保存并关闭 Source.extension.vsixmanifest 文件。
添加 MEF 扩展到 VSPackage 解决方案
添加 MEF 扩展到 VSPackage 解决方案
在**“解决方案资源管理器”中,右击解决方案节点,单击“添加”,再单击“新建项目”**。 在 添加新项目 对话框中单击扩展性在 Visual Basic 或下 Visual C#,然后 EditorClassifier。 将项目命名为 CommentAdornmentTest。
由于此项目具有强名称 VSPackage 程序集进行交互,必须对程序集进行签名。 您可以重用为 VSPackage 程序集已经创建的密钥文件。
打开项目属性并选择 签名 页。
选择 为程序集签名。
在 选择一个强名称密钥文件下,选择。 MenuCommandTest 程序集生成的 Key.snk 文件。
保存项目。
有关在 VSPackage 项目的 MEF 扩展
由于您添加一个 MEF 组件到 VSPackage,可以在清单必须指定两个目录。
备注
有关 MEF 的更多信息,请参见 Managed Extensibility Framework (MEF)。
若要引用在 VSPackage 中 MEF 组件项目
在 MenuCommandTest 项目中,打开在 VSIX 清单编辑器中的 source.extension.vsixmanifest 文件。
在标题为的 内容 下,单击 添加内容。 在 选择内容类型 列表中,选择 MEF 组件。 在 选择一个源下,选择 CommentAdornmentTest。
保存并关闭 source.extension.vsixmanifest 文件。
添加对 CommentAdornmentTest 项目。
定义修饰注释
跟踪选定文本的注释修饰包括 ITrackingSpan 和表示作者和该文本说明的那些字符串。
定义修饰注释
在 CommentAdornmentTest 项目中,删除现有类文件。
添加新的类文件并将其命名为 CommentAdornment。
添加以下 using 语句。
Imports Microsoft.VisualStudio.Text
using Microsoft.VisualStudio.Text;
将名为 CommentAdornment的类。
Friend Class CommentAdornment
internal class CommentAdornment
添加三个字段添加到 ITrackingSpan、一个作者和说明的 CommentAdornment 类。
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;
添加初始化字段的构造函数。
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; }
创建修饰的可视元素
您还必须定义修饰的可视元素。 对于本演练,请定义从 windows 演示基础 (WPF) (wpf) 类 Canvas继承的控件。
创建修饰的可视元素
创建类在 CommentAdornmentTest 项目并将其命名为 CommentBlock。
添加以下 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;
由 Canvas进行 CommentBlock 类继承。
Friend Class CommentBlock Inherits Canvas
internal class CommentBlock : Canvas
添加某些私有字段定义修饰的可视方面是。
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;
添加定义注释修饰并添加相关文本的构造函数。
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); }
还要实现绘制修饰的一个 OnRender 事件处理程序。
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); } }
添加 IWpfTextViewCreationListener
IWpfTextViewCreationListener 是一个可用于侦听视图创建事件的 MEF 组件。
添加 IWpfTextViewCreationListener
将类文件添加到 CommentAdornmentTest 项目并将其命名为 连接。
添加以下 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;
声明实现 IWpfTextViewCreationListener 的类并将其导出与 “text” ContentTypeAttribute 和 DocumentTextViewRoleAttribute 。 该内容类型属性指定元素应用的内容的类型。 文本类型是所有非二进制文件类型的基类型。 因此,几乎创建的每个文本视图是此类型。 文本视图角色的属性指定将元素应用的文本视图。 通常文档文本视图角色。行在文件撰写和存储的显示文本。
<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
执行 TextViewCreated 方法,以便调用 CommentAdornmentManager的静态 Create() 事件。
Public Sub TextViewCreated(ByVal textView As IWpfTextView) Implements IWpfTextViewCreationListener.TextViewCreated CommentAdornmentManager.Create(textView) End Sub
public void TextViewCreated(IWpfTextView textView) { CommentAdornmentManager.Create(textView); }
将可以使用执行命令的方法。
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); } }
定义修饰层
若要添加新的修饰,必须定义修饰层。
定义修饰层
在 Connector 类声明类型 AdornmentLayerDefinition 的公共字段和导出为修饰层和 OrderAttribute 指定一个名称定义修饰层 Z 顺序关系其他文本视图层的其与 NameAttribute (文本、插入符号并选择)。
<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;
提供注释修饰
在定义一个修饰时,还实现注释修饰提供程序和注释修饰管理器。 ,当基础文本中移除时,注释修饰提供程序保留注释修饰列表,听完基础文本缓冲区的 Changed 事件和删除注释修饰。
添加注释修饰提供程序
添加新的类文件添加到 CommentAdornmentTest 项目并将其命名为 CommentAdornmentProvider。
添加以下 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;
将名为 CommentAdornmentProvider的类。
Friend Class CommentAdornmentProvider
internal class CommentAdornmentProvider
添加文本缓冲区和注释修饰列表的私有字段与缓冲区相关。
Private buffer As ITextBuffer Private comments As IList(Of CommentAdornment) = New List(Of CommentAdornment)()
private ITextBuffer buffer; private IList<CommentAdornment> comments = new List<CommentAdornment>();
添加 CommentAdornmentProvider的构造函数。 ,因为提供程序使用 Create() 方法,实例化此构造函数应访问私有。 构造函数添加 OnBufferChanged 事件处理程序。 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; }
添加 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); }); }
添加 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; } }
添加 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; }
添加 CommentsChanged 事件的说明。
Public Event CommentsChanged As EventHandler(Of CommentsChangedEventArgs)
public event EventHandler<CommentsChangedEventArgs> CommentsChanged;
创建一个 Add() 方法添加修饰。
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)); }
添加一个 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; }
添加返回在特定快照范围的所有注释的一个 GetComments() 方法。
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); }
将名为 CommentsChangedEventArgs的类,如下所示。
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; } }
托管修饰注释
注释修饰管理器创建修饰并将其添加到修饰层。 该侦听 LayoutChanged 和 Closed 事件,以便能够移动或删除修饰。 它还侦听按注释修饰提供程序激发的 CommentsChanged 事件,在注释中添加或移除时。
管理注释修饰
将类文件添加到 CommentAdornmentTest 项目并将其命名为 CommentAdornmentManager。
添加以下 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;
将名为 CommentAdornmentManager的类。
Friend Class CommentAdornmentManager
internal class CommentAdornmentManager
添加某些私有字段。
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;
添加订阅该管理器到 LayoutChanged 和 Closed 事件的构造函数,以及向 CommentsChanged 事件。 ,因为该管理器使用静态 Create() 方法,实例化构造函数是私有的。
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; }
添加获取提供程序或如果必须创建一 Create() 的方法。
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); }); }
添加 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); }
添加 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; }
添加 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); } } }
添加绘制注释的私有方法。
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); } }
使用菜单命令添加注释修饰
您可以使用菜单命令通过执行 VSPackage 中 MenuItemCallback 方法创建注释修饰。
使用菜单命令添加注释修饰
将下列引用添加到 MenuCommandTest 项目:
Microsoft.VisualStudio.TextManager.Interop
Microsoft.VisualStudio.Editor
Microsoft.VisualStudio.Text.UI.Wp f
添加对 CommentAdornmentTest 项目。
打开 MenuCommandTestPackage 文件。
添加以下 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;
在 MenuItemCallback 方案,请移除现有代码。
添加代码来获取活动视图。 必须捕获 Visual Studio shell 访问的 SVsTextManager 有效的 IVsTextView。
Dim txtMgr As IVsTextManager = CType(GetService(GetType(SVsTextManager)), IVsTextManager) Dim vTextView As IVsTextView = Nothing Dim mustHaveFocus As Integer = 1 txtMgr.GetActiveView(mustHaveFocus, Nothing, vTextView)
IVsTextManager txtMgr = (IVsTextManager)GetService(typeof(SVsTextManager)); IVsTextView vTextView = null; int mustHaveFocus = 1; txtMgr.GetActiveView(mustHaveFocus, null, out vTextView);
如果此文本视图是编辑文本视图的实例,则可以将其转换为 IVsUserData 接口然后获取 IWpfTextViewHost 及其关联的 IWpfTextView。
Dim userData As IVsUserData = TryCast(vTextView, IVsUserData) If userData Is Nothing Then Console.WriteLine("No text view is currently open") Return End If Dim viewHost As IWpfTextViewHost Dim holder As Object Dim guidViewHost As Guid = DefGuidList.guidIWpfTextViewHost userData.GetData(guidViewHost, holder) viewHost = CType(holder, IWpfTextViewHost)
IVsUserData userData = vTextView as IVsUserData; if (userData == null) { Console.WriteLine("No text view is currently open"); return; } IWpfTextViewHost viewHost; object holder; Guid guidViewHost = DefGuidList.guidIWpfTextViewHost; userData.GetData(ref guidViewHost, out holder); viewHost = (IWpfTextViewHost)holder;
使用 IWpfTextViewHost 调用 Connector.Execute() 方法,获取注释修饰提供程序并添加修饰。
Connector.Execute(viewHost)
Connector.Execute(viewHost);
生成并测试代码
若要测试此代码,请生成 MenuCommand 解决方案并运行在的实验实例。
生成和测试 MenuCommand 解决方案
生成解决方案。 当您运行在调试器中查看此项目, Visual Studio 的第二个实例进行实例化。
创建文本文件。 键入一些文本然后选择它。
在 工具 菜单上,单击 添加修饰。 应在文本窗口的右侧显示气球,并且应包含与下面的文本类似的文本。
TheUserName
八个…