Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Issue with visual studio template & directory creation

I'm trying to make a Visual Studio (2010) template (multi-project). Everything seems good, except that the projects are being created in a sub-directory of the solution. This is not the behavior I'm looking for.

The zip file contains:

Folder1 +-- Project1     +-- Project1.vstemplate +-- Project2     +-- Project2.vstemplate myapplication.vstemplate 

Here's my root template:

<VSTemplate Version="3.0.0" Type="ProjectGroup" xmlns="http://schemas.microsoft.com/developer/vstemplate/2005">     <TemplateData>         <Name>My application</Name>         <Description></Description>         <Icon>Icon.ico</Icon>         <ProjectType>CSharp</ProjectType>   <RequiredFrameworkVersion>4.0</RequiredFrameworkVersion>   <DefaultName>MyApplication</DefaultName>   <CreateNewFolder>false</CreateNewFolder>     </TemplateData>     <TemplateContent>         <ProjectCollection>    <SolutionFolder Name="Folder1">     <ProjectTemplateLink ProjectName="$safeprojectname$.Project1">Folder1\Project1\Project1.vstemplate</ProjectTemplateLink>     <ProjectTemplateLink ProjectName="$safeprojectname$.Project2">Folder2\Project2\Project2.vstemplate</ProjectTemplateLink>    </SolutionFolder>         </ProjectCollection>     </TemplateContent> </VSTemplate> 

And, when creating the solution using this template, I end up with directories like this:

Projects +-- MyApplication1     +-- MyApplication1 // I'd like to have NOT this directory         +-- Folder1             +-- Project1             +-- Project2     solution file 

Any help?

EDIT:

It seems that modifying <CreateNewFolder>false</CreateNewFolder>, either to true or false, doesn't change anything.

like image 518
Fabian Vilers Avatar asked Oct 07 '10 14:10

Fabian Vilers


People also ask

Where are Visual Studio project templates stored?

By default, user templates are located in: %USERPROFILE%\Documents\Visual Studio 2019\Templates\ProjectTemplates.

What is Visual Studio template?

Visual Studio templates These templates, such as the ASP.NET Web Application and Class Library templates, are available to choose from when you create a new project. Item templates, such as code files, XML files, HTML pages, and Style Sheets, appear in the Add New Item window.


2 Answers

To create solution at root level (not nest them in subfolder) you must create two templates: 1) ProjectGroup stub template with your wizard inside that will create new project at the end from your 2) Project template

use the following approach for that

1. Add template something like this

  <VSTemplate Version="2.0.0" xmlns="http://schemas.microsoft.com/developer/vstemplate/2005" Type="ProjectGroup">     <TemplateData>       <Name>X Application</Name>       <Description>X Shell.</Description>       <ProjectType>CSharp</ProjectType>       <Icon>__TemplateIcon.ico</Icon>     </TemplateData>     <TemplateContent>     </TemplateContent>     <WizardExtension>     <Assembly>XWizard, Version=1.0.0.0, Culture=neutral</Assembly>     <FullClassName>XWizard.FixRootFolderWizard</FullClassName>     </WizardExtension>     </VSTemplate> 

2. Add code to wizard

// creates new project at root level instead of subfolder. public class FixRootFolderWizard : IWizard {     #region Fields      private string defaultDestinationFolder_;     private string templatePath_;     private string desiredNamespace_;      #endregion      #region Public Methods     ...     public void RunFinished()     {         AddXProject(             defaultDestinationFolder_,             templatePath_,             desiredNamespace_);     }      public void RunStarted(object automationObject,         Dictionary<string, string> replacementsDictionary,         WizardRunKind runKind, object[] customParams)     {         defaultDestinationFolder_ = replacementsDictionary["$destinationdirectory$"];         templatePath_ =              Path.Combine(                 Path.GetDirectoryName((string)customParams[0]),                 @"Template\XSubProjectTemplateWizard.vstemplate");           desiredNamespace_ = replacementsDictionary["$safeprojectname$"];           string error;          if (!ValidateNamespace(desiredNamespace_, out error))          {              controller_.ShowError("Entered namespace is invalid: {0}", error);              controller_.CancelWizard();          }      }       public bool ShouldAddProjectItem(string filePath)      {          return true;      }       #endregion  }   public void AddXProject(      string defaultDestinationFolder,      string templatePath,      string desiredNamespace)  {      var dte2 = (DTE) System.Runtime.InteropServices.Marshal.GetActiveObject("VisualStudio.DTE.10.0");      var solution = (EnvDTE100.Solution4) dte2.Solution;       string destinationPath =          Path.Combine(              Path.GetDirectoryName(defaultDestinationFolder),              "X");       solution.AddFromTemplate(          templatePath,          destinationPath,          desiredNamespace,          false);      Directory.Delete(defaultDestinationFolder); } 
like image 166
Siarhei Kuchuk Avatar answered Nov 04 '22 09:11

