我们编写项目的时候,很多时候都是在写重复代码,比如一个比较完整的框架,然后下面有很多代码都是重复的Copy,其实我们可以利用Visual Studio的模板替我们干这些活,我们只要关注项目具体的业务就可以了;
下面我们开始:

项目模板制作(一)(此处为原贴链接,下同)

1、模板类别

项目模板、项模板
其中,项目模板是创建项目用的,项模板是创建项用的
项目模板:
C#-项目模板-Visual Studio 项目模板及VSIX的制作 - 图1

项模板:
C#-项目模板-Visual Studio 项目模板及VSIX的制作 - 图2

2、创建项目模板

首先,将要制作成模板的项目打开,选中项目,点击文件-》导出项目模板,弹出导出模板向导
C#-项目模板-Visual Studio 项目模板及VSIX的制作 - 图3
然后填写相关信息,点击完成,这样就导出模板成功了
现在,很关键的一步就是修改导出的模板
解压刚才导出zip文件
打开.vstemplate文件

  1. <VSTemplate Version="3.0.0" xmlns="http://schemas.microsoft.com/developer/vstemplate/2005" Type="Project">
  2. <TemplateData>
  3. <Name>ConsoleApplication2</Name>
  4. <Description><没有可用的说明></Description>
  5. <ProjectType>CSharp</ProjectType>
  6. <ProjectSubType>
  7. </ProjectSubType>
  8. <SortOrder>1000</SortOrder>
  9. <CreateNewFolder>true</CreateNewFolder>
  10. <DefaultName>ConsoleApplication2</DefaultName>
  11. <ProvideDefaultName>true</ProvideDefaultName>
  12. <LocationField>Enabled</LocationField>
  13. <EnableLocationBrowseButton>true</EnableLocationBrowseButton>
  14. <Icon>__TemplateIcon.ico</Icon>
  15. </TemplateData>
  16. <TemplateContent>
  17. <Project TargetFileName="ConsoleApplication2.csproj" File="ConsoleApplication2.csproj" ReplaceParameters="true">
  18. <ProjectItem ReplaceParameters="true" TargetFileName="CodeTimer.cs">CodeTimer.cs</ProjectItem>
  19. <ProjectItem ReplaceParameters="true" TargetFileName="Program.cs">Program.cs</ProjectItem>
  20. <Folder Name="Properties" TargetFolderName="Properties">
  21. <ProjectItem ReplaceParameters="true" TargetFileName="AssemblyInfo.cs">AssemblyInfo.cs</ProjectItem>
  22. </Folder>
  23. </Project>
  24. </TemplateContent>
  25. </VSTemplate>

TemplateData节点是模板的说明,Name:模板名称,Description:模板描述,ProjectType:项目类型,

修改TemplateContext节点
TargetFileName:模板生成的文件名称 (如:TargetFileName=”$safeprojectname$.csproj”)
File:模板文件
ReplaceParameters:是否替换占位符,微软定义的占位符见附录,当然还可以自定义占位符

修改完成之后

  1. <VSTemplate Version="3.0.0" xmlns="http://schemas.microsoft.com/developer/vstemplate/2005" Type="Project">
  2. <TemplateData>
  3. <Name>ConsoleApplication2</Name>
  4. <Description><没有可用的说明></Description>
  5. <ProjectType>CSharp</ProjectType>
  6. <ProjectSubType>
  7. </ProjectSubType>
  8. <SortOrder>1000</SortOrder>
  9. <CreateNewFolder>true</CreateNewFolder>
  10. <DefaultName>ConsoleApplication2</DefaultName>
  11. <ProvideDefaultName>true</ProvideDefaultName>
  12. <LocationField>Enabled</LocationField>
  13. <EnableLocationBrowseButton>true</EnableLocationBrowseButton>
  14. <Icon>__TemplateIcon.ico</Icon>
  15. </TemplateData>
  16. <TemplateContent>
  17. <Project TargetFileName="$safeprojectname$.csproj" File="ConsoleApplication2.csproj" ReplaceParameters="true">
  18. <ProjectItem ReplaceParameters="true" TargetFileName="$safeprojectname$CodeTimer.cs">CodeTimer.cs</ProjectItem>
  19. <ProjectItem ReplaceParameters="true" TargetFileName="Program.cs">Program.cs</ProjectItem>
  20. <Folder Name="Properties" TargetFolderName="Properties">
  21. <ProjectItem ReplaceParameters="true" TargetFileName="AssemblyInfo.cs">AssemblyInfo.cs</ProjectItem>
  22. </Folder>
  23. </Project>
  24. </TemplateContent>
  25. </VSTemplate>

