# Sunday, May 20, 2007

The Visual Studio solution file for our software contains 36 projects (and growing).  If you've ever tried to find a particular file or project in a 36 project solution when many projects and folders are expanded, then you know how frustrating it can be.

The Solution

After putting up with it for over a year, I finally asked a co-worker of mine if he knew of a way to quickly jump to a particular project in Visual Studio.  He reminded me that Visual Studio has excellent macro support.  A few minutes later using Visual Studios Macro Recorder feature I had something to jump to a project I'm in a lot.  All told, I jump between around 4 projects pretty regularly - our business logic layer, domain model, smart client and UI, and having these macros have been a huge time saver!

The Visual Studio Macro

Sub TemporaryMacro() DTE.ActiveWindow.Object.GetItem("MySolution\MyProject").UIHierarchyItems.Expanded = True DTE.ActiveWindow.Object.GetItem("MySolution\MyProject").Select(vsUISelectionType.vsUISelectionTypeSelect) End Sub

Using this macro, you can straight to the "MyProject" project in your solution.  This works, but its not very generic.  The solution name is hard coded, which is fine if the particular project only lives in one solution.  But in our case, we have 3 or 4 different solutions created.  The monster with all 36 solutions, a client only solution with 10 of the projects, and a core only solution.

I don't feel like creating different macros to jump between the same 5 projects depending upon which solution is open.  So lets refactor this a little bit. 

1 Private Function GetSolution() As UIHierarchyItem 2 Dim win As Window = DTE.Windows.Item(Constants.vsWindowKindSolutionExplorer) 3 Dim uih As UIHierarchy = win.Object 4 GetSolution = uih.UIHierarchyItems.Item(1) 5 End Function 6 7 Private Function GetProject(ByVal ProjectName As String) As UIHierarchyItem 8 GetProject = GetSolution().UIHierarchyItems.Item(ProjectName) 9 End Function 10 11 Private Sub GotoProject(ByVal ProjectName As String) 12 DTE.Windows.Item(Constants.vsWindowKindSolutionExplorer).Activate() 13 14 Try 15 GetProject(ProjectName).Select(vsUISelectionType.vsUISelectionTypeSelect) 16 GetProject(ProjectName).UIHierarchyItems.Expanded = True 17 Catch ex As Exception 18 End Try 19 End Sub 20 21 Public Sub MyProject() 22 GotoProject("MyProject") 23 End Sub

Lets break this down. The GetSolution method returns a UIHierarchyItem (an item in the solution explorer tree you can click on). The first thing we do is tell the DTE (What does DTE stand for btw?) we want the solution explorer window (line 2), once we have that we can get the hierarchy (line 3), then we can get the items and get the first item (line 4) which is the solution item in the hierarchy.

NOTE: The macro environment is based on COM,  and in COM collections and arrays start at 1, and not 0.

In the GetProject method, you should see code which looks familiar.  The interesting things come in the GotoProject method.  First we active the the solution explorer window in the IDE (line 12), then get the project hierarchy item and select it so it has focus (line 15), and finally we expand the item in the hierarchy so you can see all the files under the project (line 16).

And finally on line 21 we have the actual callable macro which will warp us to the project we want in the solution.

A Good First Step

So thats a good first step, but what else can we do?  We can collapse all the items in the solution explorer, open up a particular file, or even "fix" items in your solution.

Collapsing the Solution Explorer

One thing I don't like about Visual Studio is over time, with a large number of projects,  a lot of projects, folders and compound items (winforms items with a designer and resource file) get expanded and it makes it especially hard to pick things out of the visual clutter.  I always find it to be very tedious to close all the items in the solution to clean up the clutter.  Lets write a macro to fix this mess for us.

1 Public Sub CollapseTopLevel() 2 DTE.Windows.Item(Constants.vsWindowKindSolutionExplorer).Activate() 3 4 Dim solutionWindow As EnvDTE.Window = DTE.ActiveWindow 5 solutionWindow.Visible = False 6 Dim solution As UIHierarchyItem = GetSolution() 7 8 CollapseHierarchy(solution.UIHierarchyItems, True, True) 9 solutionWindow.Visible = True 10 DTE.StatusBar.Clear() 11 DTE.StatusBar.Progress(False) 12 End Sub 13 Public Sub CollapseAll() 14 DTE.Windows.Item(Constants.vsWindowKindSolutionExplorer).Activate() 15 16 Dim solutionWindow As EnvDTE.Window = DTE.ActiveWindow 17 solutionWindow.Visible = False 18 Dim solution As UIHierarchyItem = GetSolution() 19 20 CollapseHierarchy(solution.UIHierarchyItems, True, False) 21 solutionWindow.Visible = True 22 DTE.StatusBar.Clear() 23 DTE.StatusBar.Progress(False) 24 End Sub 25 Private Sub CollapseHierarchy(ByRef items As UIHierarchyItems, ByVal IsRoot As Boolean, ByVal OnlyCollapseRootLevel As Boolean) 26 For i As Int32 = 1 To items.Count 27 If IsRoot Then DTE.StatusBar.Progress(True, "Collapsing", i, items.Count) 28 If (items.Item(i).UIHierarchyItems.Count > 0 And Not OnlyCollapseRootLevel) Then 29 DTE.StatusBar.Text = "Collapsing " & items.Item(i).Name 30 CollapseHierarchy(items.Item(i).UIHierarchyItems, False, False) 31 End If 32 items.Item(i).UIHierarchyItems.Expanded = False 33 Next 34 End Sub

