productivity, visual studio, macros, layout comments edit

There have been many people who have asked for a way to save window positions within Visual Studio. For example they layout all their tool windows in a particular order and wish to change it based on the task at hand, but want to get back to the original setup.

Previously when asked, I recommended an old add-in and always suggested they update the code for 2008. I got tired of that recommendation, I needed the ability, and I needed it in 2010.So I wrote the following macro to allow a developer to Save, Switch, and Delete window/tool positions within Visual Studio.

The Macros names are pretty self-describing but here is a screen shot of it in action: image

As always if you want to bind it to a keyboard shortcut go to Tools-->Options-->Environment—>Keyboard. Then in the 'show commands containing' box type one of the following: SwitchCurrentView, SaveCurrentView, DeleteSavedView and bind to a shortcut key. (Try - CTRL+ALT+0)

Tested on VS2008 and VS2010 should work in VS2005

Create a new macro module called 'View' and paste in the following: (You may need to add a reference to System.Drawing)

Imports System
Imports EnvDTE
Imports System.Collections.Generic
Imports System.Windows.FormsPublic
Module View
    Sub SaveCurrentView()
        Dim viewName As String
        viewName = InputBox("Name your view:", "Save view layout")
        If Not String.IsNullOrEmpty(viewName) Then
            DTE.WindowConfigurations.Add(viewName)
        End If
    End Sub

    Sub SwitchCurrentView()
        Using frm As New frmViewSwitcher
            Dim winptr As WinWrapper = New WinWrapper
            Dim ret As DialogResult
            Try
                ret = frm.ShowDialog(winptr)
                If ret = DialogResult.Cancel Then
                    Exit Sub
                End If
                DTE.WindowConfigurations.Item(frm.SelectedView).Apply()
            Catch ex As Exception
                MsgBox(ex.Message)
            Finally
                ret = Nothing
                winptr = Nothing
            End Try
        End Using
    End Sub

    Sub DeleteSavedView()
        Using frm As New frmViewSwitcher
            frm.Name = "Delete View"
            Dim winptr As WinWrapper = New WinWrapper
            Dim ret As DialogResult
            Try
                ret = frm.ShowDialog(winptr)
                If ret = DialogResult.Cancel Then
                    Exit Sub
                End If
                Dim currentView As String = DTE.WindowConfigurations.ActiveConfigurationName
                Dim selectedView As String = frm.SelectedView
                If Not String.Compare(currentView, selectedView, True) = 0 Then
                    Dim conf As WindowConfiguration = DTE.WindowConfigurations.Item(frm.SelectedView)
                    conf.Delete()
                Else
                    MsgBox("Cannot delete current view!", MsgBoxStyle.Critical + MsgBoxStyle.OkOnly, "Current View")
                End If
            Catch ex As Exception
                MsgBox(ex.Message)
            Finally
                ret = Nothing
                winptr = Nothing
            End Try
        End Using
    End Sub
End Module

