Walkthrough: Displaying SmartTags

Smart tags are tags on text that expand to display a set of actions. For example, in a Visual Basic or Visual C# project, a red line appears under a word when you rename an identifier such as a variable name. When you move the pointer over the underline, a button is displayed near the pointer. If you click the button, a suggested action is displayed, for example, Rename IsRead to IsReady. If you click the action, all references to IsRead in the project are renamed IsReady.

Although smart tags are part of the IntelliSense implementation in the editor, you can implement smart tags by subclassing SmartTag, and then implementing the ITagger(Of T) interface and the IViewTaggerProvider interface.

NoteNote

Other kinds of tags can be implemented in a similar manner.

The following walkthrough shows how to create a smart tag that appears on the current word and has two suggested actions: Convert to upper case and Convert to lower case.

To complete this walkthrough, you must install the Visual Studio 2010 SDK.

NoteNote

For more information about the Visual Studio SDK, see Visual Studio Integration SDK. To find out how to download the Visual Studio SDK, see Visual Studio Extensibility Developer Center on the MSDN Web site.

To create a MEF project

  1. Create an Editor Classifier project. Name the solution SmartTagTest.

  2. Open the source.extension.vsixmanifest file in the VSIX Manifest Editor.

  3. Make sure that the Content heading contains a MEF Component content type and that the Path is set to SmartTagTest.dll.

  4. Save and close source.extension.vsixmanifest.

  5. Add the following reference to the project, and set CopyLocal to false:

    Microsoft.VisualStudio.Language.Intellisense

  6. Delete the existing class files.

To implement a tagger for smart tags

  1. Add a class file and name it TestSmartTag.

  2. Add the following imports:

    
    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
    
    
    
  3. Add a class named TestSmartTag that inherits from SmartTag.

    
    Friend Class TestSmartTag
        Inherits SmartTag
    
    
    
  4. Add a constructor for this class that calls the base constructor with a SmartTagType of Factoid, which will cause a blue line to appear under the first character of a word. (If you use Ephemeral, a red line will appear under the last character of the word.)

    
    Public Sub New(ByVal actionSets As ReadOnlyCollection(Of SmartTagActionSet))
        MyBase.New(SmartTagType.Factoid, actionSets)
    End Sub
    
    
    
  5. Add a class named TestSmartTagger that inherits from ITagger(Of T) of type TestSmartTag, and implements IDisposable.

    
    Friend Class TestSmartTagger
        Implements ITagger(Of TestSmartTag), IDisposable
    
    
    
  6. Add the following private fields to the tagger class.

    
    Private m_buffer As ITextBuffer
    Private m_view As ITextView
    Private m_provider As TestSmartTaggerProvider
    Private m_disposed As Boolean
    
    
    
  7. Add a constructor that sets the private fields, and subscribes to the LayoutChanged event.

    
    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
    
    
    
  8. Implement GetTags so that the tag is created for the current word. (This method also calls a private method GetSmartTagActions that is explained later.)

    
    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
    
    
    
  9. Add a GetSmartTagActions method to set up the smart tag actions. The actions themselves are implemented in later steps.

    
    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
    
    
    
  10. Declare the SmartTagsChanged event.

    
    Public Event TagsChanged As EventHandler(Of SnapshotSpanEventArgs) Implements ITagger(Of TestSmartTag).TagsChanged
    
    
    
  11. Implement the OnLayoutChanged event handler to raise the TagsChanged event, which causes GetTags to be called.

    
    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
    
    
    
  12. Implement the Dispose method so that it unsubscribes from the LayoutChanged event.

    
    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
    
    
    

To implement the smart tag tagger provider

  1. Add a class named TestSmartTagTaggerProvider that inherits from IViewTaggerProvider. Export it with a ContentTypeAttribute of "text", a OrderAttribute of Before="default", and a TagTypeAttribute of SmartTag.

    
    <Export(GetType(IViewTaggerProvider))>
    <ContentType("text")>
    <Order(Before:="default")>
    <TagType(GetType(SmartTag))>
    Friend Class TestSmartTaggerProvider
        Implements IViewTaggerProvider
    
    
    
  2. Import the ITextStructureNavigatorSelectorService as a property.

    
    <Import(GetType(ITextStructureNavigatorSelectorService))>
    Friend Property NavigatorService() As ITextStructureNavigatorSelectorService
    
    
    
  3. Implement the CreateTagger(Of T) method.

    
    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
    
    
    

To implement smart tag actions

  • Create two classes, the first named UpperCaseSmartTagAction and the second named LowerCaseSmartTagAction. Both classes implement ISmartTagAction.

    
    Friend Class UpperCaseSmartTagAction
        Implements ISmartTagAction
    
    
    
    
    Friend Class LowerCaseSmartTagAction
        Implements ISmartTagAction
    
    
    

Both classes are alike except that one calls ToUpper and the other calls ToLower. The following steps cover only the uppercase action class, but you must implement both classes. Use the steps for implementing the uppercase action as a pattern for implementing the lowercase action.

  1. Declare a set of private fields.

    
    Private m_span As ITrackingSpan
    Private m_upper As String
    Private m_display As String
    Private m_snapshot As ITextSnapshot
    
    
    
  2. Add a constructor that sets the fields.

    
    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
    
    
    
  3. Implement the properties as follows.

    
    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
    
    
    
  4. Implement the Invoke method by replacing the text in the span with its uppercase equivalent.

    
    Public Sub Invoke() Implements ISmartTagAction.Invoke
        m_span.TextBuffer.Replace(m_span.GetSpan(m_snapshot), m_upper)
    End Sub
    
    
    

To test this code, build the SmartTagTest solution and run it in the experimental instance.

To build and test the SmartTagTest solution

  1. Build the solution.

  2. When you run this project in the debugger, a second instance of Visual Studio is instantiated.

  3. Create a text file and type some text.

    A blue line should be displayed under the first letter of the first word of the text.

  4. Move the pointer over the blue line.

    A button should be displayed near the pointer.

  5. When you click the button, two suggested actions should be displayed: Convert to upper case and Convert to lower case. If you click the first action, all the text in the current word should be converted to upper case. If you click the second action, all the text should be converted to lower case.

Was this page helpful?
(1500 characters remaining)
Thank you for your feedback

Community Additions

ADD
Show:
© 2015 Microsoft