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.
By default, user templates are located in: %USERPROFILE%\Documents\Visual Studio 2019\Templates\ProjectTemplates.
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.
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); }
This is based on @drweb86 answer with some improvments and explanations.
Please notice few things:
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 ...
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; } }
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With