Partial Class frmViewSwitcher
    Inherits System.Windows.Forms.Form
    Implements IDisposable
    Private _viewList As List(Of String)
    Public Sub New()
        InitializeComponent()
        _viewList = GetViews()
        _viewList.Sort()
        For Each v As String In _viewList
            lstResults.Items.Add(v)
        Next
        lstResults.Items(0).Selected = True
        Me.Focus()
    End Sub
    WriteOnly Property Name() As String
        Set(ByVal value As String)
            Me.Text = value
        End Set
    End Property
    Private Function GetViews() As List(Of String)
        Dim rtnList As New List(Of String)
        For Each wc As WindowConfiguration In DTE.WindowConfigurations
            Dim viewName As String = wc.Name
            rtnList.Add(viewName)
        Next
        Return rtnList
    End Function
    Public ReadOnly Property SelectedView() As String
        Get
            Return lstResults.SelectedItems(0).Text
        End Get
    End Property
    Protected Overrides Sub Dispose(ByVal disposing As Boolean)
        Try
            If disposing AndAlso components IsNot Nothing Then
                components.Dispose()
            End If
        Finally
            MyBase.Dispose(disposing)
        End Try
    End Sub
    Private Const ControlWidth As Int16 = 400
    Private Const ListHeight As Int16 = 200
    Private Const Padding As Int16 = 5
    Private Const WindowHeight As Int16 = Padding + ListHeight + Padding
    Private Const WindowWidth As Int16 = Padding + ControlWidth + Padding
    Private components As System.ComponentModel.IContainer
    Private Sub InitializeComponent()
        Me.lstResults = New System.Windows.Forms.ListView
        Me.colViewName = New System.Windows.Forms.ColumnHeader
        Me.colViewName.Text = "View Name"
        Me.button = New System.Windows.Forms.Button
        Me.SuspendLayout()
        Me.lstResults.Anchor = CType((((System.Windows.Forms.AnchorStyles.Top Or System.Windows.Forms.AnchorStyles.Bottom) _
                    Or System.Windows.Forms.AnchorStyles.Left) _
                    Or System.Windows.Forms.AnchorStyles.Right), System.Windows.Forms.AnchorStyles)
        Me.lstResults.Columns.AddRange(New System.Windows.Forms.ColumnHeader() {Me.colViewName})
        Me.lstResults.FullRowSelect = True
        Me.lstResults.HideSelection = False
        Me.lstResults.Location = New System.Drawing.Point(Padding, Padding)
        Me.lstResults.MultiSelect = False
        Me.lstResults.Name = "lstResults"
        Me.lstResults.Size = New System.Drawing.Size(ControlWidth, ListHeight)
        Me.lstResults.TabIndex = 1
        Me.lstResults.UseCompatibleStateImageBehavior = False
        Me.lstResults.View = System.Windows.Forms.View.Details
        Me.colViewName.Width = CInt(lstResults.Width - 10)
        Me.button.DialogResult = System.Windows.Forms.DialogResult.Cancel
        Me.button.Size = New System.Drawing.Size(1, 1)
        Me.AllowDrop = False
        Me.AutoScaleDimensions = New System.Drawing.SizeF(6.0!, 13.0!)
        Me.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font
        Me.ClientSize = New System.Drawing.Size(WindowWidth, WindowHeight)
        Me.Controls.Add(Me.lstResults)
        Me.Controls.Add(Me.button)
        Me.CancelButton = Me.button
        Me.MaximizeBox = False
        Me.MinimizeBox = False
        Me.Name = "View Switcher"
        Me.ShowIcon = False
        Me.ShowInTaskbar = False
        Me.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen
        Me.Text = "View Switcher"
        Me.ResumeLayout(False)
        Me.PerformLayout()
    End Sub

    Friend WithEvents lstResults As System.Windows.Forms.ListView
    Friend WithEvents colViewName As System.Windows.Forms.ColumnHeader
    Friend WithEvents button As System.Windows.Forms.Button
    Private Sub lstResults_DoubleClick(ByVal sender As Object, ByVal e As System.EventArgs) Handles lstResults.DoubleClick
        Me.DialogResult = System.Windows.Forms.DialogResult.OK
    End Sub
    Private Sub lstResults_KeyUp(ByVal sender As Object, ByVal e As System.Windows.Forms.KeyEventArgs) Handles lstResults.KeyUp
        If e.KeyCode = Keys.Enter Then Me.DialogResult = System.Windows.Forms.DialogResult.OK
    End Sub
End Class

Public Class WinWrapper
    Implements System.Windows.Forms.IWin32Window
    Overridable ReadOnly Property Handle() As System.IntPtr Implements System.Windows.Forms.IWin32Window.Handle
        Get
            Dim iptr As New System.IntPtr(DTE.MainWindow.HWnd)
            Return iptr
        End Get
    End Property
End Class

visual studio, macros comments edit

So tonight I was browsing unanswered questions on StackOverflow.com and I came across a question for which I had written a solution long ago and thought I would turn it into a blog-post instead.

The macro's purpose is simple - turn a string that you are concatenating into a String.Format statement. This generally produces much more readable code and becomes more maintainable long term. It even attempts to recognize when you are already using a formatted ToString.

I have tested with both C# and VB code, Visual Studio 2010 and 2008. To use Highlight the text you want to convert and invoke the macro, I would recommend that you bind it to a keystroke for quick use later on.

For example this:

var name = "Brian Schmitt";
Console.WriteLine("Hello " + name);

var money = 1234567.89;
Console.WriteLine("You have " + money.ToString("c") + " dollars");

var action = "Pay";
var util = "Electric";
Console.WriteLine("Would you like to " + action + " your " + util + " Bill");

Console.ReadLine();

Becomes this:

var name = "Brian Schmitt";
Console.WriteLine(string.Format("Hello {0}", name));

var money = 1234567.89;
Console.WriteLine(string.Format("You have {0:c} dollars", money));

var action = "Pay";
var util = "Electric";
Console.WriteLine(string.Format("Would you like to {0} your {1} Bill", action, util));
Console.ReadLine();

Finally the Macro:

