Visual Studio项目模板与向导开发

在【Xamarin+Prism开发详解系列】里面经常使用到【Prism unity app】的模板创建Prism.Forms项目:

备注:由于Unity社区已经不怎么活跃,下一个版本将会有Autofac,DryIOC,Ninject的项目模板。

Visual Studio项目模板与向导开发_第1张图片

自动弹出选择框:

Visual Studio项目模板与向导开发_第2张图片

对于我这类还没有动手写过模板的人来说,确实挺新奇的。于是就决定自己也写个类似的试试,目的就是通过向导创建跨平台Plugin项目,类似Plugin for xamarin,不过是针对Prism,对应平台可以自由选择创建。试了之后才发现也有不少注意的地方,特写下此文做备忘。

项目地址:https://github.com/NewBLife/Prism.Forms.Plugin

插件下载地址:TemplatesForPrism.vsix

1、安装插件开发用的Extensibility模板与工具

Visual Studio项目模板与向导开发_第3张图片

2、新建VSIX Project插件项目

source.extension.vsixmanifest 这个文件相当重要,里面可以指定安装目标,模板,向导等。

最后我实现的例子:

安装目标:VS2013以上(2017估计不行)

Visual Studio项目模板与向导开发_第4张图片

资产:Project模板,Item模板,Wizard向导

Visual Studio项目模板与向导开发_第5张图片

3、从【C# Item Template】与【C# Project Template】模板创建项目。

4、从【类库】模板创建Wizard项目。(Wizard向导只能是类库)

以上步骤之后的项目结构:

Visual Studio项目模板与向导开发_第6张图片

介绍:

  • Prism.Forms.Plugin.Nuspec:Plugin打包文件模板
  • Prism.Forms.Plugin:Plugin项目模板
  • Prism.Forms.Plugin.Wizard:Plugin创建向导
  • TemplatesForPrism:VSIX插件项目

 

 

 

 

5、添加引用

  • Prism.Forms.Plugin.Nuspec:Microsoft.VisaulStudio.CoreUtility
  • Prism.Forms.Plugin:Microsoft.VisaulStudio.CoreUtility
  • Prism.Forms.Plugin.WizardMicrosoft.VisaulStudio.TemplateWizardinterface,envdte
  • TemplatesForPrism:Prism.Forms.Plugin.Nuspec,Prism.Forms.Plugin,Prism.Forms.Plugin.Wizard

6、添加生成向导

6.1、NewProjectWizard项目选择向导创建新建一个WinForm选择框,返回选择的结果。

Visual Studio项目模板与向导开发_第7张图片

继承IWiazrd向导接口实现:

using EnvDTE;
using Microsoft.VisualStudio.TemplateWizard;
using System;
using System.Collections.Generic;
using System.IO;

namespace Prism.Forms.Plugin.Wizard
{
    public class NewProjectWizard : IWizard
    {
        private DTE _dte = null;

        private string _solutionDir = null;

        private string _templateDir = null;

        private string _projectName = null;

        PluginNewProjectDialogResult _dialogResult;

        public void BeforeOpeningFile(ProjectItem projectItem)
        {
        }

        public void ProjectFinishedGenerating(Project project)
        {
if
(_dialogResult.CreateAndroid) CreateProject(Target.Droid.ToString()); if (_dialogResult.CreateiOS) CreateProject(Target.iOS.ToString()); if
 (_dialogResult.CreateUwp)
                CreateProject(Target.UWP.ToString());

        }

void CreateProject(string platform) { string name = $"{_projectName}.{platform}" ; string projectPath = System.IO.Path.Combine(_solutionDir, Path.Combine(_projectName, name)); string templateName = $"Prism.Forms.Plugin.{platform}" ; string templatePath = System.IO.Path.Combine(System.IO.Path.GetDirectoryName(_templateDir), $"{templateName}.zip\\{templateName}.vstemplate"
);

            _dte.Solution.AddFromTemplate(templatePath, projectPath, name);

        }

        public void ProjectItemFinishedGenerating(ProjectItem projectItem)
        {
        }

        public void RunFinished()
        {
        }

        public void RunStarted(object automationObject, Dictionary<string, string> replacementsDictionary, WizardRunKind runKind, object[] customParams)
        {
            try
            {
                _dte = automationObject as DTE;

                _projectName = replacementsDictionary["$safeprojectname$"];

                _solutionDir = System.IO.Path.GetDirectoryName(replacementsDictionary["$destinationdirectory$"]);

                _templateDir = System.IO.Path.GetDirectoryName(customParams[0] as string);

                PluginNewProjectDialog dialog 
= new PluginNewProjectDialog(); dialog.ShowDialog(); _dialogResult =
 dialog.Result;


                if (_dialogResult.Cancelled)

                    throw new WizardBackoutException();

            }
            catch (Exception)
            {
                if (Directory.Exists(_solutionDir))

                    Directory.Delete(_solutionDir, true);
                throw;
            }
        }

        public bool ShouldAddProjectItem(string filePath)
        {
            return true;
        }
    }
}

