I delete *.suo’s all the time, before checking into Subversion…and so this tip is great: “Just thought I'd let you know that if you delete the .suo file, Visual Studio automatically sets the first project mentioned in the solution file as the startup project. Open the .sln in a text editor and move the Project tag for the startup project up to the top spot.”
|
This second entry (the first was entitled Why consider sitting for an MCTS or MCP Certification) is called Automating Visual Studio 2008, where (after a long lead reminding people of what’s to like about Visual Studio) I get around to talking about using Macros to automate Visual Studio in order to make everyday operations a little easier. The example I gave was to use macros to decorate the public properties of Controls with the necessary designer and serialization attributes. Give it a read, and leave a comment as to what you thought of it. PS: It appears that for formatting purposes, I should have handed in the source document with code even more terse code in order to not have rollover of the source code. Sorry ‘bout that, Mauricio…must of been a pain trying to get it to fit…
|
The reference book for learning how to Automate Microsoft, including Macros appears to be this one: Inside Microsoft Visual Studio .NET 2003 ISBN-13: 9780735618749 Also, and maybe more focused?: Finally, don’t forget that there is a forum dedicated to all this, in case you get stuck: Visual Studio Extensibility Forum Links:
|
An often under-utilized feature of Visual Studio is the Task window. Remember the Configuration option you saw when you first got Visual Studio? That’s where you define the Tokens that are automatically recognized in your comments. Where do you see them? In the Task Window. Why are they not visible? Because it has to be changed from User Tasks to Comments: 
|
|
|
|
|
You’ve seen that you can initiate a Build from a macro , or switch focus to window with something like: DTE.ExecuteCommand("View.CommandWindow")
But…how many other commands are there? Lots! Where can one get a list of them?
Easy…just use the Immediate Window:
That said…I’m not a huge fan of DTE.ExecuteCommand (I’ve never used it) (see here for a good demo of why) but it maybe could be useful in a squeeze.
|
I just posted in the previous example a macro to collapse all nodes in the Solution Explorer and tidy it up. But related to that is the concept of tracking the current document in the Solution Explorer. I know that Visual Studio can do it, but I wanted to see what it would take to do it on demand, via a macro. Here’s my solution: Public Sub TrackProjectItem()
If (DTE.ActiveDocument Is Nothing) Then
Return
End If
Dim solution As Solution = DTE.Solution
If solution.IsOpen = False Then
Return
End If
'Bug correction:
solution.FindProjectItem(DTE.ActiveDocument.FullName).ExpandView()
Dim FileName As String = DTE.ActiveDocument.FullName
Dim SolutionExplorerPath As String
Dim foundItem As UIHierarchyItem = _
FindItem(FileName, SolutionExplorerPath)
If foundItem Is Nothing Then
Return
End If
Dim holdWindow As Window = DTE.ActiveWindow
'Activate the Solution Explorer window:
DTE.Windows.Item(Constants.vsWindowKindSolutionExplorer) _
.Activate()
'Select the Selected Node:
foundItem.Select(vsUISelectionType.vsUISelectionTypeSelect)
holdWindow.Activate()
End Sub
Public Function FindItem(ByVal fileName As String, _
ByRef solutionExplorerPath As String) _
As UIHierarchyItem
If (String.IsNullOrEmpty(fileName)) Then _
Throw New ArgumentException("fileName")
' Get the the Solution Explorer Window:
Dim solutionExplorer As UIHierarchy = _
CType( _
DTE.Windows.Item(Constants.vsext_wk_SProjectWindow).Object, _
UIHierarchy)
Return FindItem(solutionExplorer.UIHierarchyItems.Item(1), _
fileName, _
solutionExplorerPath)
End Function
Public Function FindItem(ByVal item As UIHierarchyItem, _
ByVal fileName As String, _
ByRef solutionExplorerPath As String) _
As UIHierarchyItem
If (item Is Nothing) Then _
Throw New ArgumentNullException("item")
If (String.IsNullOrEmpty(fileName)) Then _
Throw New ArgumentException("fileName")
Dim TypeName As String = _
Microsoft.VisualBasic.Information.TypeName(item.Object)
If TypeName = "ProjectItem" Then
'Only check for filenames if the the node
' is a project Item, and not a
'SolutionClass or Project
Dim projectitem As EnvDTE.ProjectItem = _
CType(item.Object, EnvDTE.ProjectItem)
For i As Integer = 1 To projectitem.FileCount
If projectitem.FileNames(CType(i, Short)) = fileName Then
'Found! this is the node...
Dim pathParts As New List(Of String)
Dim sb As New System.Text.StringBuilder()
Dim parentItem As UIHierarchyItem = item
Do Until (parentItem Is Nothing)
pathParts.Insert(0, parentItem.Name)
Dim x As UIHierarchyItems = _
parentItem.UIHierarchyItems
Try
parentItem = _
CType(parentItem.Collection.Parent, UIHierarchyItem)
'This doesn't work: just gives back self
'and creates eternal loop:
'parentItem = parentItem.UIHierarchyItems.Parent
Catch
'Oops...the Parent is a ProjectItem,
'so cast fails...
parentItem = Nothing
End Try
Loop
solutionExplorerPath = _
String.Join("\", pathParts.ToArray())
Return item
End If
Next i
End If
'If it wasn't this node, then lets look at children:
For Each childItem As UIHierarchyItem In item.UIHierarchyItems
Dim result As UIHierarchyItem = _
FindItem(childItem, fileName, solutionExplorerPath)
If Not (result Is Nothing) Then
'Found it! great...get out...
'recursively passing back node till we are out:
Return result
End If
Next
End Function
Links:
http://social.microsoft.com/Forums/en-US/vsx/thread/1f9d7cb2-cfbc-44b7-88e5-6faf564cdc74
|
Here’s a macro that solves something that drove me nuts for years… You know when you’re working on a large solution that has many projects and hundreds of files, and you just want to collapse all the nodes in the Solution Explorer? Then this is the macro you need… 'Collapse All Nodes in the SolutionExplorer
'using using Depth first algorithm
Sub CollapseAllNodes()
' Get the the Solution Explorer Window:
Dim solutionExplorer As UIHierarchy = _
DTE.Windows.Item(Constants.vsext_wk_SProjectWindow).Object
' Check if there is any open solution
If (solutionExplorer.UIHierarchyItems.Count = 0) Then
'No Solution -- nothing to collapse:
Return
End If
'Get the Solution Node:
Dim uiSolutionNode As UIHierarchyItem = _
solutionExplorer.UIHierarchyItems.Item(1)
Try
'Turn off updating for a second:
solutionExplorer.DTE.SuppressUI = True
DeepCollapseNode(uiSolutionNode)
uiSolutionNode.UIHierarchyItems.Expanded = True
Catch
'...
Finally
'Turn updating back on:
solutionExplorer.DTE.SuppressUI = False
End Try
End Sub
'Collapses all items, starting from deep back up to top:
Public Sub DeepCollapseNode(ByVal node As UIHierarchyItem)
If (node Is Nothing) Then _
Throw New System.ArgumentException(node)
'Go down to deepest part first:
For Each childItem As UIHierarchyItem In node.UIHierarchyItems
DeepCollapseNode(childItem)
Next
'Collapse only if expanded, and has children:
If (node.UIHierarchyItems.Count > 0) And _
node.UIHierarchyItems.Expanded = True Then
node.UIHierarchyItems.Expanded = False
End If
End Sub
|
Sometimes you need to get a handle on the class that the mouse is sitting somewhere within… In other words, the active class within the active document, within the active project… Function GetActiveClass() As CodeClass
Dim currentProject As Project
Dim codeElement As CodeElement
Dim result As CodeClass
'Get the active document:
Dim activeDocument As EnvDTE.Document = _
DTE.ActiveDocument
If (activeDocument Is Nothing) Then _
Return Nothing
'to get to its ProjectItem property:
Dim currentItem As ProjectItem _
= activeDocument.ProjectItem
' Get the current currentDocSelection
' within the active document:
Dim currentDocSelection As TextSelection = _
CType(DTE.ActiveDocument.Selection, TextSelection)
' And within the active selection, figure
' out where the current cursor location
Dim currentDocPoint As TextPoint _
= currentDocSelection.ActivePoint
Try
Dim currentItemFileCodeModel As FileCodeModel _
= currentItem.FileCodeModel
' Try to read the current location,
' as a code element.
'Note that it can be many things at once
' (a property AND a class at same time...get it?)
result = _
CType( _
currentItemFileCodeModel.CodeElementFromPoint( _
currentDocPoint, vsCMElement.vsCMElementClass), _
CodeClass)
Catch ex As Exception
'Bummer...Not a class file?
result = Nothing
End Try
Return result
End Function
Links:
Visual Studio Macros- Get the Active Project
|
If you want to figure out what project you’re in, use the following code in a macro to query the DTE: Function GetActiveProject() As Project
'Get the active document:
Dim activeDocument As EnvDTE.Document = _
DTE.ActiveDocument
If (activeDocument Is Nothing) Then _
Return Nothing
'to get to its ProjectItem property:
Dim currentItem As ProjectItem _
= activeDocument.ProjectItem
'in order to get to its ContainingProject
Dim result As Project _
= currentItem.ContainingProject
Return result
End Function
|
Sometimes you need to know the language in which a class was written, in order to generate code fragments in the right syntax. Here’s a routine that will help: Public Function GetCodeElementLanguage( _
ByVal codeElement As CodeElement) As String
If (codeElement Is Nothing) Then _
Throw New ArgumentException("codeElement")
Select Case codeElement.Language
Case "{B5E9BD33-6D3E-4B5D-925E-8A43B79820B4}"
Return "VB"
Case "{B5E9BD34-6D3E-4B5D-925E-8A43B79820B4}"
Return "CS"
Case "{B5E9BD32-6D3E-4B5D-925E-8A43B79820B4}"
Return "VC++" 'Unmanaged?
Case "{B5E9BD36-6D3E-4B5D-925E-8A43B79820B4}"
Return "VC++" 'Managed?
Case Else
Return "UNKNOWN"
End Select
End Function
|
This may be useful to someone at some point. Get All CodeProperty elements within a given CodeClass: Public Function GetAllCodePropertiesInCodeClass( _
ByVal codeClass As CodeClass) As CodeProperty()
If (codeClass Is Nothing) Then _
Throw New ArgumentException("codeClass")
Dim result As New List(Of CodeProperty)()
'Loop through all members of a class
'getting only the CodeElements that are of the
'right type:
For Each codeElement As CodeElement In CodeClass.Members
If codeElement.Kind = vsCMElement.vsCMElementProperty Then
result.Add(codeElement)
End If
Next
Return result.ToArray()
End Function
Further Reading:
Once you’ve found all properties, maybe you’ll need to then find Fields backing the Properties, in order to cross-relate them.
|
I’ve just been reworking a little macro that automatically chooses Attribute values to decorate Properties with. One of the Attributes I wanted to automatically generate for any given Property was the DescriptionAttribute, which should have some short splurge of what the property does. In most cases, the text for the DescriptionAttribute is the same exact text as the code comment summary for the property anyway, which makes it easy for us to just scan the comments, extract the summary, and call it a day…about the same operation as any human would do… The code to do the above is nothing much really, but it’s a simple demonstration on how to load and parse the code comment for any given CodeElement: 'Invoked by: AutoAttributesControlProperty
Private Function SuggestDescriptionAttributeValue( _
ByVal codeProperty As CodeProperty) As String
If (codeProperty Is Nothing) Then _
Throw New ArgumentException("codeProperty")
Dim result As String
'Right...So how would one automatically
'develop text with which to describe a property?
'Turns out that its probably not necessary to
'generate text (cf: DocGhost), but just use whatever
'we can find already there...
'One place I would look for one is try to
'reuse the description that was probably
'already put on the property by the programmer...
'Did the Property have a comment already associated to it?
If (codeProperty.DocComment <> String.Empty) Then
'Yes...the Property has an associated comment...
'so, theoretically, it should
'be an Xml document that can be parsed...
'So load it up!
Dim xmlDoc As XmlDocument = New XmlDocument()
xmlDoc.LoadXml(codeProperty.DocComment)
'And look for a node called summary:
Dim xmlNode As XmlNode = xmlDoc.SelectSingleNode("//summary")
If Not (xmlNode Is Nothing) Then
'If there was a node, then we want the inner text
'we don't want any para tags or other stuff
'in the DescriptionAttribute:
result = xmlNode.InnerText
'And we don't want newlines that will cause havoc
'with the built in Xml documentation:
result = _
result.Replace(Environment.NewLine, "")
End If
End If
Return result
End Function
Links
|
I’ve been reworking a couple of Visual Studio macros that I’ve written and come across a couple of routines that might be useful to someone else. This method takes a CodeElement that defines a Property (ie, a CodeProperty) and tries to find an associated backing Field. For example, given the CodeProperty defining MyVar, it will find the CodeVariable defining _MyVar: public string MyVar {
get {
return _MyVar;
}
set {
_MyVar = value;
}
}
private string _MyVar;
The macro Function for this is as follows:
'Invoked by: AutoAttributesControlProperty
Private Function FindFieldBackingProperty(ByVal codeProperty As CodeProperty) _
As CodeVariable
If (codeProperty Is Nothing) Then _
Throw New ArgumentException("codeProperty")
Dim propName As String = codeProperty.Name
'To find fields, we need to find the
'class that is parent to the given property:
Dim codeClass As CodeClass = codeProperty.Parent
For Each tmp As CodeElement In codeClass.Members
If Not TypeOf (tmp) Is CodeVariable Then
Continue For
End If
'The common way of naming a backing field
'is to use the same name, just different case, or
'start it with an underscore:
If tmp.Name.Equals( _
propName, _
StringComparison.InvariantCultureIgnoreCase) _
Or _
tmp.Name.Equals( _
"_" & propName, StringComparison.InvariantCultureIgnoreCase) _
Then
'Found it:
Return CType(tmp, CodeVariable)
End If
Next
Return Nothing
End Function
PS: Change the match terms to suit your work environment of course…
|
|
|
Obviously, to decrease setup costs, VWD would help in some cases. The only hang was that when I looked into it for one situation when VWD came out, it turned out that it couldn't be used for control developers that worked with embedded resources. Could be used by others higher up, moving the controls around -- just not the guys and gals in charge of making the custom controls. Why? I'm referring to VWD's lack of embedded resource handling system. What I'm getting at, is that I was at first surprised that I couldn't find a way to do what I always do: namely, right-click a *.js file and embed it. Of course...that doesn't exist in an environment that doesn't create assemblies...such as VWD (there's no where to embed into...) But the fact that all the official examples on the .NET website demonstrated using the resource tags, etc. worried me that I had overlooked something... Two or more years later... I just came across this link (WAY after when I needed it ...grumble) that seems to make it more official that it can't be done that way. ... In case I lose it again: http://www.asp.net/AJAX/Documentation/Live/tutorials/EmbedScriptFile.aspx This post has something interesting on it regarding that... <<<Files marked as embedded resources are moved to 'App_GlobalResources' folder except auto generated ones. This may cause changing the code referring resources>>>
|
Introduction Creating core shared dll's (eg: XAct.Web.Controls.Common) has the advantage of having one place to add essential static methods and classes, at the cost of having a dependency in your code. Which is fine -- until you are working on another application, and make breaking changes to the common assembly, knocking out all your previous applications... This situation was happening so much that I wanted to find a way to be alerted to such situations. That's where CruiseControl.NET comes in.
Read More »
|
Another Plugin that is really useful when developing in VS 2008: check out testdriven.net
|
A little while ago I got frustrated that I renamed an assembly from its first name (Example.csproj) to another name (FinishedControl.csproj) but the Xml documentation remained as (Example.xml)...
The problem is that the DocumentFile node in the *.csproj file is hard coded.
So I pulled out Textpad, and did a regex search/replace of all csproj files on the hard drive, so that it was dynamically based on the assembly name...
Read More »
|