打开用记事本csproj项目文件
修改ItemGroup

  1. <ItemGroup>
  2. <Compile Include="$safeprojectname$CodeTimer.cs" />
  3. <Compile Include="Program.cs" />
  4. <Compile Include="Properties\AssemblyInfo.cs" />
  5. </ItemGroup>

修改完成之后压缩成Zip文件,放到下面的目录,在vs里面就可以使用刚才创建的模板了

  1. %USERPROFILE%\Documents\Visual Studio 2019\Templates\ProjectTemplates

C#-项目模板-Visual Studio 项目模板及VSIX的制作 - 图4

附录:

参数 描述
clrversion 公共语言运行时 (CLR) 的当前版本。
GUID [1-10] 用于替换项目文件中的项目 GUID 的 GUID。 最多可以指定 10 个唯一的 GUID(例如,guid1))。
itemname 用户在添加新项对话框中提供的名称。
machinename 当前的计算机名称(例如,Computer01)。
projectname 用户在新建项目对话框中提供的名称。
registeredorganization HKLM\Software\Microsoft\Windows NT\CurrentVersion\RegisteredOrganization 中的注册表项值。
rootnamespace 当前项目的根命名空间。 此参数仅适用于项目模板。
safeitemname 用户在“添加新项”对话框中提供的名称,名称中移除了所有不安全的字符和空格。
safeprojectname 用户在“新建项目”对话框中提供的名称,名称中移除了所有不安全的字符和空格。
time 以 DD/MM/YYYY 00:00:00 格式表示的当前时间。
SpecificSolutionName 解决方案的名称。 当“创建解决方案的目录”被选中,SpecificSolutionName 具有解决方案的名称。当“创建解决方案的目录”没有被选中,SpecificSolutionName是空。
userdomain 当前的用户域。
username 当前的用户名。
webnamespace 当前网站的名称。 在 Web 窗体模板中使用此参数以确保类名称是唯一的。 如果网站位于 Web 服务器的根目录下,则此模板参数将解析为 Web 服务器的根目录。
year 以 YYYY 格式表示的当前年份。

项目模板制作(二)

上面,,我们制作了项目模板,本篇我制作项模板
首先,从我们需要导出模板的项目中,文件->导出模板,弹出 导出模板向导 对话框
C#-项目模板-Visual Studio 项目模板及VSIX的制作 - 图5
选择项模板,点击下一步
C#-项目模板-Visual Studio 项目模板及VSIX的制作 - 图6
选择要导出的项,点击下一步
C#-项目模板-Visual Studio 项目模板及VSIX的制作 - 图7

选择要Reference的类库
C#-项目模板-Visual Studio 项目模板及VSIX的制作 - 图8

修改模板名称,点击完成
然后,解压生成的zip文件,如图
C#-项目模板-Visual Studio 项目模板及VSIX的制作 - 图9
打开.vstemplate文件

  1. <VSTemplate Version="3.0.0" xmlns="http://schemas.microsoft.com/developer/vstemplate/2005" Type="Item">
  2. <TemplateData>
  3. <DefaultName>CodeTimerTemplate.cs</DefaultName>
  4. <Name>CodeTimerTemplate</Name>
  5. <Description><没有可用的说明></Description>
  6. <ProjectType>CSharp</ProjectType>
  7. <SortOrder>10</SortOrder>
  8. <Icon>__TemplateIcon.ico</Icon>
  9. </TemplateData>
  10. <TemplateContent>
  11. <References>
  12. <Reference>
  13. <Assembly>System</Assembly>
  14. </Reference>
  15. </References>
  16. <ProjectItem SubType="Code" TargetFileName="$fileinputname$.cs" ReplaceParameters="true">CodeTimer.cs</ProjectItem>
  17. </TemplateContent>
  18. </VSTemplate>

