Handy .NET Macro






3.25/5 (10 votes)
Jan 16, 2006
2 min read

47894

246
This macro has a handful of methods that I find useful with .NET.
Introduction
This macro holds helpful methods for use in Visual Studio .NET. For those of you not familiar with macro writing - if you don't know how to do something, just try recording a macro and you can usually view the auto-generated code and get some idea of where to start with your own code. The methods available in this macro are:
CommentAllRoutines
Inserts comments for all the routines in the current document. Gives creation date, author, parameters, and method names.
CommentSelectedRoutines
As above, but only comments the routines you have selected.
IncrementTabIndexes
I came up with this method after getting annoyed of having to manually reset the tab indexes of ASP web controls. Select your HTML, and the first tab index will set the counter, then subsequent tab indexes will be incremented by 1.
IncrementUserText
As above, but you get an input box so that you can specify your own field to search and increment.
InsertModuleHeader
Inserts the creation date, author, and copyright info at the top of your module.
InsertProperties
Select your class level variables (must be prefixed with “_”), and this method will automatically take care of the tedious stuff of writing properties for you.
RegionToggle
Select the top or bottom of a region, and this method will take you to the other end of the region.
CloseAllOtherWindows
This macro will close all the open windows except the one you're working on.
As far as I can tell, you can only write macros in VB.NET, and you can't debug them when you're working on a C# project.
Install instructions
Save the CustomMacros.vb file to somewhere, then open the macros explorer by opening Visual Studio and pressing Alt+F11, and choosing File-->Add Existing, and choosing the saved macro.
It's all pretty self explanatory. This code comes without any guarantee but if you use it, please reference that you got it off me. Any comments appreciated. I've created these macros through a combination of editing pre-existing stuff, using the sample macros, recording macros, and plain old trial and error. I apologize if I've accidentally ripped off someone's work without acknowledging them - if I have, it certainly wasn't intentional.
Imports EnvDTE
Imports System.Diagnostics
Imports System.Text
Imports System.Environment
Imports System.Windows.Forms
Public Module CustomMacros
#Region "Class level variables"
Private Const cCommentConst As String = "//"
Private Const vbCommentConst As String = "'"
Private Const ownerLineConst As String = _
"This program and code is property" & _
" of [enter your name here]"
Private Const copyRightConst As String = _
"© 1988 to 2006. All Rights Reserved"
Private Const commentBorderConst As String = _
" --------------------------------" & _
"---------------------------------------------"
Private counter As Integer
#End Region
#Region "Insert Module Header"
''' <summary>
''' Inserts module header at top of document
''' as well as the legal spiel
''' </summary>
Sub InsertModuleHeader()
Dim projitem As ProjectItem = DTE.ActiveDocument.ProjectItem
Dim filecm As FileCodeModel = projitem.FileCodeModel
Dim element As CodeElement = filecm.CodeElements.Item(1)
Dim celttype As CodeType
'' For the sample, don't bother recursively descending all code like
'' the OutlineCode sample does. Just get a first CodeType in the
'' file.
If (TypeOf element Is CodeNamespace) Then
element = element.members.item(1)
End If
If (TypeOf element Is CodeType) Then
celttype = CType(element, CodeType)
Else
Throw New System.Exception("Didn't find a type" & _
" definition as first thing in file or find" & _
" a namespace as the first thing with" & _
" a type inside the namespace.")
End If
Dim edPoint As EditPoint = _
element.GetStartPoint(vsCMPart.vsCMPartHeader).CreateEditPoint()
'' Make doc comment start.
Dim commentStart As String = LineOrientedCommentStart()
If (commentStart.Length = 2) Then
commentStart = commentStart & commentStart.Chars(1) & " "
ElseIf (commentStart.Length = 1) Then
commentStart = commentStart & commentStart.Chars(0) & _
commentStart.Chars(0) & " "
End If
Try
DTE.UndoContext.Open("Insert Module Header")
If (element.Kind = vsCMElement.vsCMElementClass) Then
' Create comments
'Set cursor to top of document
edPoint.StartOfDocument()
edPoint.StartOfLine()
edPoint.Insert(NewLine & NewLine)
edPoint.LineUp()
edPoint.Insert(commentStart & commentBorderConst & NewLine)
edPoint.Insert(commentStart & element.Name & NewLine)
edPoint.Insert(commentStart & NewLine)
edPoint.Insert(commentStart & ownerLineConst & NewLine)
edPoint.Insert(commentStart & copyRightConst & NewLine)
edPoint.Insert(commentStart & NewLine)
edPoint.Insert(commentStart & "Author:" & _
ControlChars.Tab & UserName & NewLine)
edPoint.Insert(commentStart & "Date:" & _
ControlChars.Tab & _
System.DateTime.Now.ToLongDateString() & _
NewLine)
edPoint.Insert(commentStart & commentBorderConst)
End If
Finally
DTE.UndoContext.Close()
End Try
End Sub
#End Region
#Region "CommentRoutines"
''' <summary>
''' InsertDocComments for the selected procedures by calling
''' commentallprocedures with commentall set to false
''' </summary>
Public Sub CommentSelectedRoutines()
CommentAllRoutines(False)
End Sub
''' <summary>
''' InsertDocComments goes through the current
''' document using the VS Code Model
''' to add documentation style comments to each function.
''' </summary>
Public Sub CommentAllRoutines(Optional ByVal _
commentAll As Boolean = True)
Dim projItem As ProjectItem = DTE.ActiveDocument.ProjectItem
Dim filecm As FileCodeModel = projItem.FileCodeModel
Dim element As CodeElement = filecm.CodeElements.Item(1)
Dim elementtype As CodeType
Dim ts As TextSelection = ActiveDocument.Selection
'' Get the first CodeType in the file.
If (TypeOf element Is CodeNamespace) Then
element = element.members.item(1)
End If
If (TypeOf element Is CodeType) Then
elementtype = CType(element, CodeType)
Else
Throw New System.Exception("Type definition" & _
" or namespace definition" & _
" not found as first thing in file.")
End If
Dim edPoint As EditPoint = _
elementtype.GetStartPoint(vsCMPart.vsCMPartHeader).CreateEditPoint()
'' Make doc comment start.
Dim commentStart As String = LineOrientedCommentStart()
If (commentStart.Length = 2) Then
commentStart = commentStart & commentStart.Chars(1) & " "
ElseIf (commentStart.Length = 1) Then
commentStart = commentStart & _
commentStart.Chars(0) & _
commentStart.Chars(0) & " "
End If
'' Make this atomic & Use Try...Finally to ensure Undo works
Try
If commentAll Then
DTE.UndoContext.Open("Comment All Routines")
Else
DTE.UndoContext.Open("Comment Selected Routines")
End If
'' Iterate over code elements emitting doc comments for functions.
For Each element In elementtype.Members
If (element.Kind = vsCMElement.vsCMElementFunction) Then
'' Get Params.
Dim codefun As CodeFunction = element
edPoint.MoveToPoint(
codefun.GetStartPoint(vsCMPart.vsCMPartHeader))
If (edPoint.Line >= ts.TopPoint.Line And _
edPoint.Line <= ts.BottomPoint.Line) _
Or commentAll Then
Dim params As CodeElements = codefun.Parameters
'Insert comment
edPoint.Insert(NewLine)
edPoint.LineUp()
'Insert opening border
edPoint.Insert(ControlChars.Tab & commentStart & _
commentBorderConst & NewLine)
'Insert summary
edPoint.Insert(ControlChars.Tab & _
commentStart & "<summary>" & NewLine)
edPoint.Insert(ControlChars.Tab & commentStart & _
ControlChars.Tab & "Summary of " & _
element.Name & NewLine)
edPoint.Insert(ControlChars.Tab & _
commentStart & "</summary>")
Dim element2 As CodeElement
Dim cp As CodeParameter
'Insert parameters
For Each element2 In params
cp = element2
edPoint.Insert(NewLine)
edPoint.Insert(ControlChars.Tab & commentStart)
edPoint.Insert("<param name=" & _
cp.Name & "></param>")
Next 'param
'Insert History
edPoint.Insert(NewLine)
edPoint.Insert(ControlChars.Tab & _
commentStart & "<history>" & NewLine)
edPoint.Insert(ControlChars.Tab & _
commentStart & ControlChars.Tab)
edPoint.Insert("[" & UserName & "]" & ControlChars.Tab)
edPoint.Insert(System.DateTime.Now.ToLongDateString() & _
ControlChars.Tab & "Created" & NewLine)
edPoint.Insert(ControlChars.Tab & _
commentStart & "</history>" & NewLine)
'Insert closing border
edPoint.Insert(ControlChars.Tab & _
commentStart & commentBorderConst)
End If
End If 'we have a function
Next 'code elt member
Finally
DTE.UndoContext.Close()
End Try
End Sub
#End Region
#Region "InsertProperties"
''' <summary>
''' Create properties for selected class level
''' variables. NB these variables should all be
''' prefixed with a "_" in order to avoid case
''' sensitivity issues. Works for C# & VB.Net
''' </summary>
Public Sub InsertProperties()
Dim ts As TextSelection = DTE.ActiveWindow.Selection
Dim propertyList(ts.TextRanges.Count - 1) As String
Dim iIndex As Integer
Dim startPoint As EditPoint = ts.TopPoint.CreateEditPoint()
Dim endPoint As TextPoint = ts.BottomPoint
Try
Do While (startPoint.LessThan(endPoint))
propertyList(iIndex) = _
startPoint.GetText(startPoint.LineLength).Trim.Replace("New",_
"").Replace("()", "")
startPoint.LineDown()
startPoint.StartOfLine()
iIndex += 1
Loop
Catch ex As System.Exception
Debug.WriteLine(ex)
End Try
Try
DTE.UndoContext.Open("Insert Properties")
For iIndex = 0 To propertyList.GetUpperBound(0)
InsertProperty(propertyList(iIndex))
Next
Catch ex As System.Exception
Debug.WriteLine(ex)
Finally
DTE.UndoContext.Close()
End Try
End Sub
Private Sub InsertProperty(ByVal prop As String)
If prop.Length = 0 Then Exit Sub
Dim pType As String
Dim pMember As String
Dim pName As String
Dim ts As TextSelection
If LineOrientedCommentStart() = vbCommentConst Then
'Select properties for vbProperties
pMember = prop.Substring(prop.IndexOf(" "c)).Trim
'Remove any value assignment
If pMember.IndexOf("=") >= 0 Then
pMember = Left(pMember, pMember.IndexOf("=")).Trim
End If
pType = pMember.Substring(pMember.IndexOf("As"))
If pMember.StartsWith("_") Then
pName = pMember.Substring(1)
Else
pName = pMember
End If
pName = pName.Substring(0, pName.IndexOf("As")).Trim
pName = Left(pName, 1).ToUpper & pName.Substring(1)
pMember = pMember.Substring(0, _
pMember.Length - pType.Length).Trim
Else
'Select properties for c#
pType = prop.Substring(prop.IndexOf(" "c)).Trim
'Remove any value assignment
If pType.IndexOf("=") >= 0 Then
pType = Left(pType, pType.IndexOf("=")).Trim
End If
pMember = pType.Substring(pType.IndexOf(" "c)).Trim
pType = Left(pType, (pType.IndexOf(" "c))).Trim
If pMember.StartsWith("_") Then
pName = pMember.Substring(1)
Else
pName = pMember.Substring(0, 1).ToUpper & _
pMember.Substring(2)
End If
End If
ts = DTE.ActiveWindow.Selection
'Move cursor to two lines below selection
ts.MoveToPoint(ts.BottomPoint)
ts.NewLine(2)
ts.StartOfLine()
ts.Insert(GetPropertyString(pName, pType, pMember))
End Sub
Private Function GetPropertyString(ByVal propertyName _
As String, ByVal propertyType As String, _
ByVal propertyMember As String) As String
Dim propertyString As New StringBuilder
If LineOrientedCommentStart() = vbCommentConst Then
'Return vbformat
propertyString.Append(ControlChars.Tab & _
"Public Property " & propertyName & _
"() " & propertyType & NewLine)
propertyString.Append(ControlChars.Tab & _
ControlChars.Tab & "Get" & NewLine)
propertyString.Append(ControlChars.Tab & _
ControlChars.Tab & ControlChars.Tab & _
"Return " & propertyMember & NewLine)
propertyString.Append(ControlChars.Tab & _
ControlChars.Tab & "End Get" & NewLine)
propertyString.Append(ControlChars.Tab & _
ControlChars.Tab & "Set (ByVal Value " & _
propertyType & ")" & NewLine)
propertyString.Append(ControlChars.Tab & _
ControlChars.Tab & ControlChars.Tab & _
propertyMember & "= Value" & NewLine)
propertyString.Append(ControlChars.Tab & _
ControlChars.Tab & "End Set" & NewLine)
propertyString.Append(ControlChars.Tab & _
"End Property" & NewLine)
Else
'Return c# format
propertyString.Append(ControlChars.Tab & _
ControlChars.Tab & "public " & _
propertyType & " " & propertyName & NewLine)
propertyString.Append(ControlChars.Tab & _
ControlChars.Tab & "{" & NewLine)
propertyString.Append(ControlChars.Tab & _
ControlChars.Tab & ControlChars.Tab & _
"get" & NewLine)
propertyString.Append(ControlChars.Tab & _
ControlChars.Tab & ControlChars.Tab & _
"{" & NewLine)
propertyString.Append(ControlChars.Tab & _
ControlChars.Tab & ControlChars.Tab & _
ControlChars.Tab & "return " & _
propertyMember & ";" & NewLine)
propertyString.Append(ControlChars.Tab & _
ControlChars.Tab & ControlChars.Tab & _
"}" & NewLine)
propertyString.Append(ControlChars.Tab & _
ControlChars.Tab & ControlChars.Tab & _
"set" & NewLine)
propertyString.Append(ControlChars.Tab & _
ControlChars.Tab & ControlChars.Tab & _
"{" & NewLine)
propertyString.Append(ControlChars.Tab & _
ControlChars.Tab & ControlChars.Tab & _
ControlChars.Tab & propertyMember & _
"=Value;" & NewLine)
propertyString.Append(ControlChars.Tab & _
ControlChars.Tab & ControlChars.Tab & _
"}" & NewLine)
propertyString.Append(ControlChars.Tab & _
ControlChars.Tab & "}" & NewLine)
End If
Return propertyString.ToString
End Function
#End Region
#Region "IncrementFields"
''' ------------------------------------------------------
''' <summary>
''' Loop through selected lines looking
''' for tabIndex=". The first number found after the
''' tabIndex sets the index. Subsequent matches
''' in the selected text will be incremented by 1.
''' </summary>
''' <param name="ds"></param>
''' <remarks>
''' </remarks>
''' <history>
''' [dgera] 05/01/2006 Created
''' </history>
''' ------------------------------------------------------
Public Sub IncrementTabIndexes()
IncrementLine("tabIndex=" & """")
End Sub
Public Sub IncrementUserText()
Dim userText As String
userText = InputBox("Enter the text to search" & _
" for and increment " & NewLine & _
"(e.g. tabIndex=" & """" & ")" & NewLine _
& NewLine & "n.b. searching is" & _
" case sensitive", "Custom Increment")
If userText.Length > 0 Then
IncrementLine(userText)
End If
End Sub
''' ------------------------------------------------------
''' <summary>
''' Loop through selected lines and call
''' GetIncrementedLine to increment the searchstring
''' </summary>
''' <param name="ds"></param>
''' <remarks>
''' </remarks>
''' <history>
''' [dgera] 05/01/2006 Created
''' </history>
''' ------------------------------------------------------
Private Sub IncrementLine(ByVal searchString As String)
Dim Insertion As New StringBuilder
Dim StartLine, EndLine, Temp, numLines, i As Integer
counter = -1
Try
DTE.UndoContext.Open("Increment Fields")
StartLine = DTE.ActiveDocument().Selection.TopLine
EndLine = DTE.ActiveDocument().Selection.BottomLine
numLines = EndLine - StartLine
If EndLine < StartLine Then
Temp = StartLine
StartLine = EndLine
EndLine = Temp
End If
'Select each line and iterate. I know
'it would be more efficient to not select each line
'but if you figure out how to do it please let me know
For i = StartLine To EndLine
ActiveDocument().Selection.GotoLine(i)
ActiveDocument().Selection.SelectLine()
Insertion.Append(GetIncrementedLine(
DTE.ActiveDocument().Selection.text(), _
searchString))
Next
'Set selection back to the first line
ActiveDocument().Selection.GotoLine(StartLine)
ActiveDocument().Selection.SelectLine()
'Then select all the originally selected lines.
'NB I tried to hold the original selection in
'memory as a textSelection object but it works
'byRef so the above select statements
'caused it to change
DTE.ActiveDocument().Selection.LineDown(True, numLines)
'Delete selected lines and replace with new lines
DTE.ActiveDocument.Selection.Delete()
DTE.ActiveDocument.Selection.Insert(Insertion.ToString)
'Set selection back to the top one last time
ActiveDocument().Selection.GotoLine(StartLine)
ActiveDocument().Selection.SelectLine()
'now highlight the lines again
'to make it obvious to the user
DTE.ActiveDocument().Selection.LineDown(True, _
numLines)
Catch ex As System.Threading.ThreadAbortException
'Has been interrupted by the user so disregard
Catch ex As System.Exception
MessageBox.Show("An error has occurred" & _
" in the IncrementLineMacro: - " & ex.Message)
Finally
DTE.UndoContext.Close()
End Try
counter = -1
End Sub
''' -------------------------------------------
''' <summary>
''' Search line for the searchstring.
''' If not found return
''' as is else increment the integer
''' of searchstring and return
''' <summary>
''' <param name="ds"></param>
''' <remarks>
''' </remarks>
''' <history>
''' [dgera] 05/01/2006 Created
''' </history>
''' -------------------------------------------
Private Function GetIncrementedLine(ByVal line As String, _
ByVal searchString As String) As String
Dim midString, returnString, numString, _
leftString, rightString As String
Dim startIndex As Integer = InStr(line, searchString)
'Position of the start of the searchstring in the line
Dim endIndex, endSearchString As Integer
If startIndex > 0 Then
endSearchString = startIndex + searchString.Length
endIndex = InStr(endSearchString, line, _
"""", CompareMethod.Text)
numString = Mid(line, endSearchString, _
endIndex - endSearchString)
If counter = -1 Then
counter = Integer.Parse(numString)
Else
counter = counter + 1
End If
midString = searchString & counter & """"
leftString = Left(line, startIndex - 1)
rightString = Mid(line, endIndex + 1)
returnString = leftString & _
midString & rightString
Return returnString
End If
Return line
End Function
#End Region
#Region "Region Toggle"
''' <summary>
''' Select the top or bottom of a region and
''' this method will take you
''' to the other end of the region
''' </summary>
Public Sub RegionToggle()
Dim regionSearch As String
Dim searchUp As Boolean
Dim ts As TextSelection = DTE.ActiveWindow.Selection
ts.SelectLine()
If ts.Text.IndexOf("#End Region") >= 0 Or _
ts.Text.IndexOf("endregion") >= 0 Then
searchUp = True
End If
If searchUp Then
ts.LineUp()
ts.FindText("#", _
vsFindOptions.vsFindOptionsBackwards)
Else
ts.FindText("#")
End If
End Sub
#End Region
#Region "Close all other windows"
''' <summary>
''' Leave the current window open and close all others
''' </summary>
Public Sub CloseAllOtherWindows()
Dim currentDoc As String
currentDoc = DTE.ActiveDocument.Name()
Dim doc As EnvDTE.Document
For Each doc In DTE.Documents
If doc.Name <> currentDoc Then
doc.Close()
End If
Next
DTE.Windows.Item(currentDoc).Activate()
End Sub
#End Region
#Region "Common Helper Methods"
''' <summary>
''' Figures out if you're using vb.net or csharp
''' and returns the relevant comment string
''' </summary>
Private Function LineOrientedCommentStart(Optional _
ByVal doc As Document = Nothing) As String
If (doc Is Nothing) Then
doc = DTE.ActiveDocument
End If
Dim ext As String = doc.Name
If (ext.EndsWith(".cs")) Then
Return cCommentConst
ElseIf (ext.EndsWith(".cpp")) Then
Return cCommentConst
ElseIf (ext.EndsWith(".h")) Then
Return cCommentConst
ElseIf (ext.EndsWith(".vb")) Then
Return vbCommentConst
ElseIf (ext.EndsWith(".idl")) Then
Return cCommentConst
End If
End Function
''' <summary>
''' Figures out if you're using vb.net or csharp
''' </summary>
Private Function IsCSharp() As Boolean
Return (LineOrientedCommentStart() = cCommentConst)
End Function
#End Region
End Module
Points of Interest
Creating macros is pretty easy to do once you get started - and the ability to record things can help enormously.
The only thing I'm not really happy with is in the incrementing routines. I am retrieving the text of each line by selecting each line. It would be better if I could select it all and split it using the carriage return character, but I found that it was prone to be buggy, and sometimes the split returned multiple lines. If anyone finds a way to improve on this, please let me know.
Cheers, Darryl Gera.