There are two different methods here to collapse the solution, CollapseTopLevel and CollapseAll.  CollapseTopLevel only collapses project items in the UI, while CollapseAll will drill down and collapse every item.  The first is fast, takes less than a second to collapse 36 items, while the later takes about 15 seconds.  If you notice lines 4, 5 we grab a reference to the solution window, and then hide it.  If the solution window is visible while the projects are collapsed, the whole process takes much longer while the UI repaints.

Opening a Particular File

When working on our data access layer (DAL), I do a lot of editing of our SQL upgrade script.  I don't really like to try and find this file in the solution explorer.  More than that, when I'm editing this file, I'm always adding to the bottom of this file.  Can we do all that with a macro?  Sure enough!

1 Public Sub DbScripts() 2 DTE.Windows.Item(Constants.vsWindowKindSolutionExplorer).Activate() 3 Dim project As UIHierarchyItem = GetSolution().UIHierarchyItems.Item("SchemaUpgradeManagerProject") 4 Dim scripts As ProjectItem = project.UIHierarchyItems.Item("Scripts").UIHierarchyItems.Item("DbScripts.sql").Object 5 scripts.Open().Activate() 6 DTE.ActiveDocument.Selection.EndOfDocument() 7 End Sub

Fixing Things in the Solution

One of the frustrating things about creating NHibernate mapping files is remembering to set the build action to Embedded Resource.  Or remembering to set the build action of images, sounds and movie clips and other files to Embedded Resource.  Why not let a macro fix this mess for you too?

Public Sub FixThings() Try Dim project As Project Dim projectItem As UIHierarchyItem Dim folder As UIHierarchyItem projectItem = GetProject("DataAccessProject") folder = projectItem.UIHierarchyItems.Item("Mappings") EmbeddResources(folder, "hbm.xml") project = projectItem.Object project.Save() projectItem = GetProject("UserInterfaceProject") folder = projectItem.UIHierarchyItems.Item("Icons") EmbeddResources(folder, "png") project = projectItem.Object project.Save() DTE.StatusBar.Clear() DTE.StatusBar.Progress(False) Catch ex As Exception End Try End Sub Private Sub EmbeddResources(ByRef folder As UIHierarchyItem, ByVal extension As String) Dim file As ProjectItem For i As Int32 = 1 To folder.UIHierarchyItems.Count file = folder.UIHierarchyItems.Item(i).Object DTE.StatusBar.Progress(True, "Setting BuildAction in " & folder.Name, i, folder.UIHierarchyItems.Count) If file.Name.EndsWith(extension) Then file.Properties.Item("BuildAction").Value = 3 ' Embedded Resource End If Next End Sub

There you have it, some simple Visual Studio macros to make your development much easier!  Questions, comments, concerns, leave me a comment.

Your Macros, Now Only a Click Away!

Now that we have all of these macros, we need a way to quickly and easily access them.  Custom Visual Studio toolbars to the rescue!  To create a custom toolbar, click on Tools -> Customize -> Toolbars.  From there click on the "New" button and give it a name.  I choose the very original name of "My Macros" :). Dock your toolbar if you'd like.

With that done, click over to "Commands" tab and click on "Macros" in the left-side. All of your macros, as well as the sample macros will show up in the right-side.  Drag the macros you want to the toolbar you just created.  You'll notice that your macro has a really long name like "Macros.MyMacros.SomeName.CollapseTopLevel."  Long names like that will quickly eat up your valuable screen real estate.

Thankfully we can fix that.  While still in Customize mode, you can right-click on your macro in the toolbar (and indeed any item in any toolbar) and the third option down is "Name."  Set this to what ever you want.  Since screen real-estate is precious to me, I named my "Macros.MyMacros.ProjectName.CollapseTopLevel" macro to "ClpseTopLvl".


In my haste to publish this article at around 1 AM I failed to mention how to create a custom tool bar to give you quick access to all of your macros.

11-Feb-2009 - Johnny Idol has referenced this blog post, and gone into detail about how to actually create the macros and bind keyboard shortcuts to them.

Sunday, May 20, 2007 1:12:32 AM (Alaskan Daylight Time, UTC-08:00)
Tuesday, May 22, 2007 3:57:45 PM (Alaskan Daylight Time, UTC-08:00)
Just read your post, and thought you might be interested in a great addin for VS.
I am very happy with it and it does the things you wrote a macro for.
There is ALT+U which opens a dialog box for you to type the name of the file you want.
There is Collapse All Items when you right click in the Solution Explorer and a whole bunch of other stuff.
It a free addin (I am not the DEV, just a happy user) called DPack. Found here: http://www.usysware.com/dpack/
Brian Schmitt
Wednesday, May 23, 2007 12:38:57 AM (Alaskan Daylight Time, UTC-08:00)
I've never heard of that plugin. I did take a look at it, and as a whole its not bad. However, I already have to other resource sucking plugins installed - AnkhSVN and ReSharper. You can press ctrl-shift-n in Re#r to open/jump to a file, but #1 I always forget about it and #2 half the time I don't know off the top of my head which file I need to edit, just which project its in.

As for the Collapse All Items feature in DPack, I prefer to click one button and have everything done. Having to navigate my way to the solution item in the solution explorer just to collapse all the items would be a huge time waster for me.

If I wasn't working for a company that invests so heavily in its developers, I probably would install this utility...
Dan morphis
Saturday, August 02, 2008 4:38:33 AM (Alaskan Daylight Time, UTC-08:00)
slightly late, but DTE stands for development tools environment.
Friday, December 19, 2008 1:08:18 AM (Alaskan Standard Time, UTC-09:00)
I found the sample code scripts very useful and helpful for using them for others

All comments require the approval of the site owner before being displayed.
(will show your gravatar icon)
Home page

Comment (HTML not allowed)  

Enter the code shown (prevents robots):

Live Comment Preview