修改TemplateData,修改模板的相关信息
修改 TemplateContent,将TargetFileName改成自己想要的

打开项文件CodeTimer.cs,做一些自己的修改

  1. using System;
  2. using System.Diagnostics;
  3. using System.Runtime.InteropServices;
  4. using System.Threading;
  5. namespace $rootnamespace$
  6. {
  7. public static class $safeitemname$
  8. {
  9. public static void Initialize()
  10. }
  11. }

最后压缩成zip文件放到

  1. %USERPROFILE%\Documents\Visual Studio 2019\Templates\ProjectTemplates

然后在Vs里面的新建项就可以看到自己的项模板了
C#-项目模板-Visual Studio 项目模板及VSIX的制作 - 图10

项目模板制作(三)

前面,我们已经制作好了模板,然后放到相应的Template目录就可以在Visual Studio中使用
现在,我们采用安装VSIX扩展的方式来安装模板,这种方式需要安装Visual Studio SDK
安装了SDK之后,可以在新建项目里面看到VSIX Project
C#-项目模板-Visual Studio 项目模板及VSIX的制作 - 图11
选择VSIX Project 然后设置一下名称,点击确定,项目就新建完成了
项目结构:
C#-项目模板-Visual Studio 项目模板及VSIX的制作 - 图12

现在我们开始
首先,双击打开source.extension.vsixmanifest
C#-项目模板-Visual Studio 项目模板及VSIX的制作 - 图13
设置扩展的各种属性
然后,添加我们前面两篇制作的模板
C#-项目模板-Visual Studio 项目模板及VSIX的制作 - 图14
点击新建
C#-项目模板-Visual Studio 项目模板及VSIX的制作 - 图15
添加项模板
C#-项目模板-Visual Studio 项目模板及VSIX的制作 - 图16
添加项目模板
C#-项目模板-Visual Studio 项目模板及VSIX的制作 - 图17

添加完成之后
C#-项目模板-Visual Studio 项目模板及VSIX的制作 - 图18
现在解决方案管理器里面可以看到新增的模板
C#-项目模板-Visual Studio 项目模板及VSIX的制作 - 图19

注意:
我们的模板如何放在一个我们自己定义的组里面呢,如图
C#-项目模板-Visual Studio 项目模板及VSIX的制作 - 图20
操作:
在ItemTemplate和ProjectTemplate中新建文件夹取一个自己的名字,然后把模板拖进去就行了
C#-项目模板-Visual Studio 项目模板及VSIX的制作 - 图21

下面,我们设置安装目标,就是我们这个VSIX可以安装到哪几个版本的VS中
C#-项目模板-Visual Studio 项目模板及VSIX的制作 - 图22
图中 14代表Visual Studio的版本号 14对应VS2015
我要让VS2013也可以用,这样既可[12.0.14.0]
VS2013以上都可以用:[12.0,)
C#-项目模板-Visual Studio 项目模板及VSIX的制作 - 图23

最后,我们生成项目,打开Debug目录,双击运行VSIX扩展
C#-项目模板-Visual Studio 项目模板及VSIX的制作 - 图24
就可以安装我们制作的模板了
新建项目里面:
C#-项目模板-Visual Studio 项目模板及VSIX的制作 - 图25
新建项:
C#-项目模板-Visual Studio 项目模板及VSIX的制作 - 图26

注意:
这种方式安装的模板需要重启Visual Studio,模板才会显示

卸载模板:
C#-项目模板-Visual Studio 项目模板及VSIX的制作 - 图27
C#-项目模板-Visual Studio 项目模板及VSIX的制作 - 图28
点击卸载,重启VS就行了

项目模板制作(四)

