视觉工作室模板问题&目录创建

时间:2010-10-07 14:37:09

标签: visual-studio templates project

我正在尝试制作Visual Studio(2010)模板(多项目)。一切似乎都很好,除了项目是在解决方案的子目录中创建的。这不是我正在寻找的行为。

zip文件包含:

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>修改为true或false,不会改变任何内容。

5 个答案:

答案 0 :(得分:8)

要在根级别创建解决方案(不将它们嵌套在子文件夹中),您必须创建两个模板: 1)ProjectGroup存根模板,里面有你的向导,它将在你的最后创建新的项目 2)项目模板

使用以下方法

<强> 1。添加类似这样的模板

  <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。向代码添加代码

// 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);
}

答案 1 :(得分:4)

这是基于@ drweb86的答案以及一些改进和解释。
请注意几点:

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

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

       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线程,我们假设将创建解决方案和最终目标文件夹,然后我们才能安全地删除此文件夹。

答案 2 :(得分:4)

仅使用向导的另一种解决方案:

    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>标记中,按大多数引用最少引用的顺序排列。因为删除和添加项目。

答案 3 :(得分:3)

多项目模板非常棘手。我发现$safeprojectname$的处理几乎不可能创建一个多项目模板并正确替换名称空间值。我必须创建一个自定义向导,该向导会点亮一个新变量$saferootprojectname$,该变量始终是用户输入新项目名称的值。

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

我有6 minute video on creating project templates with TemplateBuilder。对于多项目模板,这个过程有点麻烦(但仍然比不使用TemplateBuilder好得多。我在https://github.com/ligershark/side-waffle/tree/master/TemplatePack/ProjectTemplates/Web/_Sample%20Multi%20Project的SideWaffle源代码中有一个示例多项目模板。

答案 4 :(得分:1)

实际上有一种解决方法,它是丑陋的,但在diggin&#39;网络我无法发明任何更好的东西。因此,在创建多项目解决方案的新实例时,您必须取消选中“创建新文件夹”&#39;对话框中的复选框。在开始之前,目录结构应该像

 Projects
{no dedicated folder yet}

创建解决方案后,结构如下:

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

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

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