智能标记是展开以显示设置事件在文本的标记。 例如,那么,当您对一个重命名标识符 (例如将变量名时,在 Visual Basic 或 Visual c# 项目中,为红色行显示在运行下。 当您将该下划线时的指针,按钮的指针上四处显示。 如果单击按钮,一个建议的事件显示,例如, 对 IsRead 重命名为 IsReady。 如果单击事件,所有引用到项目中 IsRead 重命名 IsReady。
虽然智能标记是 IntelliSense 实现的一部分在编辑器中,可以通过 subclassing SmartTag,然后实现 ITagger 接口和 IViewTaggerProvider 接口实现智能标记。
备注
其他标记可以以类似方式实现。
下面的演练演示如何创建显示在当前运行并具有两个建议的事件的智能标记: 大写的转换 和 例的转换。
系统必备
若要完成本演练,您必须安装 Visual Studio 2010 SDK。
备注
有关 Visual Studio SDK 的更多信息,请参见 扩展 Visual Studio 概述。若要查找有关中所列如何下载 Visual Studio SDK,请 Visual Studio Extensibility Developer Center 参见 MSDN 网站上。
创建一个 managed extensibility framework 项目 (MEF)
创建 MEF 项目
创建一个编辑分类器项目。 将解决方案命名为 SmartTagTest。
打开在 VSIX 清单编辑器中的 source.extension.vsixmanifest 文件。
确保 Content 归为包含一个 MEF 组件内容类型,而 Path 设置为 SmartTagTest.dll。
保存并关闭的 source.extension.vsixmanifest。
将下列引用添加到项目,并将 CopyLocal 到 false:
Microsoft.VisualStudio.Language.Intellisense
删除现有类文件。
实现智能标记的一个标记
实现智能标记的一个标记
将类文件并将其命名为 TestSmartTag。
添加下面的导入:
Imports System Imports System.Collections.Generic Imports System.ComponentModel.Composition Imports System.Collections.ObjectModel Imports System.Windows.Media Imports Microsoft.VisualStudio.Language.Intellisense Imports Microsoft.VisualStudio.Text Imports Microsoft.VisualStudio.Text.Editor Imports Microsoft.VisualStudio.Text.Operations Imports Microsoft.VisualStudio.Text.Tagging Imports Microsoft.VisualStudio.Utilities
using System; using System.Collections.Generic; using System.ComponentModel.Composition; using System.Collections.ObjectModel; using System.Windows.Media; using Microsoft.VisualStudio.Language.Intellisense; using Microsoft.VisualStudio.Text; using Microsoft.VisualStudio.Text.Editor; using Microsoft.VisualStudio.Text.Operations; using Microsoft.VisualStudio.Text.Tagging; using Microsoft.VisualStudio.Utilities;
添加从 SmartTag从继承的名为 TestSmartTag 的类。
Friend Class TestSmartTag Inherits SmartTag
internal class TestSmartTag : SmartTag
添加调用与 FactoidSmartTagType 的基本构造函数,将导致一条蓝线出现在单词的第一个字符下的此类的构造函数。 (如果使用 Ephemeral,一个红色直线将显示在单词的最后一个字符下。)
Public Sub New(ByVal actionSets As ReadOnlyCollection(Of SmartTagActionSet)) MyBase.New(SmartTagType.Factoid, actionSets) End Sub
public TestSmartTag(ReadOnlyCollection<SmartTagActionSet> actionSets) : base(SmartTagType.Factoid, actionSets) { }
添加从类型继承 TestSmartTagITagger 名为 TestSmartTagger 的类,并实现 IDisposable。
Friend Class TestSmartTagger Implements ITagger(Of TestSmartTag), IDisposable
internal class TestSmartTagger : ITagger<TestSmartTag>, IDisposable
添加以下私有字段添加到标记类。
Private m_buffer As ITextBuffer Private m_view As ITextView Private m_provider As TestSmartTaggerProvider Private m_disposed As Boolean
private ITextBuffer m_buffer; private ITextView m_view; private TestSmartTaggerProvider m_provider; private bool m_disposed;
添加设置私有字段的构造函数,并订阅 LayoutChanged 事件。
Public Sub New(ByVal buffer As ITextBuffer, ByVal view As ITextView, ByVal provider As TestSmartTaggerProvider) m_buffer = buffer m_view = view m_provider = provider AddHandler m_view.LayoutChanged, AddressOf OnLayoutChanged End Sub
public TestSmartTagger(ITextBuffer buffer, ITextView view, TestSmartTaggerProvider provider) { m_buffer = buffer; m_view = view; m_provider = provider; m_view.LayoutChanged += OnLayoutChanged; }
实现 GetTags ,以便标记为当前单词创建。 (此方法也会调用后进行说明。) 的私有方法 GetSmartTagActions
Public Function GetTags(ByVal spans As NormalizedSnapshotSpanCollection) As IEnumerable(Of ITagSpan(Of TestSmartTag)) Implements ITagger(Of TestSmartTag).GetTags Dim snapshot As ITextSnapshot = m_buffer.CurrentSnapshot If snapshot.Length = 0 Then Return Nothing Exit Function End If 'set up the navigator Dim navigator As ITextStructureNavigator = m_provider.NavigatorService.GetTextStructureNavigator(m_buffer) 'set up a list to contain the tags Dim list As List(Of TagSpan(Of TestSmartTag)) list = New List(Of TagSpan(Of TestSmartTag))() For Each span In spans Dim caret As ITextCaret = m_view.Caret Dim point As SnapshotPoint If CInt(caret.Position.BufferPosition) > 0 Then point = caret.Position.BufferPosition - 1 Else Exit For End If Dim extent As TextExtent = navigator.GetExtentOfWord(point) 'don't display the tag if the extent has whitespace If extent.IsSignificant Then list.Add(New TagSpan(Of TestSmartTag)(extent.Span, New TestSmartTag(GetSmartTagActions(extent.Span)))) Else Exit For End If Next span Return list End Function
public IEnumerable<ITagSpan<TestSmartTag>> GetTags(NormalizedSnapshotSpanCollection spans) { ITextSnapshot snapshot = m_buffer.CurrentSnapshot; if (snapshot.Length == 0) yield break; //don't do anything if the buffer is empty //set up the navigator ITextStructureNavigator navigator = m_provider.NavigatorService.GetTextStructureNavigator(m_buffer); foreach (var span in spans) { ITextCaret caret = m_view.Caret; SnapshotPoint point; if (caret.Position.BufferPosition > 0) point = caret.Position.BufferPosition - 1; else yield break; TextExtent extent = navigator.GetExtentOfWord(point); //don't display the tag if the extent has whitespace if (extent.IsSignificant) yield return new TagSpan<TestSmartTag>(extent.Span, new TestSmartTag(GetSmartTagActions(extent.Span))); else yield break; } }
添加一个 GetSmartTagActions 方法智能标记操作。 事件在后续步骤中实现。
Private Function GetSmartTagActions(ByVal span As SnapshotSpan) As ReadOnlyCollection(Of SmartTagActionSet) Dim actionSetList As New List(Of SmartTagActionSet)() Dim actionList As New List(Of ISmartTagAction)() Dim trackingSpan As ITrackingSpan = span.Snapshot.CreateTrackingSpan(span, SpanTrackingMode.EdgeInclusive) actionList.Add(New UpperCaseSmartTagAction(trackingSpan)) actionList.Add(New LowerCaseSmartTagAction(trackingSpan)) Dim actionSet As New SmartTagActionSet(actionList.AsReadOnly()) actionSetList.Add(actionSet) Return actionSetList.AsReadOnly() End Function
private ReadOnlyCollection<SmartTagActionSet> GetSmartTagActions(SnapshotSpan span) { List<SmartTagActionSet> actionSetList = new List<SmartTagActionSet>(); List<ISmartTagAction> actionList = new List<ISmartTagAction>(); ITrackingSpan trackingSpan = span.Snapshot.CreateTrackingSpan(span, SpanTrackingMode.EdgeInclusive); actionList.Add(new UpperCaseSmartTagAction(trackingSpan)); actionList.Add(new LowerCaseSmartTagAction(trackingSpan)); SmartTagActionSet actionSet = new SmartTagActionSet(actionList.AsReadOnly()); actionSetList.Add(actionSet); return actionSetList.AsReadOnly(); }
声明 SmartTagsChanged 事件。
Public Event TagsChanged As EventHandler(Of SnapshotSpanEventArgs) Implements ITagger(Of TestSmartTag).TagsChanged
public event EventHandler<SnapshotSpanEventArgs> TagsChanged;
实现 OnLayoutChanged 事件处理程序引发 TagsChanged 事件,从而 GetTags 调用。
Private Sub OnLayoutChanged(ByVal sender As Object, ByVal e As TextViewLayoutChangedEventArgs) Dim snapshot As ITextSnapshot = e.NewSnapshot 'don't do anything if this is just a change in case If Not snapshot.GetText().ToLower().Equals(e.OldSnapshot.GetText().ToLower()) Then Dim span As New SnapshotSpan(snapshot, New Span(0, snapshot.Length)) Dim handler As EventHandler(Of SnapshotSpanEventArgs) = Me.TagsChangedEvent If handler IsNot Nothing Then handler(Me, New SnapshotSpanEventArgs(span)) End If End If End Sub
private void OnLayoutChanged(object sender, TextViewLayoutChangedEventArgs e) { ITextSnapshot snapshot = e.NewSnapshot; //don't do anything if this is just a change in case if (!snapshot.GetText().ToLower().Equals(e.OldSnapshot.GetText().ToLower())) { SnapshotSpan span = new SnapshotSpan(snapshot, new Span(0, snapshot.Length)); EventHandler<SnapshotSpanEventArgs> handler = this.TagsChanged; if (handler != null) { handler(this, new SnapshotSpanEventArgs(span)); } } }
执行 Dispose 方法,使其从 LayoutChanged 取消订阅事件。
Public Sub Dispose() Implements IDisposable.Dispose Dispose(True) GC.SuppressFinalize(Me) End Sub Private Sub Dispose(ByVal disposing As Boolean) If disposing Then RemoveHandler m_view.LayoutChanged, AddressOf OnLayoutChanged m_view = Nothing End If m_disposed = True End Sub
public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } private void Dispose(bool disposing) { if (!this.m_disposed) { if (disposing) { m_view.LayoutChanged -= OnLayoutChanged; m_view = null; } m_disposed = true; } }
实现智能标记提供程序
实现智能标记提供程序
添加从 IViewTaggerProvider从继承的名为 TestSmartTagTaggerProvider 的类。 导出其与 “text” ContentTypeAttribute , OrderAttribute Before= " default”和 SmartTagTagTypeAttribute 。
<Export(GetType(IViewTaggerProvider))> <ContentType("text")> <Order(Before:="default")> <TagType(GetType(SmartTag))> Friend Class TestSmartTaggerProvider Implements IViewTaggerProvider
[Export(typeof(IViewTaggerProvider))] [ContentType("text")] [Order(Before = "default")] [TagType(typeof(SmartTag))] internal class TestSmartTaggerProvider : IViewTaggerProvider
导入 ITextStructureNavigatorSelectorService 为特性。
<Import(GetType(ITextStructureNavigatorSelectorService))> Friend Property NavigatorService() As ITextStructureNavigatorSelectorService
[Import(typeof(ITextStructureNavigatorSelectorService))] internal ITextStructureNavigatorSelectorService NavigatorService { get; set; }
实现 CreateTagger``1 方法。
Public Function CreateTagger(Of T As ITag)(ByVal textView As ITextView, ByVal buffer As ITextBuffer) As ITagger(Of T) Implements IViewTaggerProvider.CreateTagger If buffer Is Nothing OrElse textView Is Nothing Then Return Nothing End If 'make sure we are tagging only the top buffer If buffer Is textView.TextBuffer Then Return New TestSmartTagger(buffer, textView, Me) Else Return Nothing End If End Function
public ITagger<T> CreateTagger<T>(ITextView textView, ITextBuffer buffer) where T : ITag { if (buffer == null || textView == null) { return null; } //make sure we are tagging only the top buffer if (buffer == textView.TextBuffer) { return new TestSmartTagger(buffer, textView, this) as ITagger<T>; } else return null; }
实现智能标记操作
实现智能标记操作
创建两个类、第一个用于 UpperCaseSmartTagAction 和第二个命名 LowerCaseSmartTagAction。 两类实现 ISmartTagAction。
Friend Class UpperCaseSmartTagAction Implements ISmartTagAction
internal class UpperCaseSmartTagAction : ISmartTagAction
Friend Class LowerCaseSmartTagAction Implements ISmartTagAction
internal class LowerCaseSmartTagAction : ISmartTagAction
两类是相同的,但某个调用 ToUpper ,而其他调用 ToLower。 下面的步骤仅包含大写事件类,但是,您必须实现两类。 为实现大写事件使用步骤作为实现的情况下操作一个架构。
声明用于设置私有字段。
Private m_span As ITrackingSpan Private m_upper As String Private m_display As String Private m_snapshot As ITextSnapshot
private ITrackingSpan m_span; private string m_upper; private string m_display; private ITextSnapshot m_snapshot;
添加设置字段的构造函数。
Public Sub New(ByVal span As ITrackingSpan) m_span = span m_snapshot = span.TextBuffer.CurrentSnapshot m_upper = span.GetText(m_snapshot).ToUpper() m_display = "Convert to upper case" End Sub
public UpperCaseSmartTagAction(ITrackingSpan span) { m_span = span; m_snapshot = span.TextBuffer.CurrentSnapshot; m_upper = span.GetText(m_snapshot).ToUpper(); m_display = "Convert to upper case"; }
按如下方式实现属性。
Public ReadOnly Property DisplayText() As String Implements ISmartTagAction.DisplayText Get Return m_display End Get End Property Public ReadOnly Property Icon() As ImageSource Implements ISmartTagAction.Icon Get Return Nothing End Get End Property Public ReadOnly Property IsEnabled() As Boolean Implements ISmartTagAction.IsEnabled Get Return True End Get End Property Private privateSource As ISmartTagSource Public Property Source() As ISmartTagSource Get Return privateSource End Get Private Set(ByVal value As ISmartTagSource) privateSource = value End Set End Property Public ReadOnly Property ActionSets() As ReadOnlyCollection(Of SmartTagActionSet) Implements ISmartTagAction.ActionSets Get Return Nothing End Get End Property
public string DisplayText { get { return m_display; } } public ImageSource Icon { get { return null; } } public bool IsEnabled { get { return true; } } public ISmartTagSource Source { get; private set; } public ReadOnlyCollection<SmartTagActionSet> ActionSets { get { return null; } }
通过替换该范围内的文本执行 Invoke 方法将其转换为大写等效。
Public Sub Invoke() Implements ISmartTagAction.Invoke m_span.TextBuffer.Replace(m_span.GetSpan(m_snapshot), m_upper) End Sub
public void Invoke() { m_span.TextBuffer.Replace(m_span.GetSpan(m_snapshot), m_upper); }
生成并测试代码
若要测试此代码,请生成 SmartTagTest 解决方案并运行在的实验实例。
生成和测试 SmartTagTest 解决方案
生成解决方案。
当您运行在调试器中查看此项目, Visual Studio 的第二个实例进行实例化。
创建一个文本文件并键入一些文本。
应显示一条蓝线文本中的第一个单词的首字母下。
将条蓝线的指针。
应在指针周围显示按钮。
当单击按钮时,相关建议应公开事件: 转换为大写 和 转换为小写。 如果单击第一个事件,应将当前运行的所有文本转换为大写。 如果单击第二个事件,应将所有文本转换为小写。