模板选择后执行RunStarted==》弹出Winform选择框==》执行ProjectFinishedGenerating创建子项目

 

6.2、项目名称safeprojectgroupname向导创建

在NewProjectWizard向导的CreateProject方法里面我们设置了每个子项目的名称为"{_projectName}.{platform}",所以到达子项目的时候$safeprojectname$里面已经带了iOS,Droid,UWP等名称,所以有必要进行处理。

using EnvDTE;
using Microsoft.VisualStudio.TemplateWizard;
using System.Collections.Generic;
using System.Threading;

namespace Prism.Forms.Plugin.Wizard
{
    public class SafeProjectGroupName : IWizard
    {
        public const string ParameterName = "$safeprojectgroupname$";

        static ThreadLocal<string> projectGroupName = new ThreadLocal<string>();
        bool isRootWizard;

        public void BeforeOpeningFile(ProjectItem projectItem)
        {
        }

        public void ProjectFinishedGenerating(Project project)
        {
        }

        public void ProjectItemFinishedGenerating(ProjectItem projectItem)
        {
        }

        public void RunFinished()
        {
            // If we were the ones to set the value, we must clear it.
            if (isRootWizard)
                projectGroupName.Value = null;
        }

        public void RunStarted(object automationObject, Dictionary<string, string> replacementsDictionary, WizardRunKind runKind, object[] customParams)
        {
            if (projectGroupName.Value == null)
            {
                var name = replacementsDictionary["$safeprojectname$"];
                if (name.EndsWith(Target.Abstractions.ToString()))
                {
                    projectGroupName.Value = name.Replace($".{Target.Abstractions.ToString()}", string.Empty);
                }
                else if (name.EndsWith(Target.Droid.ToString()))
                {
                    projectGroupName.Value = name.Replace($".{Target.Droid.ToString()}", string.Empty);
                }
                else if (name.EndsWith(Target.iOS.ToString()))
                {
                    projectGroupName.Value = name.Replace($".{Target.iOS.ToString()}", string.Empty);
                }
                else if (name.EndsWith(Target.UWP.ToString()))
                {
                    projectGroupName.Value = name.Replace($".{Target.UWP.ToString()}", string.Empty);
                }
                // If there wasn't a value already, it means we're the root wizard itself.
                isRootWizard = true;
            }

            replacementsDictionary[ParameterName] = projectGroupName.Value;
        }

        public bool ShouldAddProjectItem(string filePath)
        {
            return true;
        }
    }
}

Debug测试方法:

  • 给类库添加署名,否则无法注册
  • 管理员权限启动VS2015 开发人员命令提示工具
  • 导航到wizard生成目录
  • 执行gacutil -if Prism.Forms.Plugin.Wizard.dll 命令注册类库
  • 执行gacutil –l  列出类库的Token信息(Prism.Forms.Plugin.Wizard, Version=1.0.0.0, Culture=neutral, PublicKeyToken=b2335488e79a16da, processorArchitecture=MSIL)
  • 模板文件的WizarExtension节点添加以上Token信息与入口类名称
  • 安装模板,设置断点
  • 启动另一个Visual Studio程序
  • 调试-》附加到进程-》附加新启动的Visual Studio进程
  • 新Visual Studio窗口选择建立的模板,将进入断点

7、添加模板文件并设置

星星Prism.Forms.Plugin.Nuspec模板设置

"1.0" encoding="utf-8"?>
"3.0.0" Type="Item" xmlns="http://schemas.microsoft.com/developer/vstemplate/2005" xmlns:sdk="http://schemas.microsoft.com/developer/vstemplate-sdkextension/2010">
  
    Plugin nuspec for Prism.Forms
    Create a nuspec file for Prism.Forms.Plugin solution
    Prism.Forms.Plugin.Nuspec.ico
    325a0391-d11c-4432-8658-b70405881e87
    
General
    2.0
    1
    PrismFormsPlugin.nuspec
  
  
    "$fileinputname$.nuspec" ReplaceParameters="true">Prism.Forms.Plugin.nuspec
  

注意点:

  • Item模板的ProjectType默认为CSharp,有时候没法找到,最好设置为General。
  • 模板参照文件的生成操作都设置为无。
  • 记得设置分类Category,这样好找。

Visual Studio项目模板与向导开发_第8张图片

效果如下:(在添加新项里面)

Visual Studio项目模板与向导开发_第9张图片

星星Prism.Forms.Plugin模板设置

Visual Studio项目模板与向导开发_第10张图片

每个文件夹下面为相应的模板文件

  • Prism.Forms.Plugin.Abstractions:接口定义
  • Prism.Forms.Plugin.Droid:Android平台特性的接口实现
  • Prism.Forms.Plugin.iOS:iOS平台特性的接口实现
  • Prism.Forms.Plugin.UWP:UWP平台特性的接口实现

最外面的入口模板文件Prism.Forms.Plugin.vstemplate设置:

