ONDotNet.com    
 Published on ONDotNet.com (http://www.ondotnet.com/)
 See this if you're having trouble printing code examples


Using Visual Studio .NET Macros

by Matthew MacDonald
12/09/2002

Now that the .NET mania is finally settling down and developers are starting to adapt to life with the platform, it's a good time to consider a few less-revolutionary (but very practical) details that can simply your programming life. One of these is the often-overlooked macro engine and extensibility model that's built into Visual Studio .NET. This model provides almost 200 objects that give you unprecedented control over the IDE. This includes the ability to access and manipulate the current project hierarchy, the collection of open windows, and the integrated debugger. Using this model, you can build everything from simple keystroke recordings to advanced add-ins that present their own user interface and interact with the IDE. In this article, we'll look at how you might use macros to automate common code-generation tasks, such as building property procedures for your classes.

Related Reading

Mastering Visual Studio .NET
By Ian Griffiths, Jon Flanders, Chris Sells

Macro Basics

Macros are only one of the extensibility tools provided by Visual Studio .NET, but they are easy to develop quickly, and they have surprising clout. Unlike most other Microsoft products, Visual Studio .NET macros are built out of full .NET code, which means they can use any part of the .NET class library. This allows macros to write XML files, display forms, and even contact remote Web services as part of their work. (For information about other types of Visual Studio .NET extensibility, and help choosing which one suits a particular problem, you can consult Microsoft's automation whitepaper here.)

You can create a basic macro simply by recording your actions in the Visual Studio .NET editor. Just follow these steps:

  1. Select Tools > Macros > Record Temporary Macro from the Visual Studio.NET menu.
  2. Type in the appropriate keystrokes, which will be recorded.
  3. Click the stop button on the floating macro toolbar once you're finished.
  4. You can now play the macro back using the menu or the convenience hotkey Ctrl-Shift-P.

To view the code for a recorded macro, select Select Tool > Macros > Macro Explorer. This window (shown below) shows a tree of macro modules, and the macros they contain. Each macro corresponds to a Visual Basic .NET subroutine.

Macro Explorer

To edit the macro you just created, right-click on the TemporaryMacro subroutine in the RecordingModule and select Edit. A separate IDE will load for editing macro code; it closely resembles the ordinary Visual Studio .NET environment, right down to a Project Explorer and dynamic help. Visual Studio only stores one temporary macro at a time, which is overwritten every time you record a new one. To make a temporary macro permanent, you'll need to cut and paste the code into a different subroutine.

As stated earlier, macro code uses ordinary .NET syntax and the class library. However, macro code also has access to a special set of Visual Studio .NET extensibility objects, which you won't recognize. These objects are used to interact with windows, insert and read editor text, and so on. For example, to add a new line into the editor, you would use the following macro code:


' Get the current insertion point (where the cursor is positioned).
Dim TS As TextSelection = DTE.ActiveDocument.Selection
 
' Move to the end of the line.
TS.EndOfLine()
 
' Add a new line (the programmatic equivalent of pressing Enter).
TS.NewLine()
 
' Insert some text.
TS.Insert("Sample Text")

All of these objects are contained in a special EnvDTE namespace, which is contained in the EnvDTE.dll assembly. This assembly is referenced by default in all macro projects.


Imports EnvDTE

A good way to start learning about macros is to use the record facility, and then look at the code it generates.

Macros for Property Procedures

Visual Basic 6 included several add-ins that could automate class code, including wizards that would generate a series of property procedures. Unfortunately, Visual Studio .NET has nothing comparable. This problem becomes apparent when developers begin to design a class. Unfortunately, it's far simpler to type the following code statement:


Public MyVar As String

Than to create a full property procedure that ensures proper encapsulation of private data:


Private _MyVar As String

Public Property MyVar As String
  Get
    Return _MyVar
  End Get
  Set(ByVal Value As String)
    _MyVar = Value
  End Set
End Property

The solution, clearly, is to create a macro that generates property procedures automatically as needed. One approach is to create a straightforward macro that examines the text at the current insertion point, and uses it to generate a full property procedure. We'll begin coding this macro by writing a private function (which will be added to the macro module) that takes a line of text representing a private variable declaration, and creates a corresponding property procedure declaration. In this case, the code assumes that private variables will always start with a leading underscore (_), which will be trimmed from the name of the property procedure. Depending on your conventions, the code may need to be adjusted.


Private Function GetInsertion(ByVal text As String) As String

  Dim Words() As String = text.Trim.Split()

  If Words.Length < 4 Then

    ' This line is not a valid variable declaration.
    Return ""

  Else

    Dim Insertion As String
    Insertion = " Public Property " & Words(1).Trim("_")
    Insertion &= " As " & Words(3)
    Insertion &= vbNewLine
    Insertion &= " Get"
    Insertion &= vbNewLine
    Insertion &= " Return " & Words(1)
    Insertion &= vbNewLine
    Insertion &= " End Get"
    Insertion &= vbNewLine
    Insertion &= " Set(ByVal Value As " & Words(3) & ")"
    Insertion &= vbNewLine
    Insertion &= " " & Words(1) & " = Value"
    Insertion &= vbNewLine
    Insertion &= " End Set"
    Insertion &= vbNewLine
    Insertion &= " End Property"
    Insertion &= vbNewLine & vbNewLine

    Return Insertion

  End If
End Function

The next step is to create a macro that uses this function. The code below selects the current line, generates a property procedure using GetInsertion(), and adds it after the current line.

Public Sub ExpandPrivateMembersFromSelection()

  Dim TS As TextSelection = DTE.ActiveDocument.Selection
  Dim Insertion As String, Line As String
  Dim Lines() As String = TS.Text.Split(vbNewLine)

  For Each Line In Lines
      Insertion &= GetInsertion(Line)
  Next

  TS.EndOfLine()
  TS.NewLine()
  TS.Insert(Insertion)

End Sub

The following figure shows this macro in the macro explorer. You can double-click it to run it on the current editor line.

Macro Explorer

It's now easy to extend this macro to work with multiple lines. The ExpandPrivateMembersFromSelection() macro below passes each selected line to the GetInsertion() function, and builds up the returned text (any blank lines will be ignored by GetInsertion() automatically). Then, the full text block is inserted. For example:


Public Sub ExpandPrivateMembersFromSelection()

  Dim TS As TextSelection = DTE.ActiveDocument.Selection
  Dim Insertion As String, Line As String
  Dim Lines() As String = TS.Text.Split(vbNewLine)

  For Each Line In Lines
      Insertion &= GetInsertion(Line)
  Next

  TS.EndOfLine()
  TS.NewLine()
  TS.Insert(Insertion)

End Sub

Now you simply need to type the following code:

Private _MyVar As String

And run one of the two macros to generate a corresponding property procedure for any basic data type.

At this point, you may be wondering if it's possible to write this code in a language-independent way to support any .NET language. This functionality is called the CodeDOM in the .NET Framework. Using the CodeDOM is a complex operation that is outside of the scope of this article.

Summary

Macros are a powerful tool in for automating repetitive tasks in any application. However, when used correctly, macros can become much more, and help enforce standards, promote good design, and improve consistency across an entire organization. One way is by using add-ins or macros that generate certain code structures in a standardized fashion.

You can download the code for this article at www.prosetech.com.

Matthew MacDonald is a developer, author, and educator in all things Visual Basic and .NET. He's worked with Visual Basic and ASP since their initial versions, and written over a dozen books on the subject, including The Book of VB .NET (No Starch Press) and Visual Basic 2005: A Developer's Notebook (O'Reilly). His web site is http://www.prosetech.com/.


Return to ONDotnet.com

Copyright © 2009 O'Reilly Media, Inc.