A Macro for Creating Properties from Private Fields

Macros are a useful way of speeding up your coding efforts, however writing them can be a somewhat daunting task. Luckily, .NET gives you a pretty powerful recording feature for recording macros from key strokes.


Figure 1 - Old Macro Recorder

Introduction

Macros are a useful way of speeding up your coding efforts, however writing them can be a somewhat daunting task. Luckily .NET gives you a pretty powerful recording feature for recording macros from key strokes. It seems to be much richer in recording than previous Visual Studio environments so you can create some pretty fancy macros. Also the VBA-like macro (known as VSA) that is created is pretty easy to understand so you won't have trouble modifying  it. On top of that, you can set break points in the VSA code and step through your macros. 

Recording Macros

To start recording, choose Tools->Macros->Record TemporaryMacro in the Visual Studio.NET menu as shown in figure 1. Later you can choose Save TemporaryMacro to be whatever name you like.

Figure 2 - Recording a TemporaryMacro

After you choose record, a little tape record buttonbar shows up and you can choose to stop and save the recording by pressing the middle button shown in figure 2. If you don't like your recording, simply press cancel on the right of the toolbar.

Figure 3 - Recording button bar

Editing Macros

If you have a complex macro, inevitably, you will need to make changes to your macro. By going into the macro explorer shown in figure 3, you can right click on the macro you wish to edit, choose Edit from the popup,and Visual Studio will bring you into the Macro IDE editor.

Figure 4 - Macro Explorer

Once in the Macro Editor you can delete all those lines representing the accidental keystrokes you hit while creating your macros.You can also add your own macro code to enhance the robustness of your macro. Once you see the code that was created by the macro recorder, you can glom off the knowledge inside these lines of code to figure out how to configure your own enhancments.  Also the intellisense in the editor makes it easier to figure out how to use the objects and properties in the EnvDTE namespace.Figure 4 shows a snapshot of the Macro IDE. Note that it is a full development environment in itself (similar to VBA) with debugging, class browsing, and powerful editing features. You can also set breakpoints in your macro and step through the VSA code a line at the time, while watching it perform the step-by-step editing in VS.NET.

Figure 5 - Macro IDE for creating, debugging, and modifying macros

Using the Property Macro

The full VSA code for the macro that takes a field and creates a public property for it is shown below.  This particular macro requires that you create your field with the widely accepted standard for field declarations.That is, you should place an underscore in front of the field name.

e.g.   protected int _numberOfShapes = 10;

You can of course modify the macro code to accept your own internal standard (e.g. m_  as was previously used in Visual C++).  Also the code assumes you are using a single Access keyword in front of the type (e.g. private, protected, public). 

How it Works

The macro in listing one works in a fairly simple way.It first copies the property type (assuming the type is the second word in the field) and begins creating the property by pasting it on the line below the field. The macro then copies the field name (the third word in the field) and substitutes it into the rest of the property. The field name is also copied and altered to create the name of the property.

Listing 1 - VSA Macro for turning a field into a property

Sub MakeProperty()
' go to the beginning of the line
DTE.ActiveDocument.Selection.StartOfLine(vsStartOfLineOptions.vsStartOfLineOptionsFirstText)
' move pass the Access keyword
DTE.ActiveDocument.Selection.WordRight()
' move over to the type and select
DTE.ActiveDocument.Selection.WordRight(True)
' copy the type
DTE.ActiveDocument.Selection.Copy()
' go to the end of the line and create a new line under it
DTE.ActiveDocument.Selection.EndOfLine()
DTE.ActiveDocument.Selection.NewLine()
' create the public + type for the property
DTE.ActiveDocument.Selection.Text = "public "
DTE.ActiveDocument.Selection.Paste()
' return to the field and copy the field name
DTE.ActiveDocument.Selection.LineUp()
DTE.ActiveDocument.Selection.StartOfLine
vsStartOfLineOptions.vsStartOfLineOptionsFirstText)DTE.ActiveDocument.Selection.WordRight(
False, 2)DTE.ActiveDocument.Selection.WordRight(True)
DTE.ActiveDocument.Selection.Copy()
' paste the field name at the end of the line
DTE.ActiveDocument.Selection.LineDown()
DTE.ActiveDocument.Selection.EndOfLine()
DTE.ActiveDocument.Selection.Paste()
' delete the underscore for the property
DTE.ActiveDocument.Selection.WordLeft()
DTE.ActiveDocument.Selection.Delete()
' change the first character of the property name to upper case
DTE.ActiveDocument.Selection.CharRight(True)
DTE.ActiveDocument.Selection.ChangeCase(vsCaseOptions.vsCaseOptionsUppercase)
DTE.ActiveDocument.Selection.EndOfLine()
' create the gets and sets of the property
DTE.ActiveDocument.Selection.NewLine()
DTE.ActiveDocument.Selection.Text = "{"
DTE.ActiveDocument.Selection.NewLine(2)
DTE.ActiveDocument.Selection.Text = "}"
DTE.ActiveDocument.Selection.LineUp()
DTE.ActiveDocument.Selection.Indent(3)
' create the get
DTE.ActiveDocument.Selection.Text = "get"
DTE.ActiveDocument.Selection.NewLine()
DTE.ActiveDocument.Selection.Text = "{"
DTE.ActiveDocument.Selection.NewLine()
DTE.ActiveDocument.Selection.Text = "return "
' paste the private field name in the get
DTE.ActiveDocument.Selection.Paste()
DTE.ActiveDocument.Selection.Text = ";"
DTE.ActiveDocument.Selection.NewLine()
DTE.ActiveDocument.Selection.Text = "}"
DTE.ActiveDocument.Selection.NewLine(2)
' create the set
DTE.ActiveDocument.Selection.Text = "set"
DTE.ActiveDocument.Selection.NewLine()
DTE.ActiveDocument.Selection.Text = "{"
DTE.ActiveDocument.Selection.NewLine(2)
DTE.ActiveDocument.Selection.Text = "}"
DTE.ActiveDocument.Selection.LineUp()
DTE.ActiveDocument.Selection.Indent(4)
' paste the private field name in the set
DTE.ActiveDocument.Selection.Paste()
DTE.ActiveDocument.Selection.Text = " = value;"
DTE.ActiveDocument.Selection.LineDown(
False, 3)
End Sub

Conclusion

Macros are a great way to accelerate your coding.  You can use them to create control constructs such as for..each, for loops, and while loops. You can use them to standardize your classes, web services, and more. You can even use them to insert standard comments for your project. I think once you have created one or two macros, you'll find yourself using them quite a bit. For standardization and less typing, macro is the way to go.