locked
Multi project templates and project names RRS feed

  • Question

  • I have a multi project template that is currently working. However, I would like to be able to have the template name the projects with in it based off the new solution name. I know there are replacement params you can use, but I've not been able to make them work with in the multip project template file it's self. The following is my template:

    <VSTemplate Version="2.0.0" Type="ProjectGroup" xmlns="http://schemas.microsoft.com/developer/vstemplate/2005">
      <TemplateData>
        <Name>Landstar\Template Test</Name>
        <Description>Test of multi Project Templates</Description>
        <Icon>__TemplateIcon.ico</Icon>
        <ProjectType>CSharp</ProjectType>   
      </TemplateData>
      <TemplateContent>
        <ProjectCollection>
          <SolutionFolder Name="Classes">
            <ProjectTemplateLink ProjectName="ClassLibrary">ClassLibrary\ClassLibrary.vstemplate</ProjectTemplateLink>
          </SolutionFolder>
          <ProjectTemplateLink ProjectName="WindowApplication">WindowsApplication\WindowsApplication.vstemplate</ProjectTemplateLink>       
          <ProjectTemplateLink ProjectName="WebApplication">WebApplication\WebApplication.vstemplate</ProjectTemplateLink>     
        </ProjectCollection>
      </TemplateContent>
    </VSTemplate>

    So in the case of the Classlibrary ProjectName I'd like to be able to have it as $solutionname$.ClassLibrary. (not sure if solutionName is a valid replacement param, but used for clarification of what I need).

    Thanks
    Wayne
    Tuesday, November 7, 2006 12:13 PM

