Visual Studio 多项目模板

     2023-05-08     290

关键词:

【中文标题】Visual Studio 多项目模板【英文标题】:VisualStudio Multi project Templates 【发布时间】:2012-04-05 05:50:26 【问题描述】:

我正在创建一个多项目模板

问题是当我运行模板时,每个项目都会创建一个与项目名称匹配的目录文件夹。

我不希望每个项目都默认创建文件夹,如下所示:

solutionfolder\Libraries\BL\projectname\ 我的文件 - 和 csproj 文件 solutionfolder\Libraries\BL\interfaces\projectname\ 我的文件 - 和 csproj 文件

我想要的是:

solutionfolder\Libraries\BL\ 我的文件和 csproj 文件 solutionfolder\Libraries\BL\interfaces\ 我的文件和 csproj 文件

我试过<CreateNewFolder> false </CreateNewFolder> 但这不起作用

如何在不创建项目文件夹的情况下创建项目模板?

<ProjectCollection>
    <SolutionFolder Name="Libraries">
        <SolutionFolder Name="BL">
            <SolutionFolder Name="Interfaces">
                <ProjectTemplateLink ProjectName="BL_$safeprojectname$_Interfaces">Libraries\BL\Interfaces\MyTemplate.vstemplate</ProjectTemplateLink>
            </SolutionFolder>
            <ProjectTemplateLink ProjectName="BL_$safeprojectname$">Libraries\BL\MyTemplate.vstemplate</ProjectTemplateLink>
        </SolutionFolder>
</ProjectCollection>

【问题讨论】:

【参考方案1】:

您可以通过创建向导模板来移动文件夹和项目项。只需将逻辑放在 ProjectFinishedGenerating() 方法中即可。

我将此文件用作我自己模板的参考,特别是 MoveProjectTo() 方法。

WizardImplementation.csoriginal link、cached)

using System;
using System.Collections.Generic;
using Microsoft.VisualStudio.TemplateWizard;
using VSLangProj;
using EnvDTE;
using EnvDTE80;
using System.IO;
using System.Windows.Forms;
using System.Threading;
using System.Xml;

