Wednesday, September 8, 2010

Save and Change Tool Layout in Visual Studio

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 screenshot 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.Forms

Public 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

Monday, August 9, 2010

Converting a concatenated String into String.Format

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 &= "{" & counter.ToString & ":" & fmt & "}"
valueStrings &= tmpString.Substring(0, indxToString) & delimt
Else
hardStrings &= "{" & counter.ToString & "}"
valueStrings &= tmpString & 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

Sunday, April 18, 2010

Hide Selection in 2010, 2008 and 2005

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

Wednesday, January 13, 2010

Remove Visual Studio Regions with Find and Replace

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!

Wednesday, January 6, 2010

Locate File in Solution Explorer – Visual Studio Macro

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("View.TrackActivityinSolutionExplorer")
DTE.ExecuteCommand("View.TrackActivityinSolutionExplorer")
DTE.ExecuteCommand("View.SolutionExplorer")
End Sub

Tuesday, October 20, 2009

Fixing the Visual Studio Add Reference Dialog – Quickly add a Project Reference

Today's macro addresses one of the most common complaints about Visual Studio - The "Add Reference" dialog - I will help you speed it up when you only need to add a project reference.

I know the issue is fixed in the latest beta of 2010 but I provide this here for those who are still using 2008 or lower.

I know we have all been there at one time or another – you add a new class library and want to add a reference in your UI to the project – but we dread the dialog box to add that reference - Will today be a lucky day and it only take 30 seconds to load? With this macro you can quickly add a reference to another project in the same solution. It offers a simple dialog box for you to choose one or more projects. Hope you find it just as useful as I do. Kevin Dente had a great suggestion that the add reference dialog should support filtering/searching – This is possible and I’ll see what I can do about it.

image

Here is the code: (Scroll down for instructions on how you can add it to your context menu)

Imports System
Imports EnvDTE
Imports EnvDTE80
Imports EnvDTE90
Imports System.Diagnostics
Imports System.Windows.Forms
Imports System.Collections

Public Module References
Public Sub AddProjectReference()
Dim frm As New SelectProjectForm
Dim winptr As WinWrapper = New WinWrapper
Try
Dim ret As DialogResult = frm.ShowDialog(winptr)
If ret = DialogResult.Cancel Then
Return
End If

Dim actvProjs As Array = DTE.ActiveSolutionProjects()
Dim sngProj As Project = CType(actvProjs.GetValue(0), EnvDTE.Project)
Dim vsProj As VSLangProj.VSProject = DirectCast(sngProj.Object, VSLangProj.VSProject)

For Each proj As Project In frm.SelectedProjects
Try
vsProj.References.AddProject(proj)
Catch ex As Exception
MsgBox(ex.Message, MsgBoxStyle.Critical And MsgBoxStyle.OkOnly, "Error - " & proj.Name)
End Try
Next
Finally
frm = Nothing
winptr = Nothing
End Try
End Sub

Private Class SelectProjectForm
Inherits System.Windows.Forms.Form

Public Sub New()
InitializeComponent()

lstProjects.DisplayMember = "Name"
lstProjects.DataSource = GetAllProjects()
lstProjects.Select()
End Sub

Public ReadOnly Property SelectedProjects() As Generic.List(Of Project)
Get
Dim lst As New Generic.List(Of Project)
For Each itm As Object In lstProjects.SelectedItems
lst.Add(CType(itm, Project))
Next
Return lst
End Get
End Property

Private Function GetAllProjects() As Generic.List(Of Project)
Dim lst As New Generic.List(Of Project)
For Each proj As Project In DTE.Solution.Projects
If proj.Kind = Constants.vsProjectKindSolutionItems Then
lst.AddRange(GetSubProjects(proj.ProjectItems))
Else
lst.Add(proj)
End If
Next
Return lst
End Function

Private Function GetSubProjects(ByVal pis As ProjectItems) As Generic.List(Of Project)
Dim lst As New Generic.List(Of Project)
For Each pi As ProjectItem In pis
If pi.Kind = Constants.vsProjectItemKindSolutionItems Then
lst.Add(pi.SubProject)
ElseIf pi.Kind = Constants.vsProjectKindSolutionItems Then
lst.AddRange(GetSubProjects(pi.ProjectItems))
End If
Next
Return lst
End Function