Public Sub ConvertToStringFormat()
    DTE.UndoContext.Open("ConvertToStringFormat")
    Dim textSelection As TextSelection = DTE.ActiveDocument.Selection
    Dim output As String = "string.Format(""{0}"", {1})"
    Dim delimt As String = ", "
    Dim fmtdTostring As String = ".tostring("""
    Dim txtSelection As String() = System.Text.RegularExpressions.Regex.Split(textSelection.Text.Trim, "\+\s_[+\n\r\t]|&\s_[+\n\r\t]|\+|&")
    Dim hardStrings As String = String.Empty
    Dim valueStrings As String = String.Empty
    Dim counter As Int16 = 0
    For Each str As String In txtSelection
        Dim tmpString As String = str.Trim
        If tmpString.StartsWith("""") Then
            hardStrings &= tmpString.Substring(1, tmpString.Length - 2)
        Else
            Dim fmt As String = String.Empty
            Dim indxToString As Int32 = 0
            If tmpString.ToLower.Contains(fmtdTostring) Then
                indxToString = tmpString.ToLower.IndexOf(fmtdTostring)
                fmt = tmpString.Substring(indxToString + 11, tmpString.Length - tmpString.ToLower.IndexOf(""")", indxToString) - 1)
            End If
            If fmt <> String.Empty Then
                hardStrings &amp;= "{" &amp; counter.ToString &amp; ":" &amp; fmt &amp; "}"
                valueStrings &amp;= tmpString.Substring(0, indxToString) &amp; delimt
            Else
                hardStrings &amp;= "{" &amp; counter.ToString &amp; "}"
                valueStrings &amp;= tmpString &amp; delimt
            End If
            counter += 1
        End If
    Next
    If valueStrings <> String.Empty Then valueStrings = valueStrings.Substring(0, valueStrings.Length - delimt.Length)
    textSelection.Text = String.Format(output, hardStrings, valueStrings)
    DTE.UndoContext.Close()
End Sub

visual studio, macros comments edit

Visual Studio 2010 is now out and I thought I would share a quick tip that I find useful. This shortcut can be found under Edit—> Outlining –> Hide Selection, and is bound by default to the hot key of CTRL+M, CTRL+H.

The usefulness of this feature shines when working on large blocks of code, you can highlight any portion of code, apply the Hide Selection option, and the code will 'Fold' out of sight - allowing you to focus just on the portion you are interested in. This information is stored in your .suo file, so other members of the team are not affected and will be persisted to future sessions. I have found this to work in all types of source files: .aspx, .cs, .vb, .css, and many others.

This relatively little known feature was in previous versions of Visual Studio, but became broken after 2005 SP1. Jay Flowers even wrote a CodeRush plugin to accomplish the same thing.

If you are still on 2005/2008 and would like to have this feature now I provide to you the following macro:

Sub FoldCode()
    Dim selection As TextSelection = DTE.ActiveDocument.Selection
    selection.OutlineSection()
End Sub

visual studio, regex, macros comments edit

Recently Uncle Bob was talking on twitter about regions. He says "Purge All Regions" - Well today's snippet is going to do just that.

The inspiration for this originated with Kyle Bailey's post from a few years ago. I had taken it, modified it, and today I share it with you; it can be used as a macro as Kyle created it, or you can use it right in Visual Studio's Find and Replace.

Every developer knows how to use Find and Find/Replace, however I have only found a few that know that you can use regular expressions. The regular expressions that Visual Studio supports in the Find Dialog is a slimed down version and is specific to Visual Studio.

image

The following will work in both C# and VB, in the Find Options simply select “Use Regular Expressions” and replace with nothing. Change the Look in option to specify the scope of your search and you can make the change solution wide or just your current file.

^.*\#(end)*(:Wh)*region.*\n

Simple, quick and effective!

visual studio, macros comments edit

Today's Macro is very basic, but I use it almost daily.

I use a customized IDE and one of the performance tweaks I have performed is to Turn Off the Track Active Items (Tools --> Options --> Projects and Solutions).

This feature, when enabled, syncs the selected item in your solution explorer to the file being viewed.

There are times that I have traced down into a file and then needed to locate it in the Solution Explorer; This Macro will assist you finding the current file while leaving the feature turned off.

(Other add-ins like ReSharper offer a similar feature "Locate in Solution Explorer")

About the Macro: The First Command toggles on the feature, the second toggles it back off - this allows Visual Studio to find the item in the solution explorer.

Then the third line simply causes the solution explorer to be displayed, this works if the solution explorer window is hidden or closed.

Bind it to a shortcut key and you are all set - mine is bound to (ALT+L, ALT+L)

Public Sub LocateFileInSolutionExplorer()
     DTE.ExecuteCommand(&quot;View.TrackActivityinSolutionExplorer&quot;)
     DTE.ExecuteCommand(&quot;View.TrackActivityinSolutionExplorer&quot;)
     DTE.ExecuteCommand(&quot;View.SolutionExplorer&quot;)
End Sub