namespace SharpArchApplicationWizard

    // Class that implements the IWizard() interface
    internal class WizardImplementation : IWizard
    
        /// <summary>
        /// Provide a means for sub-projects to have access to the solution name
        /// </summary>
        private static string solutionName;
        private static string guidAssignedToCore = "00000000-0000-0000-0000-000000000000";
        private static string guidAssignedToData = "00000000-0000-0000-0000-000000000000";
        private static string guidAssignedToApplicationServices = "00000000-0000-0000-0000-000000000000";
        private static string guidAssignedToControllers = "00000000-0000-0000-0000-000000000000";

        private const int MIN_TIME_FOR_PROJECT_TO_RELEASE_FILE_LOCK = 700;
        private EnvDTE._DTE dte;
        private WizardRunKind runKind;
        private Dictionary<string, string> replacementsDictionary;

        // RunStarted() method gets called before the template wizard creates the project.
        void IWizard.RunStarted(object application, Dictionary<string, string> replacementsDictionary, WizardRunKind runKind, object[] customParams) 
            this.dte = application as EnvDTE._DTE;
            this.runKind = runKind;
            this.replacementsDictionary = replacementsDictionary;

            // Store the solution name locally while processing the solution template
            if (runKind == WizardRunKind.AsMultiProject) 
                solutionName = replacementsDictionary["$safeprojectname$"];
            

            replacementsDictionary.Add("$solutionname$", solutionName);

            if (runKind == WizardRunKind.AsNewProject) 
                // Make the solution root path available for all the projects
                replacementsDictionary.Add("$solutionrootpath$", GetSolutionRootPath() + solutionName + "\\");

                AddProjectGuidsTo(replacementsDictionary);
            
        

        /// <summary>
        /// Makes the project GUIDs, which are collected during the project creation process, 
        /// available to subsequent projects
        /// </summary>
        private static void AddProjectGuidsTo(Dictionary<string, string> replacementsDictionary) 
            replacementsDictionary.Add("$guidAssignedToCore$", guidAssignedToCore);
            replacementsDictionary.Add("$guidAssignedToData$", guidAssignedToData);
            replacementsDictionary.Add("$guidAssignedToApplicationServices$", guidAssignedToApplicationServices);
            replacementsDictionary.Add("$guidAssignedToControllers$", guidAssignedToControllers);
        

        /// <summary>
        /// Runs custom wizard logic when a project has finished generating
        /// </summary>
        void IWizard.ProjectFinishedGenerating(EnvDTE.Project project) 
            if (project != null) 
                if (project.Name == "SolutionItemsContainer") 
                    PerformSolutionInitialization(project);
                    MoveSolutionItemsToLib(project);
                
                else if (project.Name == "ToolsSolutionItemsContainer") 
                    MoveSolutionItemsToToolsLib(project);
                
                else if (project.Name == "CrudScaffolding") 
                    Project movedProject = MoveProjectTo("\\tools\\", project, "Code Generation");
                    ExcludeProjectFromBuildProcess(movedProject);
                
                else if (project.Name == GetSolutionName() + ".Tests") 
                    MoveProjectTo("\\tests\\", project);
                
                else if (project.Name == GetSolutionName() + ".Web.Controllers" ||
                    project.Name == GetSolutionName() + ".ApplicationServices" ||
                    project.Name == GetSolutionName() + ".Core" ||
                    project.Name == GetSolutionName() + ".Data" ||
                    project.Name == GetSolutionName() + ".Web") 
                    Project movedProject = MoveProjectTo("\\app\\", project);

                    // Give the solution time to release the lock on the project file
                    System.Threading.Thread.Sleep(MIN_TIME_FOR_PROJECT_TO_RELEASE_FILE_LOCK);

                    CaptureProjectGuidOf(movedProject);
                
            
        

        private void CaptureProjectGuidOf(Project project) 
            if (IsProjectReferredByOtherProjects(project)) 
                string projectPath = GetSolutionRootPath() + GetSolutionName() + "\\app\\" + project.Name + "\\" + project.Name + ".csproj";

                Log("CaptureProjectGuidOf: Does " + projectPath + " exist? " + File.Exists(projectPath).ToString());
                Log("CaptureProjectGuidOf: About to open " + projectPath);

                XmlDocument xmlDocument = new XmlDocument();
                xmlDocument.Load(projectPath);
                XmlNodeList projectGuidNodes = xmlDocument.GetElementsByTagName("ProjectGuid");

                if (projectGuidNodes == null || projectGuidNodes.Count == 0)
                    throw new ApplicationException("Couldn't find a matching node in the project file for ProjectGuid");

                StoreCapturedGuidForLaterUse(project, projectGuidNodes);

                Log("CaptureProjectGuidOf: Captured the GUID " + projectGuidNodes[0].InnerText + " for " + project.Name);
            
        

        private void StoreCapturedGuidForLaterUse(Project project, XmlNodeList projectGuidNodes) 
            if (project.Name == GetSolutionName() + ".ApplicationServices") 
                guidAssignedToApplicationServices = projectGuidNodes[0].InnerText;
            
            else if (project.Name == GetSolutionName() + ".Core") 
                guidAssignedToCore = projectGuidNodes[0].InnerText;
            
            else if (project.Name == GetSolutionName() + ".Web.Controllers") 
                guidAssignedToControllers = projectGuidNodes[0].InnerText;
            
            else if (project.Name == GetSolutionName() + ".Data") 
                guidAssignedToData = projectGuidNodes[0].InnerText;
            
        

        private bool IsProjectReferredByOtherProjects(Project project) 
            return project.Name == GetSolutionName() + ".ApplicationServices" ||
                project.Name == GetSolutionName() + ".Core" ||
                project.Name == GetSolutionName() + ".Web.Controllers" ||
                project.Name == GetSolutionName() + ".Data";
        

        /// <summary>
        /// Sets up the solution structure and performs a number of related initialization steps
        /// </summary>
        private void PerformSolutionInitialization(EnvDTE.Project project) 
            CreateSolutionDirectoryStructure();
            MoveCommonAssemblyInfoToRoot(project);
        

        /// <summary>
        /// Runs custom wizard logic when the wizard has completed all tasks
        /// </summary>
        void IWizard.RunFinished() 
            // Only copy the solution items once, right after processing the solution template
            if (runKind == WizardRunKind.AsMultiProject) 
                DeleteSuoFile();

                // Operations after this must take into account that the solution path has changed
                MoveSolutionFileToProjectsDirectory();
            
        

        private void ExcludeProjectFromBuildProcess(EnvDTE.Project project) 
            Solution2 solution = dte.Solution as Solution2;
            SolutionBuild2 solutionBuild = (SolutionBuild2)solution.SolutionBuild;

            foreach (SolutionConfiguration solutionConfiguration in solutionBuild.SolutionConfigurations) 
                foreach (SolutionContext solutionContext in solutionConfiguration.SolutionContexts) 
                    if (solutionContext.ProjectName.IndexOf(project.Name) > -1) 
                        Log("ExcludeProjectFromBuildProcess: Setting build to false for project " + solutionContext.ProjectName +
                            " within the " + solutionConfiguration.Name + " configuration");
                        solutionContext.ShouldBuild = false;
                    
                
            
        

        private Project MoveProjectTo(string targetSubFolder, EnvDTE.Project project) 
            return MoveProjectTo(targetSubFolder, project, null);
        

        private Project MoveProjectTo(string targetSubFolder, EnvDTE.Project project, string solutionFolderName) 
            string projectName = project.Name;
            string originalLocation = GetSolutionRootPath() + GetSolutionName() + "\\" + projectName;

            if (Directory.Exists(originalLocation)) 
                Solution2 solution = dte.Solution as Solution2;

                Log("MoveProjectTo: Removing " + projectName + " from solution");
                solution.Remove(project);

                // Give the solution time to release the lock on the project file
                System.Threading.Thread.Sleep(MIN_TIME_FOR_PROJECT_TO_RELEASE_FILE_LOCK);

                PerformManualProjectReplacementsTo(originalLocation + "\\" + projectName + ".csproj");

                string targetLocation = GetSolutionRootPath() + GetSolutionName() + targetSubFolder + projectName;

                Log("MoveProjectTo: Moving " + projectName + " from " + originalLocation + " to target location at " + targetLocation);
                Directory.Move(originalLocation, targetLocation);

                if (!string.IsNullOrEmpty(solutionFolderName)) 
                    SolutionFolder solutionFolder = (SolutionFolder)solution.AddSolutionFolder(solutionFolderName).Object;
                    Log("MoveProjectTo: Adding " + projectName + " to solution folder " + targetLocation);
                    return solutionFolder.AddFromFile(targetLocation + "\\" + projectName + ".csproj");
                
                else 
                    Log("MoveProjectTo: Adding " + projectName + " to solution");
                    return solution.AddFromFile(targetLocation + "\\" + projectName + ".csproj", false);
                
            
            else 
                throw new ApplicationException("Couldn't find " + originalLocation + " to move");
            
        

        /// <summary>
        /// This does any manual value replacement on project files when it can't be handled 
        /// (or is being handled incorrectly by the VS templating process.
        /// </summary>
        private void PerformManualProjectReplacementsTo(string projectFilePath) 
            if (File.Exists(projectFilePath)) 
                Log("PerformManualProjectReplacementsTo: Going to PerformManualProjectReplacementsTo on " + projectFilePath);

                // Open a file for reading
                StreamReader streamReader;
                streamReader = File.OpenText(projectFilePath);

                // Now, read the entire file into a string
                string contents = streamReader.ReadToEnd();
                streamReader.Close();

                // Write the modification into the same fil
                StreamWriter streamWriter = File.CreateText(projectFilePath);
                streamWriter.Write(contents.Replace("PLACE_HOLDER_COMMON_ASSEMLY_INFO_LOCATION", "..\\..\\CommonAssemblyInfo.cs"));
                streamWriter.Close();
            
            else 
                throw new ApplicationException("Couldn't find " + projectFilePath + " to PerformManualProjectReplacementsTo");
            
        

        private void MoveCommonAssemblyInfoToRoot(EnvDTE.Project solutionItemsContainerProject) 
            string originalFileLocation = GetSolutionRootPath() + GetSolutionName() + "\\SolutionItemsContainer\\CommonAssemblyInfo.cs";

            if (File.Exists(originalFileLocation)) 
                string targetFileLocation = GetSolutionRootPath() + GetSolutionName() + "\\CommonAssemblyInfo.cs";

                Log("MoveCommonAssemblyInfoToRoot: Moving CommonAssemblyInfo.cs from " + originalFileLocation + " to root at " + targetFileLocation);
                File.Move(originalFileLocation, targetFileLocation);
            
            else 
                throw new ApplicationException("Couldn't find CommonAssemblyInfo.cs to move");
            
        

        private void MoveSolutionItemsToLib(EnvDTE.Project solutionItemsContainerProject) 
            string originalLocation = GetSolutionRootPath() + GetSolutionName() + "\\SolutionItemsContainer\\Solution Items";

            if (Directory.Exists(originalLocation)) 
                string targetLibFolder = GetSolutionRootPath() + GetSolutionName() + "\\lib";

                Log("MoveSolutionItemsToLib: Moving solution items from " + originalLocation + " to lib at " + targetLibFolder);
                Directory.Move(originalLocation, targetLibFolder);

                Solution2 solution = dte.Solution as Solution2;
                solution.Remove(solutionItemsContainerProject);
                // Give the solution time to release the lock on the project file
                System.Threading.Thread.Sleep(500);

                Directory.Delete(GetSolutionRootPath() + GetSolutionName() + "\\SolutionItemsContainer", true);
            
            else 
                throw new ApplicationException("Couldn't find " + originalLocation + " to move");
            
        

        private void MoveSolutionItemsToToolsLib(EnvDTE.Project toolsSolutionItemsContainerProject) 
            string originalLocation = GetSolutionRootPath() + GetSolutionName() + "\\ToolsSolutionItemsContainer\\Solution Items";

            if (Directory.Exists(originalLocation)) 
                string targetToolsLibFolder = GetSolutionRootPath() + GetSolutionName() + "\\tools\\lib";

                Log("MoveSolutionItemsToToolsLib: Moving tools solution items from " + originalLocation + " to tools lib at " + targetToolsLibFolder);
                Directory.Move(originalLocation, targetToolsLibFolder);

                Solution2 solution = dte.Solution as Solution2;
                solution.Remove(toolsSolutionItemsContainerProject);
                // Give the solution time to release the lock on the project file
                System.Threading.Thread.Sleep(500);

                Directory.Delete(GetSolutionRootPath() + GetSolutionName() + "\\ToolsSolutionItemsContainer", true);
            
            else 
                throw new ApplicationException("Couldn't find " + originalLocation + " to move");
            
        

        /// <summary>
        /// Note that this is called BEFORE the SLN is moved to the solution folder; therefore, we have 
        /// to add the solution name after the root path.
        /// </summary>
        private void CreateSolutionDirectoryStructure() 
            Directory.CreateDirectory(GetSolutionRootPath() + GetSolutionName() + "\\app");
            Directory.CreateDirectory(GetSolutionRootPath() + GetSolutionName() + "\\build");
            Directory.CreateDirectory(GetSolutionRootPath() + GetSolutionName() + "\\db");
            Directory.CreateDirectory(GetSolutionRootPath() + GetSolutionName() + "\\docs");
            Directory.CreateDirectory(GetSolutionRootPath() + GetSolutionName() + "\\logs");
            Directory.CreateDirectory(GetSolutionRootPath() + GetSolutionName() + "\\tests");
            Directory.CreateDirectory(GetSolutionRootPath() + GetSolutionName() + "\\tools");
        

        private void MoveSolutionFileToProjectsDirectory() 
            dte.Solution.SaveAs(
                GetSolutionRootPath() + GetSolutionName() + "\\" + GetSolutionFileName());
        

        private void DeleteSuoFile() 
            string suoFile = GetSolutionRootPath() + GetSolutionName() + ".suo";

            if (File.Exists(suoFile)) 
                Log("DeleteSuoFile: Deleting " + suoFile);
                File.Delete(suoFile);
            
        

        private void Log(string message) 
            StreamWriter streamWriter = File.AppendText(GetSolutionRootPath() + GetSolutionName() + "\\logs\\" + LOG_FILE_NAME);
            streamWriter.WriteLine(DateTime.Now.ToLongTimeString() + "\t" + message);
            streamWriter.Close();
        

        private string GetSolutionName() 
            return replacementsDictionary["$solutionname$"];
        

        private string GetSolutionFileName() 
            return GetSolutionName() + ".sln";
        

        private string GetSolutionFileFullName() 
            return dte.Solution.Properties.Item("Path").Value.ToString();
        

        private string GetSolutionRootPath() 
            return GetSolutionFileFullName().Replace(GetSolutionFileName(), "");
        

        // This method is called before opening any item which is marked for opening in the editor in the 
        // .vstemplate file using the "OpenInEditor" attribute.
        void IWizard.BeforeOpeningFile(EnvDTE.ProjectItem projectItem) 

        

        // This method is only applicable for item templates and does not get called for project templates.
        void IWizard.ProjectItemFinishedGenerating(EnvDTE.ProjectItem projectItem) 
        

        // This method is only applicable for item templates and does not get called for project templates.
        bool IWizard.ShouldAddProjectItem(string filePath) 
            return true;
        

        private const string LOG_FILE_NAME = "SharpArch.VSharpArchTemplate.log";
    

【讨论】:

【参考方案2】:

$ext_safeprojectname$ 也可用于获取基于解决方案而非当前项目的命名空间。

【讨论】:

Visual Studio 多项目模板重命名命名空间和项目名称

】VisualStudio多项目模板重命名命名空间和项目名称【英文标题】:VisualStudiomulti-projecttemplatesrenamenamespacesandprojectnames【发布时间】:2011-07-1511:56:45【问题描述】:我已经成功创建了一个多项目解决方案模板。然而,我想做的是用... 查看详情

如何在 Visual Studio 2013 中创建多项目模板?

】如何在VisualStudio2013中创建多项目模板?【英文标题】:Howtocreateamultiprojecttemplateinvisualstudio2013?【发布时间】:2015-02-0704:57:22【问题描述】:我按照Howto:CreateMulti-ProjectTemplates文章创建了一个模板,该模板将生成一个包含4个项目... 查看详情

在 Visual Studio 项目模板中创建解决方案文件夹

】在VisualStudio项目模板中创建解决方案文件夹【英文标题】:CreateaSolutionFolderinaVisualStudioProjectTemplate【发布时间】:2014-06-1507:52:43【问题描述】:我按照以下说明创建了一个多项目VisualStudio模板:http://msdn.microsoft.com/en-us/library/ms... 查看详情

Visual Studio 项目模板多个项目类型

】VisualStudio项目模板多个项目类型【英文标题】:VisualStudioprojecttemplatemultipleProjectTypes【发布时间】:2009-04-2800:34:05【问题描述】:我有一个项目模板,我希望它出现在“VisualC#”及其子类型“Test”下。我可以让它出现在一个而... 查看详情

如何创建 Visual Studio 2008 C++ 项目模板?

】如何创建VisualStudio2008C++项目模板?【英文标题】:HowtocreateaVisualStudio2008C++projecttemplate?【发布时间】:2009-11-1113:34:32【问题描述】:我在C#、ASP.NET、WinForms等项目中多次使用“导出模板”功能。今天我尝试为一个C++项目做这件... 查看详情

Visual Studio 2019 和 IdentityServer 项目模板

】VisualStudio2019和IdentityServer项目模板【英文标题】:Visualstudio2019andIdentityServerprojecttemplate【发布时间】:2019-10-1505:45:34【问题描述】:尝试遵循使用VisualStudio2019的IdentityServer教程。按照说明创建一个.NetCoreWeb应用程序,然后创建... 查看详情

Visual Studio 2010 项目模板不显示

】VisualStudio2010项目模板不显示【英文标题】:VisualStudio2010ProjectTemplatedoesn\'tshowup【发布时间】:2011-04-2521:51:57【问题描述】:我尝试在从vs2010导出项目模板后对其进行编辑,方法是提取它并在编辑后再次压缩它。但之后它不会... 查看详情

使用命令行创建新的 Visual Studio 模板项目

】使用命令行创建新的VisualStudio模板项目【英文标题】:CreatingANewVisualStudioTemplateProjectUsingTheCommandLine【发布时间】:2018-01-1712:16:06【问题描述】:我正在开展一个项目,该项目需要我们在一些RaspberryPi上远程构建。我们使用的是... 查看详情

Visual Studio 2013 中的默认模板项目崩溃

】VisualStudio2013中的默认模板项目崩溃【英文标题】:DefaultTemplateProjectsCrashinginVisualStudio2013【发布时间】:2015-01-0914:32:09【问题描述】:是我做错了什么还是VisualStudio2013默认项目损坏了?创建一个新的基本MVC项目:File->New->P... 查看详情

Visual Studio 2019 MVC 项目模板无法编译

】VisualStudio2019MVC项目模板无法编译【英文标题】:VisualStudio2019MVCprojecttemplatedoesn\'tcompile【发布时间】:2019-08-1216:29:41【问题描述】:我正在使用VisualStudio2019使用.NETFramework创建一个股票MVC项目。我引入了一些具有.NETFramework依赖... 查看详情

NUnit 是不是有 Visual Studio 测试项目模板?

】NUnit是不是有VisualStudio测试项目模板?【英文标题】:IsthereaVisualStudioTestProjecttemplateforNUnit?NUnit是否有VisualStudio测试项目模板?【发布时间】:2011-01-2711:30:54【问题描述】:是否有NUnit的官方VisualStudio测试项目模板?如果不是,... 查看详情

如何创建包含链接文件的 Visual Studio 项目模板?

】如何创建包含链接文件的VisualStudio项目模板?【英文标题】:HowdoIcreateaVisualStudioprojecttemplatethatincludeslinkedfiles?【发布时间】:2011-09-0123:28:17【问题描述】:在VisualStudio2010中,我想创建一个项目模板,其中包含指向系统上应该... 查看详情

如何在 Visual Studio 2010 中删除/卸载项目模板

】如何在VisualStudio2010中删除/卸载项目模板【英文标题】:Howtoremove/uninstallitemtemplatesinVisualStudio2010【发布时间】:2012-01-0507:24:42【问题描述】:我有一个项目模板,我做错了,想删除。我从输出位置删除了zip文件并运行了devenv/in... 查看详情

如何为 SQL 文件、Visual Studio 数据库项目创建项目模板

】如何为SQL文件、VisualStudio数据库项目创建项目模板【英文标题】:HowtocreateitemtemplatesforSQLfiles,forVisualStudioDatabaseProjects【发布时间】:2010-05-0501:54:05【问题描述】:可以为普通项目类型定义您自己的自定义模板,例如用于类库... 查看详情

Visual Studio 2013/2015 测试项目模板 - 用于 NUnit?

】VisualStudio2013/2015测试项目模板-用于NUnit?【英文标题】:VisualStudio2013/2015TestProjectTemplate-forNUnit?【发布时间】:2015-10-1611:00:28【问题描述】:我是单元测试新手,从MSTest开始,想使用NUnit。我看到VS2015中有一个测试项目模板,... 查看详情

如何在 Visual Studio 2015 中成功导出 C++ 项目模板?

】如何在VisualStudio2015中成功导出C++项目模板?【英文标题】:HowtosuccessfullyexportC++projecttemplateinVisualStudio2015?【发布时间】:2016-12-2001:47:26【问题描述】:我完成了在VisualStudio2015中将VisualC++项目导出为模板的步骤。它只包含一个... 查看详情

Visual Studio 2013 Ultimate - 缺少 C++ 项目模板

】VisualStudio2013Ultimate-缺少C++项目模板【英文标题】:VisualStudio2013Ultimate-MissingC++ProjectsTemplates【发布时间】:2015-06-1715:15:58【问题描述】:我安装了我的MicrosoftVisualStudioUltimate2013的全新安装并启动了它。然后我去了FILE-&gt;New-&a... 查看详情

Visual Studio 2017 c++ win32 控制台项目模板

】VisualStudio2017c++win32控制台项目模板【英文标题】:VisualStudio2017c++win32consoleprojecttemplate【发布时间】:2017-08-2018:56:47【问题描述】:我在VisualStudioCommunity2017v.15.3.1上,我似乎找不到Win32控制台应用程序或Win32项目。仍然有空的c++... 查看详情