Private Sub InitializeComponent()
Me.btnOk = New System.Windows.Forms.Button
Me.btnCancel = New System.Windows.Forms.Button
Me.lstProjects = New System.Windows.Forms.ListBox
Me.SuspendLayout()

Me.btnOk.Anchor = CType((System.Windows.Forms.AnchorStyles.Bottom Or System.Windows.Forms.AnchorStyles.Right), System.Windows.Forms.AnchorStyles)
Me.btnOk.Location = New System.Drawing.Point(197, 230)
Me.btnOk.Name = "btnOk"
Me.btnOk.Size = New System.Drawing.Size(75, 23)
Me.btnOk.TabIndex = 1
Me.btnOk.Text = "Ok"
Me.btnOk.UseVisualStyleBackColor = True

Me.btnCancel.Anchor = CType((System.Windows.Forms.AnchorStyles.Bottom Or System.Windows.Forms.AnchorStyles.Right), System.Windows.Forms.AnchorStyles)
Me.btnCancel.DialogResult = System.Windows.Forms.DialogResult.Cancel
Me.btnCancel.Location = New System.Drawing.Point(116, 231)
Me.btnCancel.Name = "btnCancel"
Me.btnCancel.Size = New System.Drawing.Size(75, 23)
Me.btnCancel.TabIndex = 2
Me.btnCancel.Text = "Cancel"
Me.btnCancel.UseVisualStyleBackColor = True

Me.lstProjects.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.lstProjects.FormattingEnabled = True
Me.lstProjects.Location = New System.Drawing.Point(13, 13)
Me.lstProjects.Name = "lstProjects"
Me.lstProjects.SelectionMode = System.Windows.Forms.SelectionMode.MultiExtended
Me.lstProjects.Size = New System.Drawing.Size(259, 212)
Me.lstProjects.TabIndex = 3

Me.AcceptButton = Me.btnOk
Me.AutoScaleDimensions = New System.Drawing.SizeF(6.0!, 13.0!)
Me.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font
Me.CancelButton = Me.btnCancel
Me.ClientSize = New System.Drawing.Size(284, 262)
Me.Controls.Add(Me.lstProjects)
Me.Controls.Add(Me.btnCancel)
Me.Controls.Add(Me.btnOk)
Me.FormBorderStyle = System.Windows.Forms.FormBorderStyle.SizableToolWindow
Me.Name = "Form1"
Me.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent
Me.Text = "Select Project(s)"
Me.ResumeLayout(False)
End Sub
Friend WithEvents lstProjects As System.Windows.Forms.ListBox
Friend WithEvents btnOk As System.Windows.Forms.Button
Friend WithEvents btnCancel As System.Windows.Forms.Button

Private Sub lstProjects_DoubleClick(ByVal sender As Object, ByVal e As System.EventArgs) Handles lstProjects.DoubleClick
Me.DialogResult = System.Windows.Forms.DialogResult.OK
End Sub

Private Sub lstProjects_KeyUp(ByVal sender As Object, ByVal e As System.Windows.Forms.KeyEventArgs) Handles lstProjects.KeyUp
If e.KeyCode = Keys.Enter Then Me.DialogResult = System.Windows.Forms.DialogResult.OK
End Sub

Private Sub btnOk_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles btnOk.Click
Me.DialogResult = System.Windows.Forms.DialogResult.OK
End Sub
End Class

End Module


For those who would like to add the macro (or any macro) to your right click context menu follow these steps:



  1. Right Click on Menu Bar

  2. Choose Customize at bottom

  3. In the list choose "Context Menus" - Choose the Commands tab image

  4. In the categories choose Macros and then locate your new macro image

  5. Drag it up to the context menu bar that appeared when you selected it above,

    hover over Project and Solution Context Menus, Project and then Drop it under Add Reference. image

  6. Give it a friendly name. This will make it available when you right click on a project file in solution explorer  image

Friday, September 11, 2009

