代码之家  ›  专栏  ›  技术社区  ›  Fabian Vilers

创建visual studio模板和目录时出现问题

  •  24
  • Fabian Vilers  · 技术社区  · 14 年前

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

    这是我的根模板:

    <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>
    

    而且,当使用此模板创建解决方案时,我最终得到的目录如下:

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

    有什么帮助吗?

    编辑:

    似乎在修改 <CreateNewFolder>false</CreateNewFolder> 无论是真是假,都不会改变任何事情。

    5 回复  |  直到 14 年前
        1
  •  8
  •   Filburt kukabuka    12 年前

    要在根级别创建解决方案(而不是将它们嵌套在子文件夹中),必须创建两个模板: 1) 包含向导的项目组存根模板,该模板将在 2) 项目模板

    一。添加类似这样的模板

      <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>
    

    // 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);
    }
    
        2
  •  7
  •   Dheeraj Vepakomma    8 年前

    这是基于@drweb86的答案和一些改进和解释。

    1. 带有项目链接的真实模板位于某个虚拟文件夹下,因为不能有多个根vstemplate。(在这种情况下,Visual studio根本不会显示您的模板)。
    2. 所有子项目\模板必须位于真实模板文件文件夹下。

      RootTemplateFix.vstemplate
      -> Template Folder
         YourMultiTemplate.vstemplate
              -->Sub Project Folder 1
                 SubProjectTemplate1.vstemplate
              -->Sub Project Folder 2
                 SubProjectTemplate2.vstemplate
              ...
      
    3. 在根模板向导中,可以运行用户选择表单并将其添加到静态变量中。子向导可以将这些全局参数复制到其私有字典中。

       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. 请注意,原始目标文件夹删除是通过其他线程完成的。
      原因是生成了解决方案 向导结束,将重新创建此目标文件夹。
      通过使用ohter线程,我们假设将创建解决方案和最终目标文件夹,只有这样我们才能安全地删除此文件夹。
        3
  •  5
  •   EliSherer    9 年前

    单独使用向导的另一个解决方案:

        public void RunStarted(object automationObject, Dictionary<string, string> replacementsDictionary, WizardRunKind runKind, object[] customParams)
        {
            try
            {
                _dte = automationObject as DTE2;
                _destinationDirectory = replacementsDictionary["$destinationdirectory$"];
                _safeProjectName = replacementsDictionary["$safeprojectname$"];
    
                //Add custom parameters
            }
            catch (WizardCancelledException)
            {
                throw;
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex + Environment.NewLine + ex.StackTrace, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
                throw new WizardCancelledException("Wizard Exception", ex);
            }
        }
    
        public void RunFinished()
        {
            if (!_destinationDirectory.EndsWith(_safeProjectName + Path.DirectorySeparatorChar + _safeProjectName))
                return;
    
            //The projects were created under a seperate folder -- lets fix it
            var projectsObjects = new List<Tuple<Project,Project>>();
            foreach (Project childProject in _dte.Solution.Projects)
            {
                if (string.IsNullOrEmpty(childProject.FileName)) //Solution Folder
                {
                    projectsObjects.AddRange(from dynamic projectItem in childProject.ProjectItems select new Tuple<Project, Project>(childProject, projectItem.Object as Project));
                }
                else
                {
                    projectsObjects.Add(new Tuple<Project, Project>(null, childProject));
                }
            }
    
            foreach (var projectObject in projectsObjects)
            {
                var projectBadPath = projectObject.Item2.FileName;
                var projectGoodPath = projectBadPath.Replace(
                    _safeProjectName + Path.DirectorySeparatorChar + _safeProjectName + Path.DirectorySeparatorChar, 
                    _safeProjectName + Path.DirectorySeparatorChar);
    
                _dte.Solution.Remove(projectObject.Item2);
    
                Directory.Move(Path.GetDirectoryName(projectBadPath), Path.GetDirectoryName(projectGoodPath));
    
                if (projectObject.Item1 != null) //Solution Folder
                {
                    var solutionFolder = (SolutionFolder)projectObject.Item1.Object;
                    solutionFolder.AddFromFile(projectGoodPath);
                }
                else
                {
                    _dte.Solution.AddFromFile(projectGoodPath);
                }
            }
    
            ThreadPool.QueueUserWorkItem(dir =>
            {
                System.Threading.Thread.Sleep(2000);
                Directory.Delete(_destinationDirectory, true);
            }, _destinationDirectory);
        }
    

    这支持一个级别的解决方案文件夹(如果需要,可以使我的解决方案递归以支持每个级别)

    确保 <ProjectCollection> 按引用最多到引用最少的顺序标记。因为项目的删除和添加。

        4
  •  4
  •   Sayed Ibrahim Hashimi    10 年前

    $safeprojectname$ 几乎不可能创建多项目模板并正确替换命名空间值。我不得不创建一个自定义向导来点亮一个新变量 $saferootprojectname$ 它始终是用户在新项目的名称中输入的值。

    SideWaffle (这是一个包含许多模板的模板包)我们有几个多项目模板。SideWaffle使用TemplateBuilder NuGet包。TemplateBuilder提供了多项目模板所需的向导。

    我有一个 6 minute video on creating project templates with TemplateBuilder https://github.com/ligershark/side-waffle/tree/master/TemplatePack/ProjectTemplates/Web/_Sample%20Multi%20Project .

        5
  •  2
  •   Konstantin Chernov    9 年前

    实际上有一个解决办法,它很难看,但在网络挖掘之后,我再也无法创造出更好的东西了。因此,在创建多项目解决方案的新实例时,必须取消选中对话框中的“创建新文件夹”复选框。在开始之前,目录结构应该是

     Projects
    {no dedicated folder yet}
    

    Projects
        +--MyApplication1
             +-- Project1
             +-- Project2
        solution file
    

    因此,与所需结构的唯一细微差别是解决方案文件的位置。因此,在生成并显示新的解决方案之后,您应该做的第一件事是选择解决方案并在菜单中选择“另存为”,然后将文件移动到 MyApplication1 文件夹。然后删除以前的解决方案文件,现在,文件结构如下:

    Projects
        +--MyApplication1
             +-- Project1
             +-- Project2
             solution file