Logo
HOWTO: Navigate the files of a solution from a Visual Studio .NET macro or add-in

Author: Carlos J. Quintero (Microsoft MVP) Applies to: Microsoft Visual Studio .NET 2002
Date: January 2006   Microsoft Visual Studio .NET 2003
Updated: March 2013   Microsoft Visual Studio 2005
      Microsoft Visual Studio 2008
      Microsoft Visual Studio 2010
      Microsoft Visual Studio 2012
Introduction

This article describes how to navigate the files of a solution from a Visual Studio .NET macro or add-in.

More Information

Visual Studio .NET provides the following kinds of items in the Solution Explorer:

  • The solution (root node).
  • Projects.
  • Enterprise projects (only Visual Studio .NET 2002/2003 Enterprise Edition) whose children are other projects.
  • Solution folders (only VS 2005 and higher), whose parent is a solution or other solution folder, and which are containers of other solution folders or projects.
  • Project folders, whose parent is a project or other project folder, and which are containers of project folders or files. Folders can be physical (as the ones created in VB.NET or C# projects) or virtual (like the "Header Files" folder for C++ projects).
  •  Files. Some files can be nested (such as the code-behind files).

The extensibility model of Visual Studio .NET provides the following classes to navigate a solution:

  • EnvDTE.Solution, which is the type returned by the DTE.Solution property.
  • EnvDTE.Projects, which is the type returned by the Solution.Projects property.
  • EnvDTE.Project, which is the type returned by the Projects.Item property.
  • EnvDTE.ProjectItems, which is the type returned by the Project.ProjectItems or ProjectItem.ProjectItems properties.
  • EnvDTE.ProjectItem, which is the type returned by the ProjectItems.Item property.

With those classes the navigation may seem simple, but to accommodate the kinds of items detailed above you need to take into account some properties:

  • To get the children of a project you use Project.ProjectItems.
  • To get the parent project item (if any) of a project you use Project.ParentProjectItem.
  • To get the children of a project item you use ProjectItem.ProjectItems.
  • To get the parent of a project item you use ProjectItem.Collection.Parent (returned as object, since it can be a Project or ProjectItem).
  • To get the project of a project item you use ProjectItem.ContainingProject.
  • To get the project of a project item (when the parent of a project is not directly the solution, and therefore there is a ProjectItem in the middle) you use ProjectItem.SubProject.
  • To get the kind of a project you use Project.Kind.
  • To get the kind of a project item you use ProjectItem.Kind.

The Kind property of Project or ProjectItem does not return an enum value (since .NET must accommodate project kinds provided by 3rd parties). So, the Kind property returns a unique GUID string to identity the kind. The extensibility model provides some of these GUIDs scattered through several assemblies and classes (EnvDTE.Constants, VSLangProj.PrjKind, VSLangProj2.PrjKind2, etc.) but sometimes you will have to guess the values and hardcode them.

The following macro illustrates all these concepts:

Sub NavigateSolution()
 
   Dim objProject As EnvDTE.Project
 
   Try
      If Not DTE.Solution.IsOpen Then
         MessageBox.Show("Please load or create a solution")
      Else
         For Each objProject In DTE.Solution.Projects
            NavigateProject(objProject)
         Next
      End If
   Catch objException As System.Exception
      MessageBox.Show(objException.ToString)
   End Try
 
End Sub
 
Private Sub NavigateProject(ByVal objProject As Project)
 
   Dim sMsg As String
   Dim objParentProjectItem As ProjectItem
 
   sMsg = "Project Name: " & objProject.Name
   sMsg &= CrLf & "Kind: " & DecodeProjectKind(objProject.Kind)
 
   Try
      objParentProjectItem = objProject.ParentProjectItem
   Catch
   End Try
 
   If Not (objParentProjectItem Is Nothing) Then
      sMsg &= CrLf & "Parent Project Item: " & objParentProjectItem.Name
   Else
      sMsg &= CrLf & "Parent Project Item: None"
   End If
 
   MessageBox.Show(sMsg)
 
   NavigateProjectItems(objProject.ProjectItems)
 
End Sub
 
Private Sub NavigateProjectItems(ByVal colProjectItems As ProjectItems)
 
   Dim objProjectItem As EnvDTE.ProjectItem
   Dim sMsg As String
 
   If Not (colProjectItems Is Nothing) Then
 
      For Each objProjectItem In colProjectItems
 
         sMsg = "Project Item Name: " & objProjectItem.Name
         sMsg &= CrLf & "Kind: " & DecodeProjectItemKind(objProjectItem.Kind)
         sMsg &= CrLf & "Containing Project: " & objProjectItem.ContainingProject.Name
         If TypeOf objProjectItem.Collection.Parent Is Project Then
            sMsg &= CrLf & "Parent (project): " & _
               CType(objProjectItem.Collection.Parent, Project).Name
         ElseIf TypeOf objProjectItem.Collection.Parent Is ProjectItem Then
            sMsg &= CrLf & "Parent (project item): " & _
               CType(objProjectItem.Collection.Parent, ProjectItem).Name
         End If
         MessageBox.Show(sMsg)
 
         If Not (objProjectItem.SubProject Is Nothing) Then
            ' We navigate recursively because it can be:
            ' - An Enterprise project in Visual Studio .NET 2002/2003
            ' - A solution folder in VS 2005
            NavigateProject(objProjectItem.SubProject)
         Else
            ' We navigate recursively because it can be:
            ' - An folder inside a project
            ' - A project item with nested project items (code-behind files, etc.)
            NavigateProjectItems(objProjectItem.ProjectItems)
         End If
 
      Next
 
   End If
 
End Sub
 
Private Function DecodeProjectKind(ByVal sProjectKind As String) As String
 
   Const PROJECT_KIND_ENTERPRISE_PROJECT As String = "{7D353B21-6E36-11D2-B35A-0000F81F0C06}"
   Const PROJECT_KIND_CPLUSPLUS_PROJECT As String = "{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}"
   Const PROJECT_KIND_VSNET_SETUP As String = "{54435603-DBB4-11D2-8724-00A0C9A8B90C}"
 
   Dim sResult As String
 
   Select Case sProjectKind.ToUpper
      Case VSLangProj.PrjKind.prjKindVBProject.ToUpper
         sResult = "VSLangProj.PrjKind.prjKindVBProject"
      Case VSLangProj.PrjKind.prjKindCSharpProject.ToUpper
         sResult = "VSLangProj.PrjKind.prjKindCSharpProject"
      Case VSLangProj2.PrjKind2.prjKindVJSharpProject
         sResult = "VSLangProj2.PrjKind2.prjKindVJSharpProject"
      Case VSLangProj2.PrjKind2.prjKindSDEVBProject
         sResult = "VSLangProj2.PrjKind2.prjKindSDEVBProject"
      Case VSLangProj2.PrjKind2.prjKindSDECSharpProject
         sResult = "VSLangProj2.PrjKind2.prjKindSDECSharpProject"
      Case EnvDTE.Constants.vsProjectKindMisc.ToUpper
         sResult = "EnvDTE.Constants.vsProjectKindMisc"
      Case EnvDTE.Constants.vsProjectKindSolutionItems.ToUpper
         sResult = "EnvDTE.Constants.vsProjectKindSolutionItems"
      Case EnvDTE.Constants.vsProjectKindUnmodeled.ToUpper
         sResult = "EnvDTE.Constants.vsProjectKindUnmodeled"
      Case VSLangProj.PrjKind.prjKindVSAProject.ToUpper
         sResult = "VSLangProj.PrjKind.prjKindVSAProject"
      Case PROJECT_KIND_ENTERPRISE_PROJECT
         sResult = "Enterprise project"
      Case PROJECT_KIND_CPLUSPLUS_PROJECT
         sResult = "C++ project"
      Case PROJECT_KIND_VSNET_SETUP
         sResult = "Setup project"
      Case Else
         sResult = ""
   End Select
 
   Return sResult
 
End Function
 
Private Function DecodeProjectItemKind(ByVal sProjectItemKind As String) As String
 
   Dim sResult As String
 
   Select Case sProjectItemKind
      Case EnvDTE.Constants.vsProjectItemKindMisc
         sResult = "EnvDTE.Constants.vsProjectItemKindMisc"
      Case EnvDTE.Constants.vsProjectItemKindPhysicalFile
         sResult = "EnvDTE.Constants.vsProjectItemKindPhysicalFile"
      Case EnvDTE.Constants.vsProjectItemKindPhysicalFolder
         sResult = "EnvDTE.Constants.vsProjectItemKindPhysicalFolder"
      Case EnvDTE.Constants.vsProjectItemKindSolutionItems
         sResult = "EnvDTE.Constants.vsProjectItemKindSolutionItems"
      Case EnvDTE.Constants.vsProjectItemKindSubProject
         sResult = "EnvDTE.Constants.vsProjectItemKindSubProject"
      Case EnvDTE.Constants.vsProjectItemKindVirtualFolder
         sResult = "EnvDTE.Constants.vsProjectItemKindVirtualFolder"
      Case Else
         sResult = ""
   End Select
 
   Return sResult
 
End Function


Go to the 'Visual Studio Extensibility (VSX)' web site for more articles like this (Articles section)


Top