上一篇,介绍了VSIX安装模板的方法,那么,你是不是要问,为何有些项目模板却可以有向导,那是怎么做到的
今天这篇文章就是介绍如何为自己的模板添加向导,向导可以引导你完成项目中各种参数的设置,比如项目创建人,项目描述,公司等
下图创建Web项目时的向导
C#-项目模板-Visual Studio 项目模板及VSIX的制作 - 图29
下面我们开始制作我们自己的项目向导
需要用到的两个类库: envdte , Microsoft.VisualStudio.TemplateWizardInterface
创建一个类库项目,引用上面两个类库
添加一个类,取名ProjectWizard继承IWizard接口实现 RunStarted 方法

  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Text;
  5. using System.Threading.Tasks;
  6. using System.Windows.Forms;
  7. using EnvDTE;
  8. using Microsoft.VisualStudio.TemplateWizard;
  9. namespace TemplateTestWizard
  10. {
  11. public class ProjectWizard:IWizard
  12. {
  13. private bool shouldAdd = true;
  14. public string Creator = "";
  15. public void RunStarted(object automationObject, Dictionary<string, string> replacementsDictionary, WizardRunKind runKind, object[] customParams)
  16. {
  17. Creator = replacementsDictionary["$username$"];
  18. AssemblyWizard wizard=new AssemblyWizard(this);
  19. if (wizard.ShowDialog() == DialogResult.OK)
  20. {
  21. replacementsDictionary["$username$"]=Creator;
  22. shouldAdd = true;
  23. }
  24. else
  25. {
  26. shouldAdd = false;
  27. }
  28. }
  29. public void ProjectFinishedGenerating(Project project)
  30. {
  31. }
  32. public void ProjectItemFinishedGenerating(ProjectItem projectItem)
  33. {
  34. }
  35. public bool ShouldAddProjectItem(string filePath)
  36. {
  37. return shouldAdd;
  38. }
  39. public void BeforeOpeningFile(ProjectItem projectItem)
  40. {
  41. }
  42. public void RunFinished()
  43. {
  44. }
  45. }
  46. }

下面为项目添加签名,由于制作的类库需要放到全局应用缓存中去,必须要添加签名
C#-项目模板-Visual Studio 项目模板及VSIX的制作 - 图30
然后,打开Visual Studio命令提示符,以管理员运行,用 gacutil -i 注册生成的dll

  1. gacutil -i path 注册程序集到全局应用程序缓存中
  2. gacutil -l name 查看相应的程序集
  3. gacutil -u name 卸载相应的程序集

下面,修改前面制作的Template,解压Template文件,打开 .vstemplate 文件,添加如下代码

  1. <WizardExtension>
  2. <Assembly>
  3. TemplateTestWizard, Version=1.0.0.0, Culture=neutral, PublicKeyToken=2be8376f02202422, processorArchitecture=MSIL
  4. </Assembly>
  5. <FullClassName>TemplateTestWizard.ProjectWizard</FullClassName>
  6. </WizardExtension>

其中 Assembly 节点写的是内容可以用 gacutil -l name 查看,然后复制出来
FullClassName 是项目中ProjectWizard的完全限定名称
修改完模板后,在VS中打开新建项目
C#-项目模板-Visual Studio 项目模板及VSIX的制作 - 图31

C#-项目模板-Visual Studio 项目模板及VSIX的制作 - 图32
这个自定义向导就出来了,
如果模板里面有 $username$

  1. using System;
  2. using System.Diagnostics;
  3. using System.Runtime.InteropServices;
  4. using System.Threading;
  5. namespace $safeprojectname$
  6. {
  7. /// <summary>
  8. /// 创建人:$username$
  9. /// </summary>
  10. public static class CodeTimer
  11. {
  12. public static void Initialize()
  13. {
  14. Process.GetCurrentProcess().PriorityClass = ProcessPriorityClass.High;
  15. Thread.CurrentThread.Priority = ThreadPriority.Highest;
  16. Time("", 1, () => { });
  17. }
  18. public static void Time(string name, int iteration, Action action)
  19. {
  20. if (String.IsNullOrEmpty(name)) return;
  21. // 1.
  22. ConsoleColor currentForeColor = Console.ForegroundColor;
  23. Console.ForegroundColor = ConsoleColor.Yellow;
  24. Console.WriteLine(name);
  25. // 2.
  26. GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced);
  27. int[] gcCounts = new int[GC.MaxGeneration + 1];
  28. for (int i = 0; i <= GC.MaxGeneration; i++)
  29. {
  30. gcCounts[i] = GC.CollectionCount(i);
  31. }
  32. // 3.
  33. Stopwatch watch = new Stopwatch();
  34. watch.Start();
  35. ulong cycleCount = GetCycleCount();
  36. for (int i = 0; i < iteration; i++) action();
  37. ulong cpuCycles = GetCycleCount() - cycleCount;
  38. watch.Stop();
  39. // 4.
  40. Console.ForegroundColor = currentForeColor;
  41. Console.WriteLine("\tTime Elapsed:\t" + watch.ElapsedMilliseconds.ToString("N0") + "ms");
  42. Console.WriteLine("\tCPU Cycles:\t" + cpuCycles.ToString("N0"));
  43. // 5.
  44. for (int i = 0; i <= GC.MaxGeneration; i++)
  45. {
  46. int count = GC.CollectionCount(i) - gcCounts[i];
  47. Console.WriteLine("\tGen " + i + ": \t\t" + count);
  48. }
  49. Console.WriteLine();
  50. }
  51. private static ulong GetCycleCount()
  52. {
  53. ulong cycleCount = 0;
  54. QueryThreadCycleTime(GetCurrentThread(), ref cycleCount);
  55. return cycleCount;
  56. }
  57. [DllImport("kernel32.dll")]
  58. [return: MarshalAs(UnmanagedType.Bool)]
  59. static extern bool QueryThreadCycleTime(IntPtr threadHandle, ref ulong cycleTime);
  60. [DllImport("kernel32.dll")]
  61. static extern IntPtr GetCurrentThread();
  62. }
  63. }

生成之后代码

  1. using System;
  2. using System.Diagnostics;
  3. using System.Runtime.InteropServices;
  4. using System.Threading;
  5. namespace ConsoleApplication25
  6. {
  7. /// <summary>
  8. /// 创建人:Administrator
  9. /// </summary>
  10. public static class CodeTimer
  11. {
  12. public static void Initialize()
  13. {
  14. Process.GetCurrentProcess().PriorityClass = ProcessPriorityClass.High;
  15. Thread.CurrentThread.Priority = ThreadPriority.Highest;
  16. Time("", 1, () => { });
  17. }
  18. public static void Time(string name, int iteration, Action action)
  19. {
  20. if (String.IsNullOrEmpty(name)) return;
  21. // 1.
  22. ConsoleColor currentForeColor = Console.ForegroundColor;
  23. Console.ForegroundColor = ConsoleColor.Yellow;
  24. Console.WriteLine(name);
  25. // 2.
  26. GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced);
  27. int[] gcCounts = new int[GC.MaxGeneration + 1];
  28. for (int i = 0; i <= GC.MaxGeneration; i++)
  29. {
  30. gcCounts[i] = GC.CollectionCount(i);
  31. }
  32. // 3.
  33. Stopwatch watch = new Stopwatch();
  34. watch.Start();
  35. ulong cycleCount = GetCycleCount();
  36. for (int i = 0; i < iteration; i++) action();
  37. ulong cpuCycles = GetCycleCount() - cycleCount;
  38. watch.Stop();
  39. // 4.
  40. Console.ForegroundColor = currentForeColor;
  41. Console.WriteLine("\tTime Elapsed:\t" + watch.ElapsedMilliseconds.ToString("N0") + "ms");
  42. Console.WriteLine("\tCPU Cycles:\t" + cpuCycles.ToString("N0"));
  43. // 5.
  44. for (int i = 0; i <= GC.MaxGeneration; i++)
  45. {
  46. int count = GC.CollectionCount(i) - gcCounts[i];
  47. Console.WriteLine("\tGen " + i + ": \t\t" + count);
  48. }
  49. Console.WriteLine();
  50. }
  51. private static ulong GetCycleCount()
  52. {
  53. ulong cycleCount = 0;
  54. QueryThreadCycleTime(GetCurrentThread(), ref cycleCount);
  55. return cycleCount;
  56. }
  57. [DllImport("kernel32.dll")]
  58. [return: MarshalAs(UnmanagedType.Bool)]
  59. static extern bool QueryThreadCycleTime(IntPtr threadHandle, ref ulong cycleTime);
  60. [DllImport("kernel32.dll")]
  61. static extern IntPtr GetCurrentThread();
  62. }
  63. }