Answers

  • Hello Wayne,

    Currently this is not a supported feature of vstemplates, but there is a way to achieve the equivalent behavior.

    The way to do it is to have your template run a custom wizard (Implements IWizard, is registered to be COM creatable and if managed can be found in the GAC or under the private/public assemblies directory). Most likely you will want to change your template to be a single project, and your wizard code would call into VS to add other projects (after having selected/computed the correct name). Here is an example of code I used to create the extra project(s) (I just did a few edit to it to make it more generic so I can't guarantee it is bug free or that it builds):

                if (project == null)
                    throw new ArgumentNullException("project");

                // We want to target C# so use the C# project guid (you can find that in the registry)
                Guid projectTypeGuid = new Guid("FAE04EC0-301F-11D3-BF4B-00C04F79EFBC");
                RegisteredProjectType projectType = RegisteredProjectType.CreateRegisteredProjectType((EnvDTE80.DTE2)project.DTE, projectTypeGuid);
                string fullPath = project.FileName;
                string name = Path.GetFileNameWithoutExtension(fullPath);
                string lastUsedPath = Path.GetDirectoryName(fullPath);

                    // Compute name of second project
                    name += "Impl";

                    // the 1st parameter below is the name of the template (assuming you are using US-english, this is likely the same as what you see in the new project dialog)
                    projectType.AddProjectFromTemplate("Class Library", lastUsedPath + "Impl", name);
                    if (project.DTE.Solution.Projects.Count > 1)
                    {
                        // Retrieve the project we just created (since the method above returns null instead of the project)
                        string uniqueName = Path.Combine(Path.GetFileNameWithoutExtension(name), name);
                        bool found = false;
                        foreach (EnvDTE.Project secondProject in project.DTE.Solution.Projects)
                        {
                            if (String.CompareOrdinal(secondProject.UniqueName, uniqueName) == 0)
                            {
                                uniqueName = secondProject.UniqueName;
                                found = true;
                                break;
                            }
                        }
                        if (found)
                        {
                            // Set the build order correctly (you could also add project references here if it made sense, although you may also be able to do that in your template if your name computation is simple enough which was my case)
                            EnvDTE.SolutionBuild build = project.DTE.Solution.SolutionBuild;
                            EnvDTE.BuildDependency dependency = build.BuildDependencies.Item(uniqueName);
                            if (dependency != null)
                                dependency.AddProject(project.UniqueName);
                        }
                    }

    I used similar code as my IWizard.ProjectFinishedGenerating implementation.

     

    I hope this helps

     

    Rusty

    Thursday, November 9, 2006 5:42 PM

All replies

  • Hello Wayne,

    Currently this is not a supported feature of vstemplates, but there is a way to achieve the equivalent behavior.

    The way to do it is to have your template run a custom wizard (Implements IWizard, is registered to be COM creatable and if managed can be found in the GAC or under the private/public assemblies directory). Most likely you will want to change your template to be a single project, and your wizard code would call into VS to add other projects (after having selected/computed the correct name). Here is an example of code I used to create the extra project(s) (I just did a few edit to it to make it more generic so I can't guarantee it is bug free or that it builds):

                if (project == null)
                    throw new ArgumentNullException("project");

                // We want to target C# so use the C# project guid (you can find that in the registry)
                Guid projectTypeGuid = new Guid("FAE04EC0-301F-11D3-BF4B-00C04F79EFBC");
                RegisteredProjectType projectType = RegisteredProjectType.CreateRegisteredProjectType((EnvDTE80.DTE2)project.DTE, projectTypeGuid);
                string fullPath = project.FileName;
                string name = Path.GetFileNameWithoutExtension(fullPath);
                string lastUsedPath = Path.GetDirectoryName(fullPath);

                    // Compute name of second project
                    name += "Impl";

                    // the 1st parameter below is the name of the template (assuming you are using US-english, this is likely the same as what you see in the new project dialog)
                    projectType.AddProjectFromTemplate("Class Library", lastUsedPath + "Impl", name);
                    if (project.DTE.Solution.Projects.Count > 1)
                    {
                        // Retrieve the project we just created (since the method above returns null instead of the project)
                        string uniqueName = Path.Combine(Path.GetFileNameWithoutExtension(name), name);
                        bool found = false;
                        foreach (EnvDTE.Project secondProject in project.DTE.Solution.Projects)
                        {
                            if (String.CompareOrdinal(secondProject.UniqueName, uniqueName) == 0)
                            {
                                uniqueName = secondProject.UniqueName;
                                found = true;
                                break;
                            }
                        }
                        if (found)
                        {
                            // Set the build order correctly (you could also add project references here if it made sense, although you may also be able to do that in your template if your name computation is simple enough which was my case)
                            EnvDTE.SolutionBuild build = project.DTE.Solution.SolutionBuild;
                            EnvDTE.BuildDependency dependency = build.BuildDependencies.Item(uniqueName);
                            if (dependency != null)
                                dependency.AddProject(project.UniqueName);
                        }
                    }

    I used similar code as my IWizard.ProjectFinishedGenerating implementation.

     

    I hope this helps

     

    Rusty

    Thursday, November 9, 2006 5:42 PM
  • We pretty much determined that it was not built in before I read  your reply. As such we went with using the GAT/GAX from P&P, this has worked great.

    Thanks
    Wayne
    Wednesday, November 22, 2006 3:46 PM
  • Hi...I'm trying to implement a multi-project template and I came across this thread. It seems to be exactly what I'm looking for. Unfortunately, I can't seem to figure out what assembly I need to reference for the RegisteredProjectType class to be recognized. I have VS2005 along with the SDK installed. Some searching produced the Microsoft.VisualStudio.Package namespace, but I have had no success in locating that on my system. Any help would be greatly appreciated.
    Saturday, August 23, 2008 9:20 PM
  • Nevermind.  I ended up using Project.DTE.Solution.AddFromFile() instead.

    One thing that vexed me about this though is the fact that VS2005 seems to overwrite the <ProjectGuid> with its own Guid after the  RunFinished() exits and doesn't seem to expose that value anywhere. I needed this value to link in my vdproj installer project. I ended up doing a crazy multithreaded hack that waits a bit, then parses the csproj file for the Guid. Are there any alternatives to this less-than-desireable approach?
    Sunday, August 24, 2008 1:14 AM