How to Comment-Uncomment Code Selection in Style Sheets - (or How to fix Visual Studio's broken implementation)

If you commonly comment out portions of code, then you are used to highlighting a portion of text and using the shortcut of CTRL+K, CTRL+C (or the toolbar) to comment out the selected text.

While this will work for most source files, it does not currently work inside Visual Studio for style sheets (.css) files. (See screenshot and note the status bar message)

Comment Selection not currently available

Why Microsoft has left out this basic functionality is beyond me; In this post, I would like to demonstrate how you can write your own macro to fix it.

First we will record a basic macro - later we will modify it to suit our needs.

record temporary macro

Start out by opening a style sheet and placing your cursor at the relevant piece of code, next start recording by selecting Record TemporaryMacro (Tools-->Macros-->Record... or CTRL+Shift+R). Using the keyboard highlight some style information and Ctrl+X to cut your selection to the clipboard. Next add the beginning of our comment /* and paste your code back in. Lastly add the comment close */ and stop recording your macro.

temporarymacro

Browse your Macro Explorer and you will notice a "RecordingModule" and if expanded a "Temporary Macro". Right click on the macro and choose edit causing your Macro Editor to open. If you completed the steps outlined above you should have ended up with something roughly similar to this:

Sub TemporaryMacro()
DTE.ActiveDocument.Selection.LineUp(True, 5)
DTE.ActiveDocument.Selection.StartOfLine(vsStartOfLineOptions.vsStartOfLineOptionsFirstText, True)
DTE.ActiveDocument.Selection.Cut()
DTE.ActiveDocument.Selection.Text = "/*"
DTE.ActiveDocument.Selection.Paste()
DTE.ActiveDocument.Selection.Text = "*/"
End Sub

You can see that it recorded the exact steps I took: I highlighted five lines, cut the text, typed, pasted and finally typed some more. If you go back to your text editor and undo your changes and run the macro you should end up with the same ending result.

commented style - css

As you can see, for repeatable actions, Macros are great - however at this point you might be thinking - What if I want to comment out 3 lines (or 10 or more)?. Now that we have our starting point, we can modify our temporary macro to better accommodate our concerns and match the common VS implementation.

Let's start modifying by simply grabbing the currently selected text, as you can see VS stores this in DTE.ActiveDocument.Selection and then we will append /* and */ to the beginning and end.

So we end up with something like this:

Sub TemporaryMacro()
Dim txtSel As TextSelection
txtSel = DTE.ActiveDocument.Selection
txtSel.Text = "/*" + txtSel.Text + "*/"
End Sub

That's a fairly good substitute for our previous recorded macro; We can highlight lines with our mouse or keyboard and execute the macro, and it will comment out a variable number of lines. It works, but could be better.

With the above variation, if you highlight multiple lines and then 'undo' the changes you will notice that it will undo it line by line, a minor annoyance, but luckily the macro system has something just for this scenario. It is called the UndoContext and is fairly easy to use:

Sub TemporaryMacro()
Try
DTE.UndoContext.Open("Comment CSS")
Dim txtSel As TextSelection
txtSel = DTE.ActiveDocument.Selection
txtSel.Text = "/*" + txtSel.Text + "*/"
Finally
DTE.UndoContext.Close()
End Try
End Sub

You should wrap it in a Try/Finally - This is important, but I won't go into 'why' here. Now highlight multiple lines run the macro and then undo it, your changes are preformed as a single transaction and rolled back as one too. It is a nice little enhancement that will give your final macro some polish.

One final enhancement to our macro, In VS you can Comment and also Uncomment a section of code - that would be a nice feature too. However, instead of writing it as a separate macro, let's detect if the code is currently commented, and if so, uncomment it. In the end we should be able to bind a single shortcut for both comment and uncomment.

Here is our final code:

Sub CommentCSS()
'Detect if in a CSS file
If Not DTE.ActiveDocument.Name.EndsWith("css") Then Return
Try
DTE.UndoContext.Open("Comment CSS")

Dim txtSel As TextSelection = DTE.ActiveDocument.Selection
Dim currText As String = txtSel.Text

If currText.Length > 0 Then
Dim newTxt As String
If currText.Trim.StartsWith("/*") AndAlso currText.Trim.EndsWith("*/") Then
newTxt = currText.Replace("/*", "").Replace("*/", "")
Else
newTxt = "/*" + currText + "*/"
End If

txtSel.Delete() 'This is to help keep formatting correct when multiline
txtSel.Insert(newTxt, vsInsertFlags.vsInsertFlagsInsertAtEnd)
End If
Finally
DTE.UndoContext.Close()
End Try
End Sub

Now to bind our Macro to a keyboard chord: Go to Tools-->Options-->Environment-->Keyboard - In the show commands type CommentCSS, in the dropdown "Use new shortcut in" change the selection to CSS Source Editor, finally in the "Press Shortcut Keys" type CTRL+K, CTRL+C and assign it.

key binding

Tuesday, September 8, 2009

Quickly Reformat your Project Files

Recently on twitter Phil Haacked asked about reformatting the files in a project where it didn't match his preferences. We have all been there, looking at a file that we downloaded or was given, and would find it easier to follow if we could reformat it.

There is an often overlooked feature of Visual Studio that will reformat our code for us. I frequently use it on HTML documents to quickly restructure code in View Source.

This functionality can be found under Edit-->Advanced-->Format Document (or CTRL+K, CTRL+D) as well as Format Selection – which only applies to the currently highlighted text.

image

One thing to mention is that you should define how you want your code formatted by going to Tools-->Options-->Text Editor and then select your language of choice. (Some languages offer a greater degree of customization)

Now that you know how to reformat a document and how you can define its result, we will write a macro that will achieve what Phil was looking for by - loop through each source file - open, format, save, and then close it:

    Sub FormatAll()
For Each proj As Project In DTE.Solution.Projects
FormatFileRecur(proj.ProjectItems())
Next
End Sub


Sub FormatFileRecur(ByVal projectItems As EnvDTE.ProjectItems)
For Each pi As EnvDTE.ProjectItem In projectItems
If pi.Collection Is projectItems Then
Dim pi2 As EnvDTE.ProjectItems = pi.ProjectItems
Try
If Not pi.IsOpen Then pi.Open(Constants.vsViewKindCode)
pi.Document.Activate()
DTE.ExecuteCommand("Edit.FormatDocument")
If Not pi.Document.Saved Then pi.Document.Save()
pi.Document.Close()
Catch ex As Exception
'Ignore this error - some project items cannot be opened.
End Try
If pi2 IsNot Nothing Then
FormatFileRecur(pi2)
End If
End If
Next
End Sub

Thursday, August 27, 2009

Intro to Visual Studio Macros and The Most Important Macro for Presenters

In my last post I told you how to use a macro to replace the F1 'Help' functionality to perform a search on the internet.

In this post, I hope to explain some of the basics of Macros, the single most important macro for presenters/speakers, and how you can bind a macro to a keyboard shortcut.

Basics of Macros

Visual Studio offers a very rich extensibility model and you can extend and bend it in many ways.

One way to harness the power of Visual Studio is through macros. A macro has access to some of the core functionality within Visual Studio. An advantage of VS Macros is that you do not have to install or compile them. Sharing is very simple as its just plaintext; You simply need the relative snippet of code and paste it in.

I feel that one of the hardest things about Macros is discovering they are available and beginning to use them.

So, lets get started with running a Macro, in Visual Studio hit Alt+F8, this will open your Macro Explorer. It is very much like a Solution Explorer for your Macros. It is here that you can select and run your macros. We will be taking a look at the samples already included with your install of Visual Studio.


Single Most Important Macro for Presenters

Expand the Samples and then expand Accessibility, you should see several macros there, and for this exercise we are interested in DecreaseTextEditorFontSize and IncreaseTextEditorFontSize.

Macro Explorer

Double Click to run one of them, if you have a file open in your editor you should notice the size of the font has now changed. Now Double click and run the opposite macro, and it should switch back to the size you had.

If you are a presenter you can and should definitely know about these two macros! They will allow you to quickly change your environment suitable for an audience.

If you would like to see the code at accomplished this feat, right click on the macro and choose edit, this will open up the Macro Editor, it's a stripped down version of your standard Visual Studio Editor, but you should feel comfortable using it.

For those not sitting at your IDE here is the relevant code:

' Increases the font size used within the editor.
Public Sub IncreaseTextEditorFontSize()
Dim textEditorFontsAndColors As Properties
textEditorFontsAndColors = DTE.Properties("FontsAndColors", "TextEditor")
textEditorFontsAndColors.Item("FontSize").Value += fontSizeIncrement
End Sub

' Decreases the font size used within the editor.
Public Sub DecreaseTextEditorFontSize()
Dim textEditorFontsAndColors As Properties
Dim fontSize As [Property]

textEditorFontsAndColors = DTE.Properties("FontsAndColors", "TextEditor")
fontSize = textEditorFontsAndColors.Item("FontSize")

If fontSize.Value >= minimumSupportedEditorSize Then
fontSize.Value -= fontSizeIncrement
End If
End Sub

The two methods simply take the current font size and increment it by a defined amount.

See how simple, yet powerful macros can be? I know, I know - increasing a font size is not that powerful, but this sample shows that you can tap into key places and accomplish repetitive, mundane tasks. I will show more powerful samples in the future.


Key Binding

As one final exercise lets bind a macro to a keyboard shortcut.

  1. Go to Tools-->Options, Expand Environment and Select Keyboard.
  2. In the 'Show Commands containing' textbox type 'TextEditorFont'
  3. In the Press shortcut keys textbox type Ctrl+Shift+Alt+DownArrow, Select the DecreaseTextEditorFontSize Macro above and click Assign.

Keyboard Options Dialog

Repeat for Ctrl+Shift+Alt+UpArrow and IncreaseTextEditorFontSize.

Now in the future when you need to quickly change the size of your font in the text editor, you can either double click the macro or use your newly created shortcut keys

Note: if you really do present from your main development machine, I would recommend that you create a macro that can quickly change all your standard environment (fonts/colors/size/etc…) settings at one time.

Tuesday, August 25, 2009

Better Visual Studio F1

So recently there have been some great tips on turning off the F1 key in Visual Studio.

Roy Osherove and Infinities Loop

While that is useful in turning off the Visual Studio help, it may not be useful in the event that you actually NEED help.

I have found it useful to re-bind F1 to a macro that will take the currently highlighted word from your Visual Studio text editor and perform a search at the designated site.

I have provided code below to allow you to quickly search StackOverflow, Google, MSDN, and Searchdotnet.

Caveats - The below script will only work in the text editor, I can provide additional code that will also use the selected text from the output window or the html-editor. (I tried to keep it simple.)

I love macros in VS and think they are highly under used, I will be posting more soon, so subscribe and welcome to my new blog!

Take your pick of the four provided below (or bind several to the key combinations F1, Alt+F1, Ctrl+F1, etc...)

Imports EnvDTE
Imports System.Web

Public Module Search

#Region "Search Internet Sites"
Public Const GOOGLE_FORMAT As String = "www.google.com/search?q={0}"
Public Const STACKOVERFLOW_FORMAT As String = "http://www.stackoverflow.com/search?q={0}"
Public Const SEARCHDOTNET_FORMAT As String = "http://searchdotnet.com/results.aspx?cx=002213837942349435108:jki1okx03jq&q={0}&sa=Search&cof=FORID:9#1144"
Public Const MSDN_FORMAT As String = "http://social.msdn.microsoft.com/Search/en-US/?query={0}&ac=8"

Public Sub SearchStackOverflowForSelectedText()
SearchWebPage(STACKOVERFLOW_FORMAT)
End Sub
Public Sub SearchGoogleForSelectedText()
SearchWebPage(GOOGLE_FORMAT)
End Sub
Public Sub SearchSearchDotNetForSelectedText()
SearchWebPage(SEARCHDOTNET_FORMAT)
End Sub
Public Sub SearchMSDNForSelectedText()
SearchWebPage(MSDN_FORMAT)
End Sub
Private Sub SearchWebPage(ByVal SearchURLFormat As String)
Dim sel As EnvDTE.TextSelection = DTE.ActiveWindow.Selection
Dim srchTxt As String = sel.Text.Trim
If srchTxt.Length > 0 Then
DTE.ItemOperations.Navigate(String.Format(SearchURLFormat, HttpUtility.UrlEncode(srchTxt)))
End If
End Sub
#End Region

End Module