Siarhei Kuchuk


This is based on @drweb86 answer with some improvments and explanations.
Please notice few things:

  1. The real template with projects links is under some dummy folder since you can't have more than one root vstemplate. (Visual studio will not display your template at all at such condition).
  2. All the sub projects\templates have to be located under the real template file folder.
    Zip template internal structure example:

    RootTemplateFix.vstemplate -> Template Folder    YourMultiTemplate.vstemplate         -->Sub Project Folder 1            SubProjectTemplate1.vstemplate         -->Sub Project Folder 2            SubProjectTemplate2.vstemplate         ... 
  3. On the root template wizard you can run your user selection form and add them into a static variable. Sub wizards can copy these Global Parameters into their private dictionary.

Example:

   public class WebAppRootWizard : IWizard    {     private EnvDTE._DTE _dte;     private string _originalDestinationFolder;     private string _solutionFolder;     private string _realTemplatePath;     private string _desiredNamespace;      internal readonly static Dictionary<string, string> GlobalParameters = new Dictionary<string, string>();      public void BeforeOpeningFile(ProjectItem projectItem)     {     }      public void ProjectFinishedGenerating(Project project)     {     }      public void ProjectItemFinishedGenerating(ProjectItem         projectItem)     {     }      public void RunFinished()     {         //Run the real template         _dte.Solution.AddFromTemplate(             _realTemplatePath,             _solutionFolder,             _desiredNamespace,             false);          //This is the old undesired folder         ThreadPool.QueueUserWorkItem(new System.Threading.WaitCallback(DeleteDummyDir), _originalDestinationFolder);     }      private void DeleteDummyDir(object oDir)     {         //Let the solution and dummy generated and exit...         System.Threading.Thread.Sleep(2000);          //Delete the original destination folder         string dir = (string)oDir;         if (!string.IsNullOrWhiteSpace(dir) && Directory.Exists(dir))         {             Directory.Delete(dir);         }     }      public void RunStarted(object automationObject,         Dictionary<string, string> replacementsDictionary,         WizardRunKind runKind, object[] customParams)     {         try         {             this._dte = automationObject as EnvDTE._DTE;              //Create the desired path and namespace to generate the project at             string temlateFilePath = (string)customParams[0];             string vsixFilePath = Path.GetDirectoryName(temlateFilePath);             _originalDestinationFolder = replacementsDictionary["$destinationdirectory$"];             _solutionFolder = replacementsDictionary["$solutiondirectory$"];             _realTemplatePath = Path.Combine(                 vsixFilePath,                 @"Template\BNHPWebApplication.vstemplate");             _desiredNamespace = replacementsDictionary["$safeprojectname$"];              //Set Organization             GlobalParameters.Add("$registeredorganization$", "My Organization");              //User selections interface             WebAppInstallationWizard inputForm = new WebAppInstallationWizard();             if (inputForm.ShowDialog() == DialogResult.Cancel)             {                 throw new WizardCancelledException("The user cancelled the template creation");             }              // Add user selection parameters.             GlobalParameters.Add("$my_user_selection$",                 inputForm.Param1Value);         }         catch (Exception ex)         {             MessageBox.Show(ex.ToString(), "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);         }     }      public bool ShouldAddProjectItem(string filePath)     {         return true;     } }     
  1. Notice that the original destination folder deletion is done via a different thread.
    The reason is that the solution is generated after your wizard ends and this destination folder will get recreated.
    By using ohter thread we assume that the solution and final destination folder will get created and only then we can safely delete this folder.
like image 27
Yaron Avatar answered Nov 04 '22 07:11

Yaron