"1.0" encoding="utf-8"?>
"3.0.0" 
Type="ProjectGroup"
 xmlns="http://schemas.microsoft.com/developer/vstemplate/2005" xmlns:sdk="http://schemas.microsoft.com/developer/vstemplate-sdkextension/2010">
  
    Plugin for Prism.Forms
    Creates all files necessary to create a plugin for Prism.Forms
    Prism.Forms.Plugin.ico
    CSharp
    2.0
    30
    16bac5e1-199d-4e08-9ed3-2ef287221be1
    PrismFormsPlugin
    true
    true
    1
  
  
"$safeprojectname$.Abstractions">

        Prism.Forms.Plugin.Abstractions\Prism.Forms.Plugin.Abstractions.vstemplate
      
  
       Prism.Forms.Plugin.Wizard, Version=1.0.0.0, Culture=neutral, PublicKeyToken=b2335488e79a16da     Prism.Forms.Plugin.Wizard.NewProjectWizard  
 
  

注意点:

  • 入口模板文件的Type必须为ProjectGroup,否则只创建一个工程项目
  • 模板内容必须使用ProjectTemplateLink添加每个子项目的模板文件设置
  • 由于使用向导可选择方式创建子项目,所有这里只添加了接口的模板文件
  • WizarExtension节点为向导程序设置,调用Prism.Forms.Plugin.Wizard.NewProjectWizard

子项目模板Prism.Forms.Plugin.Abstractions.vstemplate设置:

"3.0.0" xmlns="http://schemas.microsoft.com/developer/vstemplate/2005" 
Type="Project">
  
    Prism.Forms.Plugin.Abstractions
    Implementation Interface for Prism.Forms.Plugin 
    
CSharp

    
    
    1000
    
true
    Prism.Forms.Plugin.Abstractions
    true
    Enabled
    true
    __TemplateIcon.ico
    true
  
  
    "$safeprojectname$.Abstractions.csproj" File="Prism.Forms.Plugin.Abstractions.csproj" ReplaceParameters="true">
      "true" TargetFileName="I$safeprojectgroupname$.cs">IPrismFormsPlugin.cs
      "Properties" TargetFolderName="Properties">
        "true" TargetFileName="AssemblyInfo.cs">AssemblyInfo.cs
      
    
  
Prism.Forms.Plugin.Wizard, Version=1.0.0.0, Culture=neutral, PublicKeyToken=b2335488e79a16da Prism.Forms.Plugin.Wizard.SafeProjectGroupName
 
  

注意点:

  • Type为Project。
  • safeprojectgroupname是Prism.Forms.Plugin.Wizard.SafeProjectGroupName向导提供的参数,不是VS内部参数。值为用户选择模板时输入的工程名称。
  • 模板参照文件(IPrismFormsPlugin.cs,AssemblyInfo.cs等)的生成操作设置为无,否则编译出错。

IPrismFormsPlugin.cs设置:使用safeprojectgroupname

using System;

namespace $safeprojectgroupname$
{
    public interface I$safeprojectgroupname$
{
}
}

Prism.Forms.Plugin.Abstractions.csproj设置:使用safeprojectgroupname

"1.0" encoding="utf-8"?>
"12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  "$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
  
    10.0
    " '$(Configuration)' == '' ">Debug
    " '$(Platform)' == '' ">AnyCPU
    {$guid1$}
    Library
    Properties
    
$safeprojectgroupname$ $safeprojectname$
    en-US
    512
    {786C830F-07A1-408B-BD7F-6EE04809D6DB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}
    Profile7
    v4.5
  
  " '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
    true
    full
    false
    bin\Debug\
    DEBUG;TRACE
    prompt
    4
  
  " '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
    pdbonly
    true
    bin\Release\
    TRACE
    prompt
    4
    
bin\Release\$safeprojectname$.XML
  
  
    
  
  
    "I$safeprojectgroupname$.cs" />
    "Properties\AssemblyInfo.cs" />
  
  "$(MSBuildExtensionsPath32)\Microsoft\Portable\$(TargetFrameworkVersion)\Microsoft.Portable.CSharp.targets" />
  

总体文件结构如下:

Visual Studio项目模板与向导开发_第11张图片

备注:

IPrismFormsPlugin.cs,AssemblyInfo.cs,Prism.Forms.Plugin.Abstractions.csproj等都是通过VS自动生成的,不是手工创建的。具体方法就是事先建立后想要的项目结构与文件,然后使用VS的导出模板功能就可以自动生成了。添加进模板项目的时候一定记得将生成操作设置为无。

Visual Studio项目模板与向导开发_第12张图片

8、插件source.extension.vsixmanifest文件设置

添加安装目标,资产。

Visual Studio项目模板与向导开发_第13张图片

9、生成发布

使用效果如下:

Visual Studio项目模板与向导开发_第14张图片

Visual Studio项目模板与向导开发_第15张图片

这回再也不用每次都添加好几次工程了,一次搞定。懒人就是这样想办法偷懒微笑

你可能感兴趣的:(Visual Studio项目模板与向导开发)