在【Xamarin+Prism开发详解系列】里面经常使用到【Prism unity app】的模板创建Prism.Forms项目:
备注:由于Unity社区已经不怎么活跃,下一个版本将会有Autofac,DryIOC,Ninject的项目模板。
自动弹出选择框:
对于我这类还没有动手写过模板的人来说,确实挺新奇的。于是就决定自己也写个类似的试试,目的就是通过向导创建跨平台Plugin项目,类似Plugin for xamarin,不过是针对Prism,对应平台可以自由选择创建。试了之后才发现也有不少注意的地方,特写下此文做备忘。
项目地址:https://github.com/NewBLife/Prism.Forms.Plugin
插件下载地址:TemplatesForPrism.vsix
1、安装插件开发用的Extensibility模板与工具
2、新建VSIX Project插件项目
source.extension.vsixmanifest 这个文件相当重要,里面可以指定安装目标,模板,向导等。
最后我实现的例子:
安装目标:VS2013以上(2017估计不行)
资产:Project模板,Item模板,Wizard向导
3、从【C# Item Template】与【C# Project Template】模板创建项目。
4、从【类库】模板创建Wizard项目。(Wizard向导只能是类库)
以上步骤之后的项目结构:
介绍:
5、添加引用
6、添加生成向导
6.1、NewProjectWizard项目选择向导创建新建一个WinForm选择框,返回选择的结果。
继承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测试方法:
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
2.0 1 PrismFormsPlugin.nuspec "$fileinputname$.nuspec" ReplaceParameters="true">Prism.Forms.Plugin.nuspec
注意点:
效果如下:(在添加新项里面)
Prism.Forms.Plugin模板设置
每个文件夹下面为相应的模板文件
最外面的入口模板文件Prism.Forms.Plugin.vstemplate设置:
"1.0" encoding="utf-8"?>Type="ProjectGroup""3.0.0"
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
Prism.Forms.Plugin.Abstractions\Prism.Forms.Plugin.Abstractions.vstemplate
注意点:
子项目模板Prism.Forms.Plugin.Abstractions.vstemplate设置:
Type="Project">"3.0.0" xmlns="http://schemas.microsoft.com/developer/vstemplate/2005"
CSharpPrism.Forms.Plugin.Abstractions Implementation Interface for Prism.Forms.Plugin
1000
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
注意点:
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
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
"I$safeprojectgroupname$.cs" /> "Properties\AssemblyInfo.cs" /> "$(MSBuildExtensionsPath32)\Microsoft\Portable\$(TargetFrameworkVersion)\Microsoft.Portable.CSharp.targets" />
总体文件结构如下:
备注:
IPrismFormsPlugin.cs,AssemblyInfo.cs,Prism.Forms.Plugin.Abstractions.csproj等都是通过VS自动生成的,不是手工创建的。具体方法就是事先建立后想要的项目结构与文件,然后使用VS的导出模板功能就可以自动生成了。添加进模板项目的时候一定记得将生成操作设置为无。
8、插件source.extension.vsixmanifest文件设置
添加安装目标,资产。
9、生成发布
使用效果如下:
这回再也不用每次都添加好几次工程了,一次搞定。懒人就是这样想办法偷懒。