Tips, tricks and all the info about Microsoft Visual Studio 2008

Automating Visual Studio 2008

, posted: 16-Mar-2009 09:00

This is ultimately going to be a demonstration on how to develop macros to automate Visual Studio 2008, but before we get to that I would like to take a second to talk about Visual Studio proper.

Visual Studio Features we can’t live without
Developing code with Visual Studio is a breeze compared to other Integrated Development Environments (IDE’s) due to the many features it provides:

     Efficient Navigation:
Visual Studio’s Code Editor provides efficient means to navigate around large files (using collapsible code blocks), large projects (using Bookmarks) that contain large numbers of classes and/or properties (using Object Explorer) as well as solutions that contain numerous projects (using Solution Explorer).

     Syntax Coloring:
Coloring the Code Editor’s text improves code comprehension, increasing productivity. It’s difficult to explain how useful this is – but it’s immediately apparent once you have to do without it when going back to a more primitive code editor.

     Background Compilation:
In addition to syntax coloring, Visual Studio provides immediate syntax warnings and errors (as green and red wavy underlines) by compiling newly entered code in the background.

     Code Completion:
Many other IDE’s provide some form of code completion – but none as complete and intuitive as IntelliSense.  Not only does Visual Studio’s code completion system work for all the built in languages (C++, C#, VB.NET, and next year -- in 2010 -- F# and PHP as well) but also XML, CSS, XAML, and any other number of languages for which extensions for Visual Studio are available.

     Code Creation:
It was named Visual Studio, rather than Code Studio, or Text Studio, because in addition to the traditional text based way of entering code, it also offers a Visual way of developing the code and metadata necessary to describe UI’s, schemas, classes and mappings.

It does this by providing intuitive graphical designers for developing UI forms and controls for WinForm, WPF, Web, and Silverlight applications, making the process trivially easy by supporting drag and drop placement of standard and custom controls, separation of design layout code and logic code, with automatic event wiring between the two following the event driven programming model, support for databinding, as well as custom popup property editors and design tasks.

In addition to UI designers, Visual Studio provider database schema designers (to graphically create and design database schemas as well as queries), a class designer (to graphically develop or document classes and their interactions), and a mapping designer (to graphically design the mapping between database schemas and the code entities that encapsulate the data).

     Code Commenting:
Anybody who has had to pick up and continue the work of others – or had to go back to their own code after six months -- knows the importance of documentation. Visual Studio’s use of XML based comment tags was a fantastic addition to our arsenal.

     Code Reuse:
Although not a great fan of code snippets (saved templates for repetitive code) because I believe that the increased effort required to create and maintain them introduces code latency (bugs found and fixed in production code are not often reapplied to the original templates as well), Visual Studio offers a built in code snippet creation and management solution.

     Code Refactoring:
Beginning with VS2005, several refactoring tools are built directly into the IDE, which are accessible from the Code Editor’s context menu, or the main menu’s Refactor menu (which is only visible when the Code Editor has focus).

The refactoring tools offered are sparse, but helpful:

     o    Extract Method: Defines a new method, based on a selection of code statements.
     o    Encapsulate Field: Turns a public field into a private field, wrapping it with a public property.
     o    Extract Interface: Defines a new interface based on the current class’ public properties and methods.
     o    Reorder Parameters: Provides a way to reorder member parameters, as well as the arguments of all references to it throughout a project.
     o    Remove Parameters: Removes a member’s parameter, and the argument from of all references to it throughout a project.
     o    Rename: This allows you to rename a code token (method, field, etc.), and all references to it throughout  a project.
     o    Promote Local Variable to Parameter: Moves a local variable to the parameter set of the defining method.

Visual Studio Automation
In addition to the above mentioned features, Visual Studio’s IDE is also completely automatable, in much the same way as any Microsoft Office product. Automation is achieved by using the DTE (design-time environment) object model to manage the following:

     o    Window objects (used to close, rearrange, or otherwise manipulate open windows)
     o    Document objects (used to edit text)
     o    Solution and project objects (used to manage the underlying files and project collection)
     o    Code-manipulation objects (used to analyze your project’s code constructs)
     o    Tool-window objects (used to configure the IDE’s interface)
     o    Debugging objects (used for tasks such as creating breakpoints and halting execution)
     o    Event objects (used to react to IDE events)
     o    Automation can be achieved with full blown Extensions for Visual Studio, or in a more ad-hoc way, using Macros.

Extensions
The exposure of the DTE means that in addition to Visual Studio’s built in tools and features (such as Code Snippets and Refactoring tools), one can download and install a whole selection of extension tools. Their quality range from the truly mediocre (and worse) to the very useful.

Note: In many ways, this is Microsoft’s distribution strategy: they provide a fantastic extensible shell, with enough features to be better than their competitors, but still leave as much space for 3rd parties to develop extensions to provide additional value. This can sometimes be a little infuriating, as one would sometimes like to see them add features we think of as essential, but also understandable, from their business point of view.

Two non-free extensions that are constantly highly rated by end users are Resharper (R#) v4.1 and CodeRush. Resharper takes refactoring to a completely higher level, and CodeRush provides a more seamless means of creating, managing, and using code templates.

Note: Although not an extension, if you are satisfied with the code snippet solution provided in Visual Studio 2008, and not interested in using CodeRush, you may be interested in Snippet Editor.

Another extension that is very highly appreciated -- and happens to be free -- is Roland Weigelt's GhostDoc, which improves the Code Commenting capabilities of Visual Studio, by automatically documenting your code with text heuristically developed from the name and type of the Property itself, producing output similar to the following:

#region Properties
private Uri _ImagePath;

///
/// Gets or sets the image path.
///
/// The image path.
public Uri ImagePath {
  get { return _ImagePath; }
  set { _ImagePath = value; }
}
#endregion


Even if GhostDoc can easily be abused to make absolutely useless documentation (garbage in / garbage out) – it can save an enormous amount of time under certain circumstances.

Note: At the very least it ultimately leads to developers choosing clearer names for their properties and methods – if only to nudge GhostDoc in the right direction, which adds clarity to code in general.

Macros
Finally, even though there are many free and not so free extensions available for Visual Studio, the results they produce may not be to your liking.  Sometimes the task you wish to achieve is specific to your work environment, and therefore you never will find an extension that will do it.
You could develop your own extension to fulfill your needs, but the development time required to create a full blown extension is not trivial.

In many cases the creation of a custom macro is a more appropriate solution.

A Macro to decorate Control Properties with ComponentModel Attributes
An example of an automatable task that would not be worth the trouble of developing a complete extension, but could appropriately be addressed by a macro is the decoration of control properties with ComponentModel attributes.

For example, in the above code example, the ImagePath property, if part of a Control, needs to be decorated with at least one or two attributes:

     o    System.ComponentModel.CategoryAttribute
     o    System.ComponentModel.DescriptionAttribute

and potentially, with:

     o    System.ComponentModel.BrowsableAttribute (only required if the public property should be hidden from the Properties Window, since its default value is true anyway)

In addition to the above attributes, the following should also be considered (they will depend to some degree on whether the control is for intended for use in WinForms or WebForms):

     o    System.ComponentModel.EditorAttribute
     o    System.ComponentModel.DesignerSerializationVisibilityAttribute
     o    System.Web.UI.PersistenceModeAttribute

Note:To keep things simple, we won’t be addressing these last 3 attributes in this demonstration.

The final result could look something like:

///
/// Gets or sets the image path.
///
/// The image path.
[
Category("Appearance"),
Description("Gets or sets the image path."),
DefaultValue(null),
]
public Uri ImagePath { get; set; }


Leaving this to be done by hand is error prone at best, and the kind of task that an automation macro is perfect for.

Let’s create one.
 
Launching the Macro IDE
The process of creating a macro for Visual Studio, will be familiar to anyone who has created macros for Microsoft’s Office products (Excel, Word, Outlook, etc.).

The Visual Studio’s Macro Editor is launched either by pressing Alt-F11, or selecting Tools/Macros/Macro IDE… from Visual Studio’s menu:
 

 
For this example, we’re start by creating a new Module file to contain our public subroutine, and call it XSS_AttributeManagement:
 


Macros are simply Public Parameter less Methods within Modules
For your macro to be later callable from a Visual Studio button or Shortcut key, it has to be defined as Public and a Sub that takes no arguments, so we’ll create in our newly created Module, a Sub called AutoAttributeControlProperty, and as long as its parameter less, and public, it will immediately become visible in Visual Studio’s Macro explorer:


 
The Goal of the Macro
Breaking down the functionality we need to achieve, we need a macro that does basically the following:

     o    Figures out what Document is currently being shown in the Code Explorer (ie, the DTE.ActiveDocument)
     o    Figure out the CodeElement the cursor is over
     o    See if the CodeElement can be typed as a CodeProperty (rather than a CodeVariable or CodeFunction, or white space within a CodeClass) before continuing.
     o    Figuring out where the start of the CodeProperty is, and create an EditPoint from it.
     o    Insert new Attributes at the new EditPoint.

The following annotated code does this:

Imports System
Imports EnvDTE
Imports EnvDTE80
Imports EnvDTE90
Imports System.Diagnostics

'Add ref to System.Xml Namespace
'after having added System.XML.dll
'as a Reference to 'MyMacros':
Imports System.Xml


Public Module XSS_AttributeManagement

    'Macro to add Attributes to properties for IDE integration.

    Sub AutoAttributeControlProperties()

        Dim fileCodeModel As FileCodeModel = _
          DTE.ActiveDocument.ProjectItem.FileCodeModel

        ' Get the current selection, within the
        ' current document:
        Dim selection As TextSelection = _
          DTE.ActiveDocument.Selection

        ' From the selection, get the active point
        ' which is the current cursor location:
        Dim point As TextPoint = selection.ActivePoint

        ' Try to get the code element -- of type Property --
        ' at the current location:
        Dim codeElement As CodeElement
        Try
            codeElement = _
              fileCodeModel.CodeElementFromPoint( _
                point, vsCMElement.vsCMElementProperty)

        Catch ex As Exception
            'CodeElementFromPoint is a bit rude, and
            'throws an exception if it doesn't work...
            codeElement = Nothing
        End Try


        'If it's null...then we're not on a Property:
        If (codeElement Is Nothing) Then
            MsgBox( _
              String.Format( _
               "Place cursor on a Property before running this macro ('{0}').", _
               System.Reflection.MethodBase.GetCurrentMethod().Name), _
              MsgBoxStyle.Exclamation)
            Return
        End If

        'Good...
        'The element we are on is a Property,
        'so let's type it for easier subsequent use:
        Dim codeProperty As CodeProperty = codeElement


        'From the property, extract its name for later use:
        Dim propName As String = codeProperty.Name

        'For comparison purposes only, let's lowercase it:
        Dim propNameLowered = propName.ToLower()

        'Right...
        'Let's start trying to fill in the bits...

        'If all goes well, we want a Category and Description
        'for the property:
        Dim category As String = String.Empty
        Dim description As String = String.Empty

        'Note to self...
        'Is there a Switch/Case in VB.NET ?

        If (propNameLowered.StartsWith("border")) Then
            category = "Appearance"
        ElseIf (propNameLowered.StartsWith("background")) Then
            category = "Appearance"
        ElseIf (propNameLowered.EndsWith("width")) Then
            category = "Appearance"
        ElseIf (propNameLowered.Contains("color")) Then
            category = "Appearance"
        ElseIf (propNameLowered.EndsWith("height")) Then
            category = "Appearance"
        ElseIf (propNameLowered.Contains("image")) Then
            category = "Appearance"
        ElseIf (propNameLowered.EndsWith("icon")) Then
            category = "Appearance"
        ElseIf (propNameLowered.StartsWith("show")) Then
            category = "Appearance"
        ElseIf (propNameLowered.Contains("style")) Then
            category = "Appearance"
        ElseIf (propNameLowered.StartsWith("allow")) Then
            category = "Behavior"
        ElseIf (propNameLowered.StartsWith("use")) Then
            category = "Behavior"
        ElseIf (propNameLowered.EndsWith("data")) Then
            category = "Data"
        End If

        'Right...
        'should have a piece of text suitable for a CategoryAttribute
        'But don't yet have a suitable piece of text for a decent
        'DescriptionAttribute...

        'One place I would look for one is try to reuse the description
        'that was 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 System.Xml.XmlDocument = _
              New System.Xml.XmlDocument()
            xmlDoc.LoadXml(codeProperty.DocComment)

            'And look for a node called summary:
            Dim xmlNode As System.Xml.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:
                description = xmlNode.InnerText
                'And we don't want newlines that will cause havoc
                'with the built in Xml documentation:
                description = _
                  description.Replace(System.Environment.NewLine, "")
            End If
        End If

        'Where are we?
        'We have a Category and we have a Documentation
        '(although both could be string.Empty...)

        'Let's inject it in...
        Dim indentLevel As Integer = 2

        'We want to insert attributes just above
        'the code description, but under any
        'code comments that may or may not
        'already be there...
        Dim insertPoint As EditPoint = _
          codeProperty.StartPoint.CreateEditPoint()

        'insertPoint.LineUp(1)
        insertPoint.Insert("[")
        insertPoint.Insert(System.Environment.NewLine)
        insertPoint.Indent(Nothing, indentLevel)
        insertPoint.Insert("#if (!CE) && (!PocketPC) && (!pocketPC) && (!WindowsCE)")
        insertPoint.Insert(System.Environment.NewLine)
        insertPoint.Indent(Nothing, indentLevel)
        insertPoint.Insert("// Attributes not available in Compact NET")
        insertPoint.Insert(" (cf: DesignTimeAttributes.xmta)")
        insertPoint.Insert(System.Environment.NewLine)
        insertPoint.Indent(Nothing, indentLevel)
        insertPoint.Insert("System.ComponentModel.Browsable(true),")
        insertPoint.Insert(System.Environment.NewLine)
        insertPoint.Indent(Nothing, indentLevel)
        insertPoint.Insert(String.Format("System.ComponentModel.Category(""{0}""),", category))
        insertPoint.Insert(System.Environment.NewLine)
        insertPoint.Indent(Nothing, indentLevel)
        insertPoint.Insert(String.Format("System.ComponentModel.Description(""{0}""),", description))
        insertPoint.Insert(System.Environment.NewLine)
        insertPoint.Indent(Nothing, indentLevel)
        insertPoint.Insert("#endif")
        insertPoint.Insert(System.Environment.NewLine)
        insertPoint.Indent(Nothing, indentLevel)
        insertPoint.Insert("System.ComponentModel.DefaultValue(null), //TODO:Set this.")
        insertPoint.Insert(System.Environment.NewLine)
        insertPoint.Indent(Nothing, indentLevel)
        insertPoint.Insert("]")
        insertPoint.Insert(System.Environment.NewLine)
        insertPoint.Indent(Nothing, indentLevel)
    End Sub

End Module

You’ll notice that the code references the System.Xml namespace to extract the property’s description – but the System.XML.dll assembly is not automatically referenced by the Macro environment, so you first will have to add the reference to your project before it will be useable:
 

 
Taking your Macro out for a first spin
Once that has been accomplished, your module should compile, and you’re ready to take it for a test run.  Return to your User Control’s definition in Visual Studio proper, click anywhere within the Property we intend to decorate, and invoke the Macro by clicking the macro’s name in the Macro Explorer:
 


If all works out as intended, it analyses the code under the cursor, and fills in the missing attributes.

#region Properties
///
/// Gets or sets the image path.
///
/// The image path.
[
#if (!CE) && (!PocketPC) && (!pocketPC) && (!WindowsCE)
// Attributes not available in Compact NET (cf: DesignTimeAttributes.xmta)
System.ComponentModel.Browsable(true),
System.ComponentModel.Category("Appearance"),
System.ComponentModel.Description("Gets or sets the image path."),
#endif
System.ComponentModel.DefaultValue(null), //TODO:Set this.
]
public Uri ImagePath { get; set; }
#endregion
 
 
Making it easier to invoke the Macro: creating a custom Toolbar Button
When you have debugged and tweaked the macro to your satisfaction, it’s probable you will wish to have it available for use in the future.

Although the Macro Explorer is perfectly suitable, you may prefer a faster way to invoke the macro, such as creating a button for it.

     o    select from the menu Tools, then Customize.
     o    Pick the Command Tab from the Customize dialog that comes up.
     o    Select Macros from the Categories list, and the macro you just created from the Commands list:
 


     o    Drag and drop the macro on to any menu bar:



     o    Right-click the new button you’ve just created, and use the context menu to change the button’s name and icon:
 


     o    Close the Customize dialog, to see the results:
 


Click your new button while the cursor is within a Property in the Code Editor…presto!

Better Yet – Hook it up as a Keyboard Shortcut
Buttons are fun…but not as practical as keyboard shortcuts.

To hook your Macro up to a keyboard shortcut, do the following:
     o    Select Tools, Options
     o    In the Options dialog that pops up, expand the Environment, and select Keyboard
     o    In the text box labelled Show commands containing, start typing the word ‘macro’, then the name of your macro, the macro becomes highlighted in the list below. Select it.
     o    Update the value within the Use new shortcut in  dropdown to state Text Editor. 
     o    Within the Press shortcut keys press a key combination. I chose Ctrl-Shift-Alt-D, as it was very similar to GhostDoc’s Ctrl-Shift-D:



     o    Hit Ok, and give it a spin.

Where to go from here
The above Sub macro was intentionally kept simple in order to introduce the concept of macro development.

One can add more complex logic to add the other attributes mentioned, namely System.ComponentModel.EditorAttribute, System.ComponentModel.DesignerSerializationVisibilityAttribute and System.Web.UI.PersistenceModeAttribute.

There are many macro samples online to help you get where ever you wish to go, including some examples at my website you may find useful getting started. 
 
Links:
     Visual Studio 2008
    
     Visual Studio Features:
     o    http://download.microsoft.com/download/5/f/e/5feb6914-bcdf-432f-81c7-e386812b086a/VisualStudio2008-ProductComparison-v1.08.pdf 
     o    Refactoring C# Code Using Visual Studio 2005

     Visual Studio 3rd Party Language Packages:
     o    PHP for Visual Studio:
           http://www.jcxsoftware.com/
           http://www.codeplex.com/php4vs
           Phalanger, PHP for .NET: Introduction for .NET developers

     Visual Studio Extensions:
     o    Visual Studio Gallery of Extensions for Visual Studio
     o    Visual Studio Add-Ins Every Developer Should Download Now
    
     Other Editors:
     o    Emonics
     o    http://www.ibm.com/developerworks/library/os-eclipse-migratenetvs/index.html

     Development Resources:
     o    My blog’s Visual Studio Macro posts
     o    Automation and Extensibility Reference
     o    EnvDTE Namespace
    
     Attributes for properties in Control development:
     o    System.ComponentModel.CategoryAttribute
     o    System.ComponentModel.DescriptionAttribute
     o    System.ComponentModel.BrowsableAttribute
     o    System.ComponentModel.EditorAttribute
     o    System.ComponentModel.DesignerSerializationVisibilityAttribute
     o    System.Web.UI.PersistenceModeAttribute
    
Download Visual Studio 2008 90 day trial
For detailed information and to request a free 90-day trial DVD of Visual Studio 2008 Team Suite to be sent out to you, go to the Microsoft Visual Studio webpage.

About the Author
Sky Sigal has been programming on and off for 25 years, over 10 of those professionally, creating enterprise solutions in various languages, on different platforms. You can read about what he's currently taking apart and putting back together at http://skysigal.xact-solutions.com.
 

Other related posts:
The New Zealand ALM Conference 2011 (Application Life Cycle Management)
Writing your own Html Helpers for the ASP.NET MVC Framework
Windows Azure Table Storage - Getting Started - Part 2




Permalink to Automating Visual Studio 2008 (2 comments) | Main Index




Comment by hockmanli, on 2-Apr-2009 20:30

Good posting! more professional web templates at itemplatez.com... its a easy download.


Comment by kjward, on 29-Sep-2009 09:47

thanks lots for all the great info. i'm sure the answer to my question is in there somewhere, but please bear with me while i expose my general ignorance... we would like to create an app with a list of general classes (data access, etc.) that have been stored in our source control library that a developer can then choose to include in a new project baseline. once their selection is complete, the app then generates the visual studio project code and opens visual studio with the baseline project ready to edit. how to do this? thanks in advance


About the Visual Studio 2008 Blog

In the years since the hugely successful release of Visual Studio 2005, Microsoft has used developer feedback from all over the world to introduce over 250 new features in the 2008 release.

This sponsored blog will bring Visual Studio 2008 tips and tricks from well known developers in the Australian and New Zealand tech community directly to you.

Make sure you bookmark this blog or subscribe to our RSS feed.



Other blog posts

The New Zealand ALM Conference...
Writing your own Html Helpers ...
Windows Azure Table Storage - ...
Windows Azure Table Storage - ...
XML and XSLT in Visual Studio ...
Introduction to New SQL Server...
Using the Visual Studio 2008 P...
Getting started with Windows A...
Why consider sitting for an MC...
Unit testing .Net Compact Fram...


Some recent comments

Praveen M on Visual Studio 2008 support for building SilverLigh: I installed SILVERLIGHT TOOLS for VS2008 at my work. I am able to do silverlight...

zefo on Unit testing with Visual Studio 2008: Just minor recommendation: better use *.png for the screenshots. Jpeg is more su...

gil ziny on Unit testing with Visual Studio 2008: When I associate the unit test with TFS testing policy, if fails during check-in...

lynette on Visual Studio 2008 JavaScript Debugging: i have enable in IE the ability to debug scripts. if i go to my section in my c...

Junkie on Why consider sitting for an MCTS or MCP Certificat: The way you presented the case for certification has been completely different f...

Brian Link on Unit testing with Visual Studio 2008: You seem to be incorrect in one of your opening remarks - it seems that web.conf...

Dipanwaya on Unit testing .Net Compact Framework applications w: Steps Behind the Test in the VS 2008? How to test a module in the VS 2008 Envir...

Ricardo on Visual Studio 2008 support for building SilverLigh: Yes! Found the solution myself :) Reset the environment of Visual Web Developer...

Ricardo on Visual Studio 2008 support for building SilverLigh: Have the same problem after installing Visual Web Developer 2008 SP1 and Silverl...

kjward on Automating Visual Studio 2008: thanks lots for all the great info. i'm sure the answer to my question is in th...



Disclaimer

The Visual Studio 2008 blog is sponsored by Microsoft NZ. The blog posts are the authors' genuine accounts of their experiences with Visual Studio 2008 and are not influenced or filtered by